Получение переменных внутри функции Javascript с использованием BeautifulSoup, Python, Regex - PullRequest
2 голосов
/ 13 октября 2019

В функции Javascript определен массив images, который необходимо извлечь и преобразовать из строки в объект списка Python.

Python Beautifulsoup используется для анализа.

        var images = [
            {   
                src: "http://example.com/bar/001.jpg",  
                title: "FooBar One" 
            },  
            {   
                src: "http://example.com/bar/002.jpg",  
                title: "FooBar Two" 
            },  
        ]
        ;

Вопрос: Почему мой код ниже не может захватить этот массив images, икак мы можем это исправить?

Спасибо!

Требуемый вывод Объект списка Python.

[
    {   
        src: "http://example.com/bar/001.jpg",  
        title: "FooBar One" 
    },  
    {   
        src: "http://example.com/bar/002.jpg",  
        title: "FooBar Two" 
    },  
]

Фактический код

import re
from bs4 import BeautifulSoup

# Example of a HTML source code containing `images` array
html = '''
<html>
<head>
<script type="text/javascript">

    $(document).ready(function(){
        var images = [
            {   
                src: "http://example.com/bar/001.jpg",  
                title: "FooBar One" 
            },  
            {   
                src: "http://example.com/bar/002.jpg",  
                title: "FooBar Two" 
            },  
        ]
        ;
        var other_data = [{"name": "Tom", "type": "cat"}, {"name": "Jerry", "type": "dog"}];

</script>
<body>
<p>Some content</p>
</body>
</head>
</html>
'''

pattern = re.compile('var images = (.*?);')
soup = BeautifulSoup(html, 'lxml')
scripts = soup.find_all('script')  # successfully captures the <script> element
for script in scripts:
    data = pattern.match(str(script.string))  # NOT extracting the array!!
    if data:
        print('Found:', data.groups()[0])     # NOT being printed

Ответы [ 4 ]

1 голос
/ 13 октября 2019

Вы можете использовать более короткое ленивое регулярное выражение и библиотеку hjson для работы с ключами без кавычек

import re, hjson

html = '''
<html>
<head>
<script type="text/javascript">

    $(document).ready(function(){
        var images = [
            {   
                src: "http://example.com/bar/001.jpg",  
                title: "FooBar One" 
            },  
            {   
                src: "http://example.com/bar/002.jpg",  
                title: "FooBar Two" 
            },  
        ]
        ;
        var other_data = [{"name": "Tom", "type": "cat"}, {"name": "Jerry", "type": "dog"}];

</script>
'''
p = re.compile(r'var images = (.*?);', re.DOTALL)
data = hjson.loads(p.findall(html)[0])
print(data)
1 голос
/ 13 октября 2019

Метод 1

Возможно,

 \bvar\s+images\s*=\s*(\[[^\]]*\])

может работать до некоторой степени:

Тест

import re
from bs4 import BeautifulSoup

# Example of a HTML source code containing `images` array
html = '''
<html>
<head>
<script type="text/javascript">

    $(document).ready(function(){
        var images = [
            {   
                src: "http://example.com/bar/001.jpg",  
                title: "FooBar One" 
            },  
            {   
                src: "http://example.com/bar/002.jpg",  
                title: "FooBar Two" 
            },  
        ]
        ;
        var other_data = [{"name": "Tom", "type": "cat"}, {"name": "Jerry", "type": "dog"}];

</script>
<body>
<p>Some content</p>
</body>
</head>
</html>
'''

soup = BeautifulSoup(html, 'html.parser')
scripts = soup.find_all('script')  # successfully captures the <script> element

for script in scripts:
    data = re.findall(
        r'\bvar\s+images\s*=\s*(\[[^\]]*\])', script.string, re.DOTALL)
    print(data[0])

Вывод

[{
src: "http://example.com/bar/001.jpg",
title:" FooBar One "},
{
src:" http://example.com/bar/002.jpg",
title: "FooBar Two"},
]


Если вы хотите упростить / изменить / изучить выражение, это было объяснено на верхней правой панели regex101.com . При желании вы также можете посмотреть в эту ссылку , как она будет сопоставляться с некоторыми примерами ввода.


Метод 2

Другой вариант будет:

import re

string = '''
<html>
<head>
<script type="text/javascript">

    $(document).ready(function(){
        var images = [
            {   
                src: "http://example.com/bar/001.jpg",  
                title: "FooBar One" 
            },  
            {   
                src: "http://example.com/bar/002.jpg",  
                title: "FooBar Two" 
            },  
        ]
        ;
        var other_data = [{"name": "Tom", "type": "cat"}, {"name": "Jerry", "type": "dog"}];

</script>
<body>
<p>Some content</p>
</body>
</head>
</html>
'''

expression = r'src:\s*"([^"]*)"\s*,\s*title:\s*"([^"]*)"'

matches = re.findall(expression, string, re.DOTALL)

output = []
for match in matches:
    output.append(dict({"src": match[0], "title": match[1]}))

print(output)

Выход

[{'src': 'http://example.com/bar/001.jpg', 'title': 'FooBar One'}, {'src': 'http://example.com/bar/002.jpg', 'title': 'FooBar Two'}]

Демо

1 голос
/ 13 октября 2019

Вот способ попасть туда, без регулярных выражений, даже не Beautifulsoup - просто обычные манипуляции со строками Python - в 4 простых шага:)

step_1 = html.split('var images = [')
step_2 = " ".join(step_1[1].split())
step_3 = step_2.split('] ; var other_data = ')
step_4= step_3[0].replace('}, {','}xxx{').split('xxx')
print(step_4)

Вывод:

['{ src: "http://example.com/bar/001.jpg", title: "FooBar One" }',
 '{ src: "http://example.com/bar/002.jpg", title: "FooBar Two" }, ']
1 голос
/ 13 октября 2019

re.match соответствует началу строки. Ваше регулярное выражение должно передать всю строку. Используйте

pattern = re.compile('.*var images = (.*?);.*', re.DOTALL)

Строка все еще не в допустимом формате списка Python. Вам необходимо выполнить некоторые манипуляции, прежде чем вы сможете применить ast.literal_eval

for script in scripts:
    data = pattern.match(str(script.string))
    if data:
        list_str = data.groups()[0]
        # Remove last comma
        last_comma_index = list_str.rfind(',')
        list_str = list_str[:last_comma_index] + list_str[last_comma_index+1:]
        # Modify src to 'src' and title to 'title'
        list_str = re.sub(r'\s([a-z]+):', r'"\1":', list_str)
        # Strip
        list_str = list_str.strip()
        final_list = ast.literal_eval(list_str.strip())
        print(final_list)

Выход

[{'src': 'http://example.com/bar/001.jpg', 'title': 'FooBar One'}, {'src': 'http://example.com/bar/002.jpg', 'title': 'FooBar Two'}]
...