XPath в scrapy

Теги: crawlers 

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 и селекторами можно найти смотри тут:

Еще ссылки: