Не так страшен XPATH как его незнание. XPath примеры - шпаргалка для разбора страниц

XPath является одним из ключевых моментов на дороге к пониманию XSLT. И на первых порах хочется получить какой-нибудь простой способ поэкспериментировать с ним, чтобы в деталях разобраться, как он работает. Способа такого, впрочем, не наблюдается. Приходится или качать совсем не бесплатные XML/XSLT редакторы, или довольствоваться статичными картинками с zvon.org . Может быть, я плохо искал. Но с моей колокольни все обстоит именно так. И когда передо мной в очередной раз встала задача «Объяснить XSLT», в голове и родилась идея крохотного сервиса. После того, как все заработало, было решено выкатить его для всеобщего пользования: наверняка не я один сталкивался с похожими проблемами.

Как это работает

Все просто до безобразия: вводим XML для экспериментов, пишем запрос и по ходу набора наблюдаем результат. Выглядит это примерно вот так:

Впрочем, чего разглядывать картинки, можно пойти и пощупать это все живьем .

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

Примеры использования xpath из практики парсинга информации с сайтов. Приведены участки кода xpath.

Получить текст заголовока h1

//h1/text()

Получить текст заголовока с классом produnctName

//h1[@class="produnctName"]/text()

Получить значение определенного span по классу

//span[@class="price"]

Получить значение атрибута title у кнопки с классом addtocart_button

//input[@class="addtocart_button"]/@title

//a/text()

//a/@href

Изображение src

//img/@src

Изображение сразу за определенным элементом в DOM, ось following

//h1[@class="produnctName"]//following::div/img/@src

Изображение в 4 div по счету

//div/img/@src

XPath (XML Path Language) — язык запросов к элементам XML-документа. Разработан для организации доступа к частям документа XML в файлах трансформации XSLT и является стандартом консорциума W3C. XPath призван реализовать навигацию по DOM в XML.

XML имеет древовидную структуру. У элемента дерева всегда существуют потомки и предки, кроме корневого элемента, у которого предков нет, а также тупиковых элементов (листьев дерева), у которых нет потомков.

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

Функции над множествами узлов

  • * — обозначает любое имя или набор символов по указанной оси, например: * — любой дочерний узел; @* — любой атрибут.
  • $name — обращение к переменной, где name — имя переменной или параметра.
  • дополнительные условия выборки или, что то же самое, предикат шага адресации. Должен содержать логическое значение. Если содержит числовое, считается что это порядковый номер узла, что эквивалентно приписыванию перед этим числом выражения «position()=»
  • {} — если применяется внутри тега другого языка (например HTML), то XSLT процессор рассматривает содержимое фигурных скобок как XPath.
  • / — определяет уровень дерева, то есть разделяет шаги адресации
  • | — объединяет результат. То есть, можно написать несколько путей разбора через знак | и в результат такого выражения войдёт всё, что будет найдено любым из этих путей.
  • node-set node ()

Возвращает все узлы. Вместо этой функции часто используют заменитель "*", но, в отличие от звездочки, функция node() возвращает и текстовые узлы.

  • string text ()

Возвращает набор текстовых узлов;

  • node-set current ()

Возвращает множество из одного элемента, который является текущим. Если мы делаем обработку множества с условиями, то единственным способом дотянуться из этого условия до текущего элемента будет данная функция.

  • number position ()

Возвращает позицию элемента в множестве. Корректно работает только в цикле

  • number last ()

Возвращает номер последнего элемента в множестве. Корректно работает только в цикле

  • number count (node-set)

Возвращает количество элементов в node-set.

  • string name (node-set?)

Возвращает полное имя первого тега в множестве.

  • string namespace-uri (node-set?)
  • string local-name (node-set?)

Возвращает имя первого тега в множестве, без пространства имён.

  • node-set id (object)

Находит элемент с уникальным идентификатором

