Как работает xpath. Технология XPath. Использование PHP функций совместно с XPath

Стандартное решение через 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.

Получить текст заголовока 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 есть

XPath используется для навигации по элементам и атрибутам XML-документа. XPath является одним из основных элементов в стандарте XSLT консорциума W3C.

1 Что такое XPath

Выражения XPath

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

Стандартные функции XPath

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

XPath используется в XSLT

XPath является одним из основных элементов в стандарте XSLT. Без знания XPath вы не будете иметь возможность создавать XSLT-документы.

2 Терминология XPath

Узлы

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

Harry Potter J. K. Rowling 2005 29.99

Пример узлов в документе XML выше:

(корневой элемент) J. K. Rowling (узел) lang="en" (атрибут)

Атомарные значения

Атомарные значения являются узлами, не имеющие детей или родителей. Пример атомарных значений:

J. K. Rowling "en"

Элементы

Элементы - это атомарные значения или узлы.

3 Отношения узлов

Родитель

Каждый элемент и атрибут имеет одного родителя. В следующем примере элемент «книга» (book) является родителем элементов «название» (title), «автор» (author), «год» (year) и «цена» (price):

Harry Potter J K. Rowling 2005 29.99

Потомки

Узлы элементов могут иметь ноль, один или более потомков. В следующем примере элементы «название», «автор», «год» и «цена» - они все потомки элемента книга:

Harry Potter J K. Rowling 2005 29.99

Элементы одного уровня

Это узлы, которые имеют одного и того же родителя. В следующем примере элементы «название», «автор», «год» и «цена» все являются элементами одного уровня:

Harry Potter J K. Rowling 2005 29.99

Предки

Родитель узла, родитель родителя узла и т.д. В следующем примере предки элемента «название» (title) - это элементы «книга» (book) и «книжный магазин» (bookstore):

Harry Potter J K. Rowling 2005 29.99

Потомки

Дети узла, дети детей узла и т.д. В следующем примере потомками элемента «книжный магазин» являются элементы «книга», «название», «автор», «год» и «цена»:

Harry Potter J K. Rowling 2005 29.99

4 Синтаксис XPath

XPath использует выражения пути для выбора узлов или множества узлов в документе XML. Узел можно выбрать, следуя пути или по шагам. Мы будем использовать следующий XML-документ в приведённых ниже примерах.

Harry Potter 29.99 Learning XML 39.95

Выбор узлов

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

В приведенной ниже таблице перечислены некоторые пути выражения и результат выполнения выражения:

Выражение XPath Результат
bookstore Выбирает все узлы с именем "bookstore"
/bookstore Выбирает корневой элемент книжного магазина

Примечание: Если путь начинается с косой черты (/), он всегда представляет собой абсолютный путь к элементу!

bookstore/book Выбирает все элементы «книга» (book), которые являются потомками элемента «книжный магазин» (bookstore)
//book Выбирает все элементы «книга» независимо от того, где они находятся в документе
bookstore//book Выбирает все элементы «книга», которые являются потомком элемента «книжный магазин», независимо от того, где они находятся под элементом «книжный магазин»
//@lang Выбирает все атрибуты, которые называются "lang"

Предикаты

Предикаты используются для поиска специфического узла или узла, который содержит специфическое значение. Предикаты всегда обрамляются квадратными скобками. В приведённой ниже таблице перечислены некоторые выражения пути с предикатами, и результат выражения:

Выражения XPath Результат
/bookstore/book Выбирает первый элемент «книга», который является потомком элемента «книжный магазин».

Примечание: В IE 5,6,7,8,9 первый узел имеет индекс , но в соответствии с рекомендациями W3C, это . Для решения этой проблемы в IE, задаётся опция "SelectionLanguage" для XPath:

На JavaScript: xml .setProperty("SelectionLanguage", "XPath");
/bookstore/book Выбирает последний элемент «книга» (book), который является дочерним элементом элемента «книжный магазин» (bookstore)
/bookstore/book Выбирает предпоследний элемент «книга», который является дочерним элементом элемента «книжный магазин»
/bookstore/book Выбор первых двух элементов «книга», которые являются потомками элемента «книжный магазин»
//title[@lang] Выбирает все элементы «название» (title), которые имеют атрибут с именем "lang"
//title[@lang="en"] Выбирает все элементы «название», которые имеют атрибут «язык» со значением "en"
/bookstore/book Выбирает все элементы «книга» после элемента «книжный магазин», которые имеют элемент «цена» со значением больше, чем 35.00
/bookstore/book/title Выбирает все элементы «название» книги элемента «книжный магазин», которые имеют элемент «цена» со значением больше, чем 35.00

