XPath в сравнении с селекторами CSS

Назначение выражений XPath и селекторов CSS

Да, мы только что сказали, что предполагаем, что вы уже имеете представление о том, что они делают, но если мы ошиблись в предположениях, то позвольте нам просто побаловать наших читателей кратким обзором.

Назначение выражений XPath и селекторов CSS заключается в предоставлении выразительного языка, позволяющего находить и выбирать элементы в XML- и HTML-документах. Предположим, что у нас есть следующая веб-страница:

<!doctype html>
<html>
    <head>
        <title>What is the DOM ?</title>
        <meta charset="utf-8" />
    </head>

    <body>
        <h1>DOM 101</h1>
        <p>Webscraping is awsome !</p>
        <p>Here is my <a href="https://www.scrapingbee.com/blog/">blog</a></p>
    </body>
</html>

А теперь мы хотим извлечь URL нашего анкорного тега, указывающего на ScrapingBee. Как мы можем это сделать?

Конечно, можно вручную разобрать всю HTML-строку (например, токенизировать все и проверить на наличие <a) или - если уже более изощренно - использовать регулярные выражения (например, <a\s+href="([^"]+)), но все это - ручной разбор строки, а это редко бывает интересно. 

Именно в этом случае на помощь приходят селекторы XPath и CSS. В нашем примере мы можем получить доступ к тегу якоря с помощью следующего синтаксиса в XPath и CSS соответственно.​

История селекторов XPath и CSS

Обе технологии появились довольно давно. Они появились примерно в середине-конце 90-х годов прошлого века, и хотя во многих отношениях они очень и очень похожи - их можно считать двоюродными братьями или даже сестрами, - тем не менее, они имеют разное происхождение и разные первоначальные цели.

XPath: Хотя границы между HTML и XML, по общему признанию, всегда были несколько подвижны, HTML всегда был ориентирован на работу в Интернете, в то время как XML всегда был в первую очередь форматом структурированных документов для данных. Именно здесь и появился XPath. XPath был представлен одновременно с XML и имел целью предоставить язык запросов для XML. Он должен позволять быстро и легко получать доступ к информации внутри данного XML-документа, независимо от того, где он находится - в Интернете или в известной базе данных XML.

CSS: Селекторы CSS не были самостоятельным изобретением, а являются частью общей экосистемы CSS. Однако они являются ее неотъемлемой частью, поскольку обеспечивают основную функциональность - поиск того самого элемента (элементов), к которому будет применен блок объявления правила. В этом контексте селекторы CSS всегда были очень сильно (и почти исключительно) ориентированы на веб и веб-страницы.

В то время как XPath оставался (в основном) верен своему первоначальному назначению - созданию языка запросов к структурам XML-документов (к которым относится и HTML), CSS-селекторы со временем вышли за рамки использования только для стилизации и превратились в самостоятельную технологию запросов к веб-документам.

Как мы уже говорили, обе технологии практически идентичны по своим возможностям и имеют общие черты синтаксиса, однако между ними все же существует достаточно много различий, а также преимуществ и ограничений. Именно это мы и рассмотрим далее.

Преимущества и ограничения XPath

Вообще говоря, XPath является более сложным языком. Если вы можете реализовать запрос с помощью селекторов CSS, вы можете сделать то же самое с помощью XPath. Обратное не всегда возможно.

Например, одним из основных преимуществ XPath является его способность поддерживать двунаправленное обходное движение по дереву. Вы можете найти определенный элемент, а затем, используя ось parent, выбрать его родителя или даже предков. Несмотря на то, что селекторы CSS предлагают полуэквивалент в виде псевдокласса :has, это все равно далеко не XPath, поскольку он охватывает лишь ограниченный набор случаев использования, а его поддержка браузерами все еще весьма сомнительна.

Несмотря на то, что производительность в данном контексте обычно не играет большой роли, иногда производительность XPath может быть выше, чем у селекторов CSS. Это связано с тем, что некоторые CSS-селекторы используют XPath для внутренних целей и сначала преобразуют свои селекторы в XPath-выражения.

Одним из недостатков XPath, безусловно, является его более многословный синтаксис. Такие вещи, как классы HTML, должны быть указаны в виде атрибутов, в то время как CSS поддерживает их как, скажем так, первоклассных граждан в своем синтаксисе.

Плюсы и минусы
+ Поддержка двунаправленного перемещения по дереву (обращение к родительскому или предковому элементу)
+ Поддержка выбора не только элементов документа (т.е. атрибутов и содержимого)
+ Встроенные функции для различных вариантов использования (например, text(), count())
+ Поддержка абсолютных и относительных путей поиска

- Более подробный синтаксис

Преимущества и ограничения CSS-селекторов

Одним из основных преимуществ CSS-селекторов является их "естественность" в контексте Web. Если вы знакомы с веб-разработкой, то вы сразу же поймете, что такое CSS-селекторы, поскольку уже давно используете их при оформлении страниц.