Оси — это база языка XPath. Для некоторых осей существуют сокращённые обозначения.

  • ancestor:: — Возвращает множество предков.
  • ancestor-or-self:: — Возвращает множество предков и текущий элемент.
  • attribute:: — Возвращает множество атрибутов текущего элемента. Это обращение можно заменить на «@»
  • child:: — Возвращает множество потомков на один уровень ниже. Это название сокращается полностью, то есть его можно вовсе опускать.
  • descendant:: — Возвращает полное множество потомков (то есть, как ближайших потомков, так и всех их потомков).
  • descendant-or-self:: — Возвращает полное множество потомков и текущий элемент. Выражение «/descendant-or-self::node()/» можно сокращать до «//» . С помощью этой оси, например, можно вторым шагом организовать отбор элементов с любого узла, а не только с корневого: достаточно первым шагом взять всех потомков корневого. Например, путь «//span» отберёт все узлы span документа, независимо от их положения в иерархии, взглянув как на имя корневого, так и на имена всех его дочерних элементов, на всю глубину их вложенности.
  • following:: — Возвращает необработанное множество, ниже текущего элемента.
  • following-sibling:: — Возвращает множество элементов на том же уровне, следующих за текущим.
  • namespace:: — Возвращает множество, имеющее пространство имён (то есть присутствует атрибут xmlns).
  • parent:: — Возвращает предка на один уровень назад. Это обращение можно заменить на «..»
  • preceding:: — Возвращает множество обработанных элементов исключая множество предков.
  • preceding-sibling:: — Возвращает множество элементов на том же уровне, предшествующих текущему.
  • self:: — Возвращает текущий элемент. Это обращение можно заменить на «.»
  • Tutorial

Продолжение перевода неофициальной документации Selenium для Python.
Перевод сделан с разрешения автора Baiju Muthukadan.
Оригинал можно найти .

1. Установка
2. Первые Шаги
3. Навигация
4. Поиск Элементов
5. Ожидания
6. Объекты Страницы
7. WebDriver API
8. Приложение: Часто Задаваемые Вопросы

4. Поиск элементов

Существует ряд способов поиска элементов на странице. Вы вправе использовать наиболее уместные для конкретных задач. Selenium предоставляет следующие методы поиска элементов на странице:
  • find_element_by_id
  • find_element_by_name
  • find_element_by_xpath
  • find_element_by_link_text
  • find_element_by_partial_link_text
  • find_element_by_tag_name
  • find_element_by_class_name
  • find_element_by_css_selector
Чтобы найти все элементы, удовлетворяющие условию поиска, используйте следующие методы (возвращается список):
  • find_elements_by_name
  • find_elements_by_xpath
  • find_elements_by_link_text
  • find_elements_by_partial_link_text
  • find_elements_by_tag_name
  • find_elements_by_class_name
  • find_elements_by_css_selector
[Как вы могли заметить, во втором списке отсутствует поиск по id. Это обуславливается особенностью свойства id для элементов HTML: идентификаторы элементов страницы всегда уникальны. - Прим. пер.]

Помимо общедоступных (public) методов, перечисленных выше, существует два приватных (private) метода, которые при знании указателей объектов страницы могут быть очень полезны: find_element and find_elements.

Пример использования:

From selenium.webdriver.common.by import By driver.find_element(By.XPATH, "//button") driver.find_elements(By.XPATH, "//button")
Для класса By доступны следующие атрибуты:

ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector"

4.1. Поиск по Id

Используйте этот способ, когда известен id элемента. Если ни один элемент не удовлетворяет заданному значению id, будет вызвано исключение NoSuchElementException.


Элемент form может быть определен следующим образом:

Login_form = driver.find_element_by_id("loginForm")

4.2. Поиск по Name

Используйте этот способ, когда известен атрибут name элемента. Результатом будет первый элемент с искомым значением атрибута name. Если ни один элемент не удовлетворяет заданному значению name, будет вызвано исключение NoSuchElementException.

Для примера, рассмотрим следующий исходный код страницы:


Элементы с именами username и password могут быть определены следующим образом:

Username = driver.find_element_by_name("username") password = driver.find_element_by_name("password")
Следующий код получит кнопку “Login”, находящуюся перед кнопкой “Clear”:

Continue = driver.find_element_by_name("continue")