Выбор неизвестных узлов

Специальные символы XPath могут использоваться для выбора неизвестных XML узлов.

В приведённой ниже таблице мы перечислили некоторые пути выражения и результаты выражений:

Выбор нескольких путей

С помощью оператора | в выражениях XPath вы можете выбрать несколько путей. В таблице ниже перечислены несколько выражений путей и результаты их применения:

5 Оси XPath

Мы будем использовать следующий XML документ далее в примере.

Harry Potter 29.99 Learning XML 39.95

Оси определяют наборы узлов, относительно текущего узла.

Название оси Результат
ancestor Выбирает всех предков (родителей, прародителей и т.д.) текущего узла
ancestor-or-self Выбирает всех предков (родителей, прародителей и т.д.) текущего узла и сам текущий узел
attribute
child
descendant Выбирает всех потомков (детей, внуков и т.д.) текущего узла
descendant-or-self Выбирает всех потомков (детей, внуков и т.д.) текущего узла и сам текущий узел
following Выбирает всё в документе после закрытия тэга текущего узла
following-sibling Выбирает все узлы одного уровня после текущего узла
namespace Выбирает все узлы в данном пространстве имён (namespace) текущего узла
parent Выбирает родителя текущего узла
preceding Выбирает все узлы, которые появляются перед текущим узлом в документе, за исключением предков, узлов атрибутов и узлы пространства имён
preceding-sibling Выбирает всех братьев и сестёр до текущего узла
self Выбирает текущий узел

6 Выражения пути выборки

Путь определения местоположения может быть абсолютным или относительным. Абсолютный путь расположения начинается с косой черты (/), а относительный - нет. В обоих случаях путь выборки состоит из одного или нескольких шагов, разделённых косой чертой:

Абсолютный путь расположения:

/step/step/...

Относительный путь выборки расположения:

Step/step/...

Каждый шаг оценивается по узлам в текущем наборе узлов. Шаг состоит из:

  • ось (определяет древовидную связь между выбранными узлами и текущим узлом);
  • проверка узла (идентифицирует узел в пределах оси);
  • ноль или более предикатов (для дальнейшего уточнения выбранного набор узлов)

Синтаксис шага выборки такой:

Axisname::nodetest имяОси::проверкаУзла[предиктор]

Пример Результат
child::book Выбирает все узлы «книга» (book), которые являются потомками текущего узла
attribute::lang Выбирает атрибут «язык» (lang) текущего узла
child::* Выбирает всех потомков текущего узла
attribute::* Выбирает все атрибуты текущего узла
child::text() Выбирает все текстовые узлы текущего узла
child::node() Выбирает всех ближайших потомков текущего узла
descendant::book Выбирает всех потомков текущего узла
ancestor::book Выбирает всех предков «книга» (books) текущего узла
ancestor-or-self::book Выбирает всех предков «книга» (book) текущего узла - и текущий узел, если он также «книга» (book)
child::*/child::price Выбирает все потомки «цена» (price) через один уровень от текущего узла

7 Операторы XPath

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

Оператор Описание Пример
| Вычисляет два набора узлов //book | //cd
+ Сложение 6 + 4
- Вычитание 6 - 4
* Умножение 6 * 4
div Деление 8 div 4
= Равенство price=9.80
!= Неравенство price!=9.80
< Меньше, чем price<9.80
<= Меньше или равно price≤9.80
> Больше, чем price>9.80
>= Больше или равно price≤9.80
or Или price=9.80 or price=9.70
and И price>9.00 and price<9.90
mod Остаток от деления 5 mod 2

8 Примеры XPath

Давайте рассмотрим базовый синтаксис XPath на нескольких примерах. Мы будем использовать следующий XML документ "books.xml" в примерах ниже:

Everyday Italian Giada De Laurentiis 2005 30.00 Harry Potter J K. Rowling 2005 29.99 XQuery Kick Start James McGovern Per Bothner Kurt Cagle James Linn Vaidyanathan Nagarajan 2003 49.99 Learning XML Erik T. Ray 2003 39.95

Загрузка XML документа

Используйте XMLHttpRequest для загрузки XML документов, который поддерживается большинством современных браузеров:

Var xmlhttp=new XMLHttpRequest()

Код для устаревших браузеров Microsoft (IE 5 и 6):

Var xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")

Выбор узлов

К сожалению, работа с XPath в Internet Explorer и в других браузерах может отличаться. В наших примерах мы будем использовать код, который должен работать в большинстве браузеров. Internet Explorer использует метод "selectNodes()" для выбора узлов XML документа:

XmlDoc.selectNodes(xpath);

Firefox, Chrome, Opera и Safari используют метод evaluate() для выбора узлов из XML документа:

XmlDoc.evaluate(xpath, xmlDoc, null, XPathResult.ANY_TYPE, null);

Выбор всех заглавий

Следующий пример выбирает все узлы заголовков:

/bookstore/book/title

Выбор заголовка первой книги

Следующий пример выбирает заголовок первого узла «книга» после элемента «книжный магазин» (bookstore):

/bookstore/book/title

Выбор всех цен

Следующий пример выбирает текст всех узлов «цена» (price):

/bookstore/book/price

Выбирает узлы с ценой >35

Следующий пример выбирает все узлы с ценами выше 35:

/bookstore/book/price

Выбор узлов заголовков с ценой >35

Следующий пример выбирает все узлы заголовков с ценой выше 35:

/bookstore/book/title

Сегодня мы плотно рассмотрим тему использования XPath вместе с PHP. Вы увидите на примерах, как XPath значительно сокращает количество кода. Рассмотрим использование запросов и функций в XPath.

В начале, предоставлю вам два типа документов: DTD и XML, на примере которых мы рассмотрим функционирование PHP DOM XPath. Вот как они выглядят:

A Book An Author Horror chapter one Another Book Another Author Science Fiction chapter one

Основные XPath запросы

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

//library/book

Вот так! Два слеша впереди определяют корневой элемент документа, а один слеш производит переход к дочернему элементу book. Это просто и быстро, не так ли?

Но что, если вы хотите выбрать определенный элемент book из множества? Давайте предположим, что вам нужны книги «Определенного автора». XPath запрос для этого будет следующим:

//library/book/author/..

Вы можете использовать text() в квадратных скобках для сравнения значения узла. Также «/..» означает, что мы хотим использовать родительский элемент (т. е. возвращаемся на один узел выше).

XPath запросы осуществляются с помощью одной или двух функций: query() и evaluate() . Обе формируют запрос, но разница в возвращаемом результате. query() всегда будет возвращать DOMNodeList , в отличии evaluate() будет возвращать текстовый результат, если это возможно. Для примера, если ваш XPath запрос будет возвращать количество книг написанных определенным автором, тогда query() возвратит пустой DOMNodeList, evaluate() просто возвратит число, вы можете использовать это непосредственно для получения данных из узла.

Код и преимущества скорости XPath

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

domDocument->getElementsByTagName("author"); foreach ($elements as $element) { if ($element->nodeValue == $author) { $total++; } } return $number; }

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

domDocument); $result = $xpath->query($query); return $result->length; }

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

domDocument); return $xpath->evaluate($query); }

Мы можем получить информацию, которую нам нужно, с помощью одной строки XPath запроса. Нет необходимости создавать множество PHP фильтров. Это наиболее простой и быстрый способ написать этот функционал!

Заметьте, что evaluate() использовался в последнем примере. Это потому что функция count() возвращает текстовый результат. Используя query(), возвратиться DOMNodeList, но он будет пустым.

XPath стоит использовать, не только потому что это делает ваш PHP код проще, это также дает преимущество в скорости выполнения кода. Я заметил, что первая версия была на 30% быстрее в среднем, по сравнению со второй. Но третья на 10% быстрее первой. Конечно же, это зависит от вашего сервера и запросов, которые используете. Использование XPath в его чистом виде, дает величайшие результаты в скорости и простоте написания кода.

XPath Функции

