XPath в scrapy
XPath — это язык выражений, который позволяет обрабатывать значения, соответствующие модели данных, определенной в XQuery и модели данных Xpath
В [scrapy] используются два подхода - [css-selectors] и собственно xpath. Собственно обертка для обоих реализована через библиотеку parsel.
Данная статья будет связана с работай в xpath в [scrapy] на базе parsel.
[scrapy] оперирует селекторами, потому что они «выбирают» определенные части HTML-документа, заданные выражениями XPath или CSS. XPath — это язык для выбора узлов в XML-документах, который также можно использовать с HTML.
Типичный подход выглядит так:
>>> response.xpath('//span/text()').get()
'good'
>>> response.css('span::text').get()
'good'
Селекторы scrapy реализованы через инстанцы класса scrapy.selector.Selector (или, когда нужно обработать список селекторов scrapy.selector.SelectorList
). Практического смысла конструировать селекторы в ручную нет, так как объект response
доступен через колбеки и имеет соответствующие методы .css()
и .xpath()
. Но, в любом случае можно и вручную
>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').get()
'good'
Классы предоставляют следующие методы:
xpath(xpath, namespaces=None, **kwargs)
находит узлы, соответствующие запросу xpath, и возвращает результат в виде экземпляра SelectorList
со всеми сведенными элементами. Элементы возвращенного списка также реализуют интерфейс Selector
.
query
- это строка с синтаксисом xpath запросаnamespaces
позволяет использовать собственные пространства имен (параметр не регистрируется для будущих вызовов - надо использовать особый метод для регистрации)**kwargs
- дополнительные аргумсенты, котоыре могут быть переданы в выражение xpath во так
>>> selector.xpath('//a[href=$url]', url="http://www.example.com")
css(query)
применяет селектор CSS и возвращает экземпляр SelectorList
get()
возвращает единственную строку, соответствующую запросу или None
>>> response.css('title::text').get()
'Example website'
attrib
возвращает словарь атрибутов для элемента
>>> response.css('img').attrib['src']
'image1_thumb.jpg'
re(regex, replace_entities=True)
применить регулярное выражение и вернуть список стрко с совпадениями. Или первое встреченное вот так: re_first(regex, default=None, replace_entities=True)
register_namespace(prefix, uri)
позволяет зарегистрировать нестандартное пространство имен (это бывает важно, если реализуются узлы, не предусмотренные стандартной спецификацией). remove_namespaces()
удаляет пространство имен.
__bool__()
вернет True
если в селекторе есть хоть что-то. Иначе False
getall()
возвращает список соответствий запросу или пустой список
>>> response.css('img').xpath('@src').getall()
['image1_thumb.jpg',
'image2_thumb.jpg',
'image3_thumb.jpg',
'image4_thumb.jpg',
'image5_thumb.jpg']
Кроме того поддерживаются методы extract()
и extract_first()
, однако они устарели и предпочтительно использовать get()
и getall()
Пример использования xpath и селекторов в scrapy смотри тут
>>> sel = Selector(html_response)
>>> sel.xpath("//h1").getall() # this includes the h1 tag
>>> sel.xpath("//h1/text()").getall() # this excludes the h1 tag
>>> for node in sel.xpath("//p"):
... print(node.attrib['class'])
Можно сотавлять вложенные селекторы, так как xpath()
и css()
возвращают списки селекторов.
Выбор атрибутов осуществляется через нотацию @
>>> response.xpath("//a/@href").getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
Выбор css-селекторов через ::
>>> response.css('a::attr(href)').getall()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
Кроме того можно использовать свойство attrib
самого селектора
>>> [a.attrib['href'] for a in response.css('a')]
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']
Использование регулярок так-же не является сложной задачей:
>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
['My image 1',
'My image 2',
'My image 3',
'My image 4',
'My image 5']
Особенности работы с xpath
Все селекторы на оснвое xpath отсчитываются от корня документа. Это надо иметь ввиду, когда извлекаются объекты с помощью вложенных селекторов
плохо
divs = response.xpath('//div')
for p in divs.xpath('//p'): # this is wrong - gets all <p> from the whole document
print(p.get())
лучше
for p in divs.xpath('.//p'): # extracts all <p> inside
print(p.get())
или
for p in divs.xpath('p'):
print(p.get())
Выбирать элемент по классу можно разными способами, т.к. в может быть много разных классов
Можно использовать цепь селекторов
from scrapy import Selector
sel = Selector(
text='<div class="hero shout"><time datetime="2014-07-23 19:00">Special date</time></div>'
)
sel.css('.shout').xpath('./time/@datetime').getall()
['2014-07-23 19:00']
Beware of the difference between //node[1] and (//node)[1]
Using text nodes in a condition
Variables in XPath expressions
Removing namespaces
Selector.remove_namespaces()
позволяет избавиться от пространства имен, чтобы оперировать более простыми понятиями
Using EXSLT extensions
Больше подробностей прои работу с xpath и селекторами можно найти смотри тут:
- подробное руководство по использованию селекторов и xpath в parsel
- XPath/CSS Equivalents in Wikibooks
- Xpath cheatsheet
- XPath Tutorial w3s
Еще ссылки:
- [crawlers]
- [scrapy]
- [css-selectors]
- [2022-02-12-daily-note] парсинг sitemap, фильтрацияи и способы обхода в scrapy
- parsel is a BSD-licensed Python library to extract and remove data from HTML and XML using XPath and CSS selectors, optionally combined with regular expression
- [xpath-css-examples]