В отличие от выражений XPath, селекторы CSS прекрасно понимают, что такое идентификаторы и классы HTML, и предоставляют элементы с собственным синтаксисом (например, #id & .class). По этой причине CSS-селектор можно считать чуть менее многословным (a#id против //a[@id="id"]).

К ограничениям можно отнести тот факт, что CSS-селекторы работают только на уровне элементов. То есть, если необходимо извлечь ссылку из тега <a>, то с помощью CSS-селекторов можно найти только все соответствующие теги, а к атрибуту href придется обращаться на втором этапе, вне контекста CSS-селектора (например, tag.getAttribute("href")). XPath выражения позволяют и здесь получить прямой доступ к атрибутам (например, //a/@href).

Еще одним существенным ограничением является нисходящий подход, который означает, что дерево DOM можно обходить только в одном направлении и нельзя выбирать родительские элементы (например, `... для файловых систем). Хотя это, возможно, не самый распространенный вариант использования, все же бывают случаи, когда это может пригодиться, и при использовании селекторов CSS приходится искать какой-то обходной путь.

Плюсы и минусы
+ Веб-разработчики знакомы с синтаксисом
+ Идентификаторы и классы HTML являются гражданами первого класса (синтаксис хэша и точки)

- Можно выбирать только элементы (без атрибутов или содержимого)
- Не поддерживает восходящий обход (доступ к родительскому элементу или элементу-предку)
- Сложнее указать абсолютный путь поиска

Извлечение данных с помощью CSS-селекторов и ScrapingBee

На всякий случай, если вы еще не знакомы с сервисом извлечения данных ScrapingBee: это SaaS-платформа для извлечения данных с легким REST API, собственными SDK-пакетами для некоторых языков и поддержкой интеграции с безголовыми браузерами.
 
Несмотря на то, что XPath, несомненно, более совершенен для некоторых случаев использования, мир веб-парсинга в значительной степени определяется селекторами CSS, и пользователи, особенно веб-девелоперы, часто чувствуют себя более комфортно с ними, поэтому ScrapingBee решила поддерживать селекторы CSS в качестве основного языка выражения в API.
 
Запуск задания по извлечению данных довольно прост, и для этого достаточно предоставить JSON-объект с полями, которые вы хотите извлечь из страницы, в качестве параметра extract_rules. Например, следующий объект определяет два элемента извлечения ("title" и "subtitle") вместе с соответствующими CSS-селекторами (h1 для заголовка и #subtitle для подзаголовка).
{
"title": "h1",
"subtitle": "#subtitle"
}
В ответ вы получите аналогичный JSON-объект, содержащий поля с их значениями со страницы.

Использование селекторов XPath и CSS в браузере

Хотя существует множество инструментов (например, https://try.jsoup.org), которые помогут вам в управлении выражениями XPath и CSS-селекторами, на самом деле один такой инструмент уже установлен на вашей машине, той, которой вы пользуетесь в данный момент: это ваш браузер.

Инструменты разработчика в браузерах Chrome и Firefox не только облегчают оценку и отладку выражений XPath и селекторов, но и помогают найти нужные выражения. Просто щелкните правой кнопкой мыши на нужном элементе и выберите пункт Inspect. Теперь у вас должно быть открыто дерево DOM страницы, и вы можете получить доступ к отдельным элементам.

Поиск правильного селектора XPath и CSS с помощью браузера

В нашем примере на сайте example.com мы щелкнули правой кнопкой мыши на ссылке "Подробнее" и теперь имеем контекстное меню со всеми необходимыми опциями. Давайте выберем пункт "Копировать". Здесь выделенные записи будут представлять для нас особый интерес.

Если мы выберем пункт Копировать селектор, то браузер сохранит в буфере обмена путь CSS-селектора элемента, например, следующее выражение:

body > div > p:nth-child(3) > a

Если же мы выберем Copy XPath или Copy full XPath, то браузер сделает то же самое со следующим выражением XPath:

/html/body/div/p[2]/a

В любом случае в буфере обмена должно появиться выражение для данного элемента. Правда, следует иметь в виду, что даже Google и Mozilla, вероятно, не могут творить чудеса (пока), и эти выражения могут быть применимы только для данной страницы, а может быть, и для ее загрузки. Зачастую может потребоваться тонкая настройка выражения.

Тонкая настройка выражений

В двух предыдущих примерах мы использовали абсолютные пути, и они прекрасно работали в нашем контексте, однако всегда есть возможность для оптимизации. Например, учитывая, что у example.com есть только одна единственная ссылка, мы могли бы легко сократить наши выражения до следующих:

# XPath
//a

# CSS
a

С одной стороны, к сожалению, это не только не очень разборчиво, но и может не работать при перезагрузке из-за изменения классов HTML.

С другой стороны, к счастью, в данном случае у нас есть очень специфический HTML-элемент, который значительно упростит все дело -> <time>.

Мы просто используем time (или //time для XPath) и получаем "только" тот элемент, который нам нужен. Кавычки, потому что Twitter показывает связанные твиты, и в каждом из них будет элемент <time>, так что убедитесь, что используете только первый.

Однако не только конкретные типы элементов могут помочь нам сузить выражение, мы также должны обращать внимание на HTML-идентификаторы, классы, атрибуты, а иногда даже на содержимое.

IDs должен быть уникальным, поэтому быстрый #myid или (//*[@id="myid"]) позволит получить элемент быстрее и стабильнее, чем абсолютный путь.
Хотя классы не обязательно уникальны, они все равно могут оказать волшебное действие, особенно если, например, поместить их в контекст иерархии документа: h1 > b.price

И последнее, но не менее важное: инструменты devtools также позволяют оценивать выражения XPath и селекторы CSS.

Просто нажмите F12 еще раз, выберите вкладку Elements и нажмите Ctrl/⌘ + F, чтобы открыть окно поиска.

Окно поиска

Здесь можно пробовать различные XPath-выражения и CSS-селекторы и перебирать найденные элементы (если они, конечно, есть).

Довольно удобно для быстрого опробования выражений.

Итог

Мы надеемся, что статья вам понравилась, и вы получили первое представление о том, что представляют собой эти два языка и в чем их сильные и слабые стороны.

Поскольку в этой статье мы старались сосредоточиться на основных различиях между селекторами XPath и CSS, мы не стали углубляться в технические детали синтаксиса элементов каждого языка.