4.3. Поиск по XPath

XPath – это язык, использующийся для поиска узлов дерева XML-документа. Поскольку в основе HTML может лежать структура XML (XHTML), пользователям Selenium предоставляется возможность посредоством этого мощного языка отыскивать элементы в их веб-приложениях. XPath выходит за рамки простых методов поиска по атрибутам id или name (и в то же время поддерживает их), и открывает спектр новых возможностей, таких как поиск третьего чекбокса (checkbox) на странице, к примеру.

Одно из веских оснований использовать XPath заключено в наличии ситуаций, когда вы не можете похвастать пригодными в качестве указателей атрибутами, такими как id или name, для элемента, который вы хотите получить. Вы можете использовать XPath для поиска элемента как по абсолютному пути (не рекомендуется), так и по относительному (для элементов с заданными id или name). XPath указатели в том числе могут быть использованы для определения элементов с помощью атрибутов отличных от id и name.

Абсолютный путь XPath содержит в себе все узлы дерева от корня (html) до необходимого элемента, и, как следствие, подвержен ошибкам в результате малейших корректировок исходного кода страницы. Если найти ближайщий элемент с атрибутами id или name (в идеале один из элементов-родителей), можно определить искомый элемент, используя связь «родитель-подчиненный». Эти связи будут куда стабильнее и сделают ваши тесты устойчивыми к изменениям в исходном коде страницы.

Для примера, рассмотрим следующий исходный код страницы:


Элемент form может быть определен следующими способами:

Login_form = driver.find_element_by_xpath("/html/body/form") login_form = driver.find_element_by_xpath("//form") login_form = driver.find_element_by_xpath("//form[@id="loginForm"]")

  1. Абсолютный путь (поломается при малейшем изменении структуры HTML страницы)
  2. Первый элемент form в странице HTML
  3. Элемент form, для которого определен атрибут с именем id и значением loginForm
Элемент username может быть найден так:

Username = driver.find_element_by_xpath("//form") username = driver.find_element_by_xpath("//form[@id="loginForm"]/input") username = driver.find_element_by_xpath("//input[@name="username"]")

  1. Первый элемент form с дочерним элементом input, для которого определен атрибут с именем name и значением username
  2. Первый дочерний элемент input элемента form, для которого определен атрибут с именем id и значением loginForm
  3. Первый элемент input, для которого определен атрибут с именем name и значением username
Кнопка “Clear” может быть найдена следующими способами:

Clear_button = driver.find_element_by_xpath("//input[@name="continue"][@type="button"]") clear_button = driver.find_element_by_xpath("//form[@id="loginForm"]/input")

  1. Элемент input, для которого заданы атрибут с именем name и значением continue и атрибут с именем type и значением button
  2. Четвертый дочерний элемент input элемента form, для которого задан атрибут с именем id и значением loginForm
Представленные примеры покрывают некоторые основы использования XPath, для более углубленного изучения рекомендую следующие материалы:
  • XPath Tutorial - с интерактивными примерами
Существует также пара очень полезных дополнений (add-on), которые могут помочь в выяснении XPath элемента:
  • XPath Checker - получает пути XPath и может использоваться для проверки результатов пути XPath
  • Firebug - получение пути XPath - лишь одно из многих мощных средств, поддерживаемых этим очень полезным плагином
  • XPath Helper - для Google Chrome

4.4. Поиск гиперссылок по тексту гиперссылки

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

Для примера, рассмотрим следующий исходный код страницы:

Are you sure you want to do this?

Continue Cancel
Элемент-гиперссылка с адресом «continue.html» может быть получен следующим образом:

Continue_link = driver.find_element_by_link_text("Continue") continue_link = driver.find_element_by_partial_link_text("Conti")

4.5. Поиск элементов по тэгу

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

Для примера, рассмотрим следующий исходный код страницы:

Welcome

Site content goes here.


Элемент заголовка h1 может быть найден следующим образом:

Heading1 = driver.find_element_by_tag_name("h1")

4.6. Поиск элементов по классу

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

Для примера, рассмотрим следующий исходный код страницы:

Site content goes here.


Элемент “p” может быть найден следующим образом:

Content = driver.find_element_by_class_name("content")

4.7. Поиск элементов по CSS-селектору

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

Для примера, рассмотрим следующий исходный код страницы:

Site content goes here.


Элемент “p” может быть определен следующим образом:

Content = driver.find_element_by_css_selector("p.content")
На Sauce Labs есть

Стандартное решение через xsl:choose и его недостатки

Подобные задачи в XSLT традиционно решают так:

< xsl : choose > < xsl : when test = " $cond " > < xsl : copy-of select = " $one " /> < xsl : otherwise > < xsl : copy-of select = " $two " />

Однако это решение, помимо очевидной громоздкости, обладает еще одним, весьма существенным недостатком: то, что получается в результате — RTF. Т.е. результат такого выражения приемлем в выходном потоке, но никак не может быть использован (по крайней мере в рамках "чистого" XSLT 1.0, без расширений) для последующих преобразований.

Конечно, с помощью exsl:node-set (или ее аналога), мы можем превратить этот RTF в полноценное множество узлов, но останется одна проблема: это будет новое множество узлов, никак не связанное с исходными документами. Узел, полученый в результате преобразования, будет его корневым узлом — применить к нему, к примеру, ось ancestor мы уже не сможем.

Попробуем преодолеть этот недостаток.

Решение на чистом XPath 1.0

Решение

Воспользуемся тем фактом, что при задании осей выборки мы можем использовать предикаты , налагающие на них ограничивающие условия: $one[$cond] | $two .

Его недостатки

Это выражение лишено недостатков "классического" варианта, но у него есть свои, «дополняющие» их:

  1. Если xsl:choose нельзя (кросс-процессорным способом) использовать для получения node-set"ов, то этот вариант, напротив, может работать только с node-sets в качестве как параметров, так и результата.

Т.е. приведенный пример будет работать только если $one и $two — множества узлов. Запихнуть в это выражение строку или число уже не получится.

  1. Если в примере вместо переменной $cond использовать некоторое сложное выражение, результат его вычисления может зависеть от содержимого $one и $two .

Например $one | $two — значение count(.) будет разным в обоих случаях, и совсем не тем, которое, очевидно, имел бы ввиду гипотетический горе-автор такого выражения...

  1. Исходное выражение достаточно сложно для понимания. Когда я привел пример его употребления в форуме, мне приходилось слышать замечания, что «вообще-то | в xslt — это не or , а объединение нодесетов...»

Совершенно верно, но or в XPath работает отнюдь не так, как привыкли пользователи большинства современных языков. Оно возвращает результат уже преобразованным в логический тип, что делает его совершенно непригодным для данной задачи. И здесь нужно использовать именно объединение с последующей проверкой, хоть это и не очевидно.

  1. При обработке этого выражения вычисляются обе ветки и лишь затем проверяется их соответствие условию. Хотя это почти никогда не вызывает проблем (выражения XSLT не должны иметь побочных действий) — иногда это все же имеет значение.

Утверждение «никакие функции в XSLT не имеют побочных эффектов» неверно . Помимо функций расширений (а наличие у них побочных эффектов вполне возможно), есть по крайней мере одна такая функция, входящая в спецификацию XSLT 1.0. Это функция document : действительно, вряд ли кто-нибудь станет утверждать, что вызов document("http://example.com/cgi-bin/counter.pl") побочных эффектов иметь не будет...

Заметим сразу, что преодолеть эти недостатки этого выражения в рамках «чистого» XSLT 1.0 (т.е. без расширений) нельзя. В этом случае остается лишь предложить использовать этот вариант, если нам нужен на выходе node-set, и "классический" — во всех остальных случаях.

Использование расширений EXSLT

Если же снизить рамки требований по совместимости, и допустить использование EXSLT, то нам представляется отличная возможность написать собственную функцию, эмулирующий тернарный оператор, лишенный большей части указанных недостатков.

