Как извлечь сырой HTML из селектора Scrapy?

Я извлекаю данные js, используя response.xpath('//*')re_first(), а затем преобразовываю их в собственные данные Python. Проблема в том, что методы extract/re, похоже, не обеспечивают способ не заключать в кавычки html, т.е.

оригинальный HTML:

{my_fields:['O'Connor Park'], }

извлечь вывод:

{my_fields:['O'Connor Park'], }

превращение этого вывода в JSON не будет работать.

Какой самый простой способ обойти это?

2 ответа

Решение

Короткий ответ:

  • Scrapy / Parsel селекторы .re() а также .re_first() методы заменяют сущности HTML (кроме <, &)
  • вместо этого используйте .extract() или же .extract_first() чтобы получить сырой HTML (или необработанные инструкции JavaScript) и использовать Python re модуль на извлеченной строке

Длинный ответ:

Давайте рассмотрим пример ввода и различные способы извлечения данных Javascript из HTML.

Образец HTML:

<html lang="en">
<body>
<div>
    <script type="text/javascript">
        var i = {a:['O&#39;Connor Park']}
    </script>
</div>
</body>
</html>

Используя scrapy Selector, который использует библиотеку parsel, у вас есть несколько способов извлечь фрагмент Javascript:

>>> import scrapy
>>> t = """<html lang="en">
... <body>
... <div>
...     <script type="text/javascript">
...         var i = {a:['O&#39;Connor Park']}
...     </script>
...     
... </div>
... </body>
... </html>
... """
>>> selector = scrapy.Selector(text=t, type="html")
>>> 
>>> # extracting the <script> element as raw HTML
>>> selector.xpath('//div/script').extract_first()
u'<script type="text/javascript">\n        var i = {a:[\'O&#39;Connor Park\']}\n    </script>'
>>> 
>>> # only getting the text node inside the <script> element
>>> selector.xpath('//div/script/text()').extract_first()
u"\n        var i = {a:['O&#39;Connor Park']}\n    "
>>> 

Теперь, используя .re (или же .re_first) вы получите другой результат:

>>> # I'm using a very simple "catch-all" regex
>>> # you are probably using a regex to extract
>>> # that specific "O'Connor Park" string
>>> selector.xpath('//div/script/text()').re_first('.+')
u"        var i = {a:['O'Connor Park']}"
>>> 
>>> # .re() on the element itself, one needs to handle newlines
>>> selector.xpath('//div/script').re_first('.+')
u'<script type="text/javascript">'    # only first line extracted
>>> import re
>>> selector.xpath('//div/script').re_first(re.compile('.+', re.DOTALL))
u'<script type="text/javascript">\n        var i = {a:[\'O\'Connor Park\']}\n    </script>'
>>> 

HTML-сущность &#39; был заменен апострофом. Это связано с w3lib.html.replace_entities() вызывать .re/re_first реализация (см. parsel исходный код, в extract_regex функция), которая не используется при простом вызове extract() или же extract_first()

Вы также можете использовать ту же функцию, которая используется Selector учебный класс' extract метод, но с разными аргументами:

from lxml import etree
etree.tostring(selector._root)

Начиная с parsel 1.2.0 (2017-05-17) вы можете передать replace_entities=False как для re а также re_first чтобы избежать поведения по умолчанию.

Другие вопросы по тегам