Как извлечь сырой 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) и использовать Pythonre
модуль на извлеченной строке
Длинный ответ:
Давайте рассмотрим пример ввода и различные способы извлечения данных Javascript из HTML.
Образец HTML:
<html lang="en">
<body>
<div>
<script type="text/javascript">
var i = {a:['O'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'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'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'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-сущность '
был заменен апострофом. Это связано с 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
чтобы избежать поведения по умолчанию.