Для создания функций расширений EXSLT предоставляет элементы func:function и func:result , где префикс func: соответствует xmlns http://exslt.org/functions .
Первый из них практически точно соответствует элементу XSLT 2.0 xsl:function . Аналога же result там нету, для создания возвращаемых значений используется стандартный синтаксис коструктора последовательности .

Решение для общего случая

Итак, определим собственную функцию расширения (lib: — префикс нашего собственного пространства имен, допустим — urn:xslt:library):

< func : function name = " lib:if" > < xsl : choose > < xsl : when test = " $cond " > < func : result select = " $then" /> < xsl : otherwise > < func : result select = " $else" />

Наш пример с ее помощью запишется так: lib:if($cond, $one, $two) .

Если есть побочные эффекты

Приведенный выше варинат лишен многих недостатков, присущих стандартным решениям. Он компактен, прост для понимания, его можно использовать с аргументами любого типа, вычисление $cond происходит в момент вызова функции. Однако осталась одна проблема: оба остальных параметра тоже вычисляются в момент вызова функции.

В большинстве случаев этим можно пренебречь. Но если наши аргументы имеют побочные эффекты — это недопустимо. Потому воспользуемся для решения этой задачи еще одной функцией EXSLT — dyn:evaluate , где xmlns:dyn = "http://exslt.org/dynamic" . Она, как нетрудно догадаться, получает в качестве аргумента строку и вычисляет ее как выражение XPath непосредственно в том контексте , в котором вызвана:

< func : function name = " lib:if-dyn" > < xsl : choose > < xsl : when test = " $cond " > < func : result select = " dyn:evaluate($then)" /> < xsl : otherwise > < func : result select = " dyn:evaluate($else)" />

Таким образом, любое выражение, которое мы передадим этой функции — в виде строки, не следует забывать об этом — будет вычислено лишь в том случае, если оно действительно используется. Но следует отметить, что lib:if , вообще говоря, должна выполняться быстрее (и не требовать еще одного модуля расширений) — поэтому, если нам не нужна эта дополительная функциональность, всегда следует использовать lib:if .

я решил озадачиться статьёй про XPath. Зачем мне нужен XPath? У меня есть задача организовать препроцессинг данных: Есть, например, набор операций, описываемых XML документом. Каждая операция — это либо веб-запрос либо запрос к базе данных. Мне необходимо поля из предшествующих операций, подставлять в последующие операции… Примерно так.

50 ... здесь мне необходимо использовать значение элемента request из operation id="1"

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

В моём случае, мне нужно было выбирать из XML некоторые поля, чтобы формировать другие XML документы, содержащие указанные поля. Конечно такая задача может быть решена и без XPath. Но тогда логика будет зашита в код программы. А XPath позволяет логику поиска внутри XML документов вынести во внешние конфигурационные данные. Т.е. для моей программы входными данными являются и XML документы и XPath выражения. В таком сочетании и проявляется мощь XPath.

P.S. Впоследствии, в дополнение к теоретической части, я написал пару постов с описанием практического применения XPath на Java:

Это предисловие. Приступаем к изучению. От добра-добра не ищут. На предмет XML-технологий пока самый интересный сайт: w3schools.org. Поэтому просто перевожу XPath tutorial оттуда.

    • Применение XPath на Java

XPath используется для навигации по элементам и атрибутам XML документа. XPath занимает главенствующее место среди W3C XSLT стандартов. XQuery и XPointer — оба базируются на XPath expressions.

Что есть XPath?

  • XPath — синтаксис для определения частей XML документа
  • XPath использует path expressions для навигации в XML документах
  • XPath содержит библиотеку стандартных функций
  • XPath базовый элемент в XSLT
  • XPath имеет статус W3C recommendation

XPath Path Expressions

XPath использует path expressions для выбора узлов или наборов узлов в XML документе. Path expressions сильно напоминают пути, которые вы используете при работе с традиционными компьютерными файловыми системами.

XPath Standard Functions

XPath включает более 100 встроенных функций. Эти функции для строковых значений, цифровых значений, сравнения времени и дат, манипуляции узлами и QName manipulation, манипуляции последовательностью, работы с boolean значениями и другие.