Вот несколько функций, которые могут использоваться с XPath. Также вы найдете множество ресурсов, которые детально рассматривают каждую доступную функцию. Если вам нужно вычислять DOMNodeList или сравнивать nodeValue (значение узла), можно найти подходящую XPath функцию, которая исключит использование лишнего PHP кода.

Вы уже это знаете на примере count() функции. Давайте воспользуемся функцией id(), для получение названий книг с заданными ISBN. Для этого нужно использовать следующее XPath выражение:

id("isbn1234 isbn1235")/title

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

domDocument); $result = $xpath->query($query); $books = array(); foreach ($result as $node) { $book = array("title" => $booknode->nodeValue); $books = $book; } return $books; }

Обработка сложных функций в XPath невероятно проста.

Использование PHP функций совместно с XPath

Иногда вам необходимо будет больше функциональности, которую не могут предоставить стандартные функции XPath. К счастью, PHP DOM позволяет взаимодействовать собственным функциям PHP с XPath запросами.

Давайте рассмотрим пример, который возвращает количество слов в названии книги. В этой простейшей функции, мы напишем следующее:

domDocument); $result = $xpath->query($query); $title = $result->item(0)->getElementsByTagName("title") ->item(0)->nodeValue; return str_word_count($title); }

Но, мы также можем включить функцию str_word_count() непосредственно в XPath запрос. Это можно сделать с помощью нескольких шагов. Прежде всего, нам нужно зарегистрировать namespase с XPath объектом. PHP функции в XPath запросах вызываются с помощью строки «php:functionString», после чего прописывается имя желаемой функции. Также, namespace более подробно рассматривается на http://php.net/xpath. Другие значения namespace будут выдавать ошибку. После этого нам нужно вызвать registerPHPFunctions(). Эта функция сообщает PHP, что когда идет обращение через namespace «php:», этот вызов будет обрабатывать именно PHP.

Примерный синтаксис вызова функций будет следующим:

php:functionString("nameoffunction", arg, arg...)

Давайте совместим все это вместе в следующем примере функции getNumberOfWords():

domDocument); //регистрируем php namespace $xpath->registerNamespace("php", "http://php.net/xpath"); //теперь php функции могут вызываться в xpath запросах $xpath->registerPHPFunctions(); $query = "php:functionString("str_word_count",(//library/book[@isbn = "$isbn"]/title))"; return $xpath->evaluate($query); }

Заметьте, что вам не нужно вызывать XPath функцию text() чтобы получить текст узла. Метод registerPHPFunctions() делает это автоматизированным. Хотя, следующий пример строки кода также будет валидным:

php:functionString("str_word_count",(//library/book[@isbn = "$isbn"]/title))

Регистрирование PHP функций не ограничено для функций, которые включены в PHP. Вы можете определить свои собственные функции и использовать их внутри XPath. Единственное отличие в том, что придется использовать «php:function» вместо «php:functionString».

Давайте напишем функцию, которая будет за пределами класса, для демонстрации базовой функциональности. Функция, которую мы будем использовать, возвращает книги автора «George Orwell». Она должна возвращать true для каждого узла, который вы хотите включить в запрос.

nodeValue == "George Orwell"; }

Аргумент, который передается в функцию, является массивом DOM элементов. Эта функция проходит по массиву и определяет нужные элементы, после чего включает их в DOMNodeList. В этом примере, испытываемый узел был /book, также мы использовали /author для определения нужных элементов.

Теперь мы можем создать функцию getGeorgeOrwellBooks():

domDocument); $xpath->registerNamespace("php", "http://php.net/xpath"); $xpath->registerPHPFunctions(); $query = "//library/book1"; $result = $xpath->query($query); $books = array(); foreach($result as $node) { $books = $node->getElementsByTagName("title") ->item(0)->nodeValue; } return $books; }

Если функция compare() статическая, тогда вам нужно внести поправку в XPath запрос:

//library/book

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

В завершение

XPath - это отличный способ сократить количество кода и повысить его обработку, при работе с XML. Дополнительная функциональность PHP DOM позволяет вам расширить XPath функции. Это реально полезная штука, если вы будете ее использовать и углубляться в специфику, вам придется меньше и меньше писать кода.




Есть вопросы?

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: