Xpath как найти второй элемент

First notice that the provided XML is not well-formed!

I assume that intended wellformed XML more or less looks like this:

<table>
    <tr>
        <td class="data1"><p>1</p></td>
        <td class="data1"><p>2</p></td>
        <td class="data1"><p>3</p></td>
        <td class="data1"><p>4</p></td>
    </tr>
    <tr>
        <td class="data1"><p>5</p></td>
        <td class="data1"><p>6</p></td>
        <td class="data1"><p>7</p></td>
        <td class="data1"><p>8</p></td>
    </tr>
</table>

For this XML document here are my answers.

Use:

/*/*/td[position() = 3 or position() = 4]/p

Or, you can use the XPath union operator:

/*/*/td[3]/p | /*/*/td[4]/p

The following is wrong:

/*/*/td[3] [4]/p

This specifies the selection of 4th node of /*/*/td[3] and will select nothing because /*/*/td[3] only selects two nodes.

Lastly, here is a transformation that when run demonstrates the result of all XPath expressions above:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:copy-of select=" /*/*/td[position() = 3 or position() = 4]/p"/>

  ----------------------

  <xsl:copy-of select="   /*/*/td[3]/p  | /*/*/td[4]/p"/>

  ----------------------

  <xsl:copy-of select=" /*/*/td[3][4]/p"/>

 </xsl:template>
</xsl:stylesheet>

Two solutions have been proposed so far, but none of them selects anything from the HTML snippet you show in the question. When simply wrapping it in a root element:

<root>
 <div>
    <div class="content">test1</div>
 </div>
 <div>
    <div class="content">test2</div>
 </div>
 <div>
    <div class="content">test3</div>
 </div>
</root>

Both //div[@class='content'][2] and //div[2][@class='content'] select nothing from that document. They both assume that the second predicate (between [ and ]) is applied to an intermediate result sequence, but the second predicate is also applied to the nodes of the initial document tree. To select a subset of an «intermediate» result, use parentheses around the first part:

(//div[@class='content'])[2]

and the single result will be

<div class="content">test2</div>

More details

//div[@class='content'][2] means:

Select all elements called div from anywhere in the document, but only the ones that have a class attribute whose value is equal to «content». Of those selected nodes, only keep those which are the second div[@class = 'content'] element of their parent.

So, this expression would only select a result from the following document:

<root>
 <div>
    <div class="content">test1</div>
 </div>
 <div>
    <div class="content">test2</div>
    <div class="other">other</div>
    <div class="content">MATCH</div>
 </div>
 <div>
    <div class="content">test3</div>
 </div>
</root>

//div[2][@class='content'] means:

Select all elements called div from anywhere in the document, but only the ones that are the second child of their parent. Of those selected nodes, only keep those which have a class attribute whose value is equal to «content».

Only producing a match given a document like

<root>
 <div>
    <div class="content">test1</div>
 </div>
 <div>
    <div class="other">other</div>
    <div class="content">MATCH</div>
 </div>
 <div>
    <div class="content">test3</div>
 </div>
</root>

Я пытаюсь выбрать div который выделен синей полосой:

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

Это второй div на странице, который содержит текст "All", поэтому моя попытка следующая:

all_button = driver.find_element_by_xpath("//div[contains(., 'All')][2]")

По какой-то причине, когда я запускаю свою программу, это выбирает div, выделенный желтым цветом. Я попытался использовать string() и text() вместо периода в секции contains(), но он дает тот же результат. Есть идеи?

01 июль 2018, в 05:05

Поделиться

Источник

2 ответа

замещать

//div[contains(., 'All')][2]

с

(//div[contains(., 'All')])[2]

чтобы выбрать второй такой div а не ограничивать такие элементы div во второй дочерней позиции.

Вы можете еще более затянуть предикат, чтобы использовать строковое равенство, а не сдерживание подстроки (чтобы избежать совпадения <div>All good men</div>, например), если ваши данные действительно показаны:

(//div[.='All'])[2]

kjhughes
01 июль 2018, в 03:59

Поделиться

Вы можете попробовать быть более строгим с вашим выбором, добавив условие:

all_buttons = driver.find_element_by_xpath(
          "//class[contains(@class, 'filter-option selected') and contains(text(), 'All')]")

Теперь для отладки проверьте размер возвращаемого списка:

print(len(all_buttons))

Если размер списка кнопок «Все», полученный в соответствии с DOM, вы должны делать все.

AutomatedOwl
01 июль 2018, в 03:02

Поделиться

Ещё вопросы

  • 0JS не работает на ПК, но работает на JSFIddle
  • 0Проверка в PHP не понимается
  • 1Нахождение холста в визуальном дереве
  • 1Python — удаление пропорции / процента словарных пар
  • 0Функция клика Jquery работает только дважды
  • 1Как создать параллельную очередь Java, из которой мы можем заблокировать более одного элемента за один вызов?
  • 0как вызвать на jquery.smart мастере кнопку Готово?
  • 0Как очистить список, если ответ ложный
  • 0JavaScript: список не обновляется после всплывающего поиска
  • 1Дополнительный набор баров на участке в Pandas?
  • 1Как открыть разговор в WhatsApp на определенный номер с намерением
  • 1Можно ли выполнить стартовый код в службе WCF до того, как кто-то подключится?
  • 1Включить / отключить все входы и флажки с любым флажком
  • 1Как добавить p5.dom в p5.js в режиме экземпляра
  • 0Возможность обмена переменными из директив на сервисы в Angular
  • 0Как подсчитать конкретные строки в GROUP BY SQL
  • 0AngularJS / Javascript — Как я могу заменить весь объект на JSON
  • 1Как я могу удалить первые n столбцов / строк с 0 значениями в 2D матрице?
  • 0Я хочу выбрать каждое значение в раскрывающемся списке, и он будет динамически выполнять модульное тестирование с использованием транспортира
  • 1Создание выполнения элемента с помощью KitItems с использованием NetSuite SuiteTalk в C #
  • 0Хороший материал 4 Learning Mysql Workbench
  • 0удалить элемент из локального хранилища
  • 1256-битное шифрование Blowfish в Java
  • 1Неожиданное поведение при программном переключении исключений в Android P и Q Beta
  • 1Разрешение службы ServiceStack и определение типа контента
  • 1Панды: ускорение группового
  • 0PHP — Symfony2 Функциональное тестирование HTTPS-маршрутов
  • 0Скомпилировать функцию MATLAB во что-то, что можно запустить на терминале linux без MATLAB?
  • 1«Ни один экземпляр типа Hra не доступен».
  • 1OSMnx Получить координаты Lat Lon чистых узлов пересечения
  • 1Ошибка «JAVA_HOME не установлена» при установке pig. Что с этим делать?
  • 1403.14 запрещенная ошибка для контроллера с именем «безопасность» в IIS 8.0 Express
  • 1как получить куки-файлы двух серверов 1. запускает приложение (HTTP), 2. запускает сервер websocket (WS)?
  • 0JQUERY установить clearInterval через время
  • 0Проблема отправки писем в php [дубликаты]
  • 0Ошибка с командой импорта Sqoop
  • 1Автобокс wth ++, — оператор в Java
  • 1Получить все документы с помощью GridFSOperations
  • 0Конфликт между двумя JavaScript. Плавная прокрутка между якорями и обратно
  • 1давая значение из одного класса в расширенный Java
  • 0Угловой JS ngclick в ngrepeat не работает
  • 0Активная запись реляционной базы данных Yii Framework 2.0
  • 1Как раздеть рисунок в струне?
  • 1В Joda-time 2.1 имеет значение long, переданное в DateTimeZOne.getStandardOffset?
  • 1Трудности с использованием Python-запроса (POST) + API
  • 0Как отфильтровать определенную базу данных с помощью grep
  • 0отображать высоту div в соответствии с высотой другого div
  • 0Синтаксис управления потоком без контейнеров в select не работает в IE8

Сообщество Overcoder

При работе с Selenium если элемент на веб-странице не обнаруживаются общеизвестными локаторами locators, использующими значения атрибутов дерева DOM таких как id, class и name, то для его поиска используют либо CSS селекторы, либо локаторы XPath (XML Path).

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

В этом руководстве мы познакомимся с некоторами особенностями языка ХРath применительно к практике использования выражений XPath для поиска сложных или динамически подгружаемых элементов, атрибуты которых также могут динамически изменяться (обновляться).

При рассмотрении примеров, я буду использовать следующий скрипт, который осуществляет поиск элементов на странице поиска Яндекса:

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

import os
import time
from pprint import pprint

# тестовая страница, на которой мы ищем
target_page = "https://yandex.ru/" 
# то самое выражение XPath, которое мы тестируем
xpath_testing = "//div[contains(@class, 'home-logo')]//child::*"  


dir_current = os.getcwd()
driverLocation = dir_current + "chromedriver.exe"
chrome_options = Options()
chrome_options.add_argument("--headless")

driver = Chrome(driverLocation, chrome_options=chrome_options)
data_text = driver.get(target_page)
time.sleep(3)

try:   
    elements_ = driver.find_elements(By.XPATH, xpath_testing)
    
    for element_ in elements_:
        pprint(f"Выбран элемент с тегом: "{element_.tag_name }"")
        pprint(f"Содержимое атрибута class: "{element_.get_attribute('class')}"")
        pprint(f"Текстовое содержимое элемента: {'Нет содержимого' if not element_.text else element_.text}")   
        
except:
    print('Элемент по заданному XPath выражению не найден :(')
    
finally:
    driver.quit()

Переменной target_page присваивается строковое значение, содержащие адрес страницы, на которой мы будем осуществлять поиск элементов. Критерий поиска будем задавать с использованием XPath выражения, которое также в виде строки присваиваем переменной xpath_testing.

Содержание

  • Коротко о XML и XPath
  • Маршруты поиска
    • Абсолютные пути
    • Относительные пути
    • Подстановочные выражения
  • Предикаты
  • Используем индексы для указания позиции элемента
  • Используем логические операторы OR и AND в выражения XPath
  • Используем функции языка XPath
    • Функция text()
    • Функция contains()
    • Функция starts-with()
    • Функция last()
    • Функция position()
  • Используем полные маршруты поиска элементов
    • Ось предков (ancestor axis)
    • Ось следующих одноуровневых узлов (following-sibling axis)
    • Ось дочерних элементов (child axis)
    • Ось следующих узлов (following axis)
    • Ось предыдущих одноуровневых узлов (preceding-sibling axis)
    • Ось предыдущих узлов (preceding axis)
    • Ось потомков (descendant axis)
    • Ось потомков, включая контекстный узел (descendant-or-self axis)
    • Ось предков, включая контекстный узел (ancestor-or-self axis)

Коротко о XML и XPath

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

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

  1. HTML и XML документы состоят из элементов, каждый из которых включает «начальный тэг» (<element>), «конечный тэг» (</element>), а также информацию, заключенную между этими двумя тэгами (содержимое элемента).
  2. Элементы могут быть аннотированы атрибутами, содержащими метаданные об элементе и его содержимом.
  3. Любой документ представляет собой дерево, состоящее из узлов (элементов). Некоторые типы узлов могут содержать другие узлы.
  4. Существует единственный корневой узел, который в конечном счете включает в себя все остальные узлы.

Для выбора узлов и наборов узлов дерева документа и последующей обработки Xml использует особый язык XPath. XPath – это отличный от XML язык, используемый для идентификации определенных частей XML документов (элементов страницы). Применительно к html страницам XPath позволяет писать выражения, позволяющие получить, например, ссылку на первый элемент li неупордоченного списка ul, седьмой дочерний элемент третьего элемента div, ссылку а, содержащую строку «Купить по акции» и т. д. XPath позволяет получать ссылки на элементы по их положению на странице (дереве документа), положению относительно другого элемента, тегу, текстовому содержимому и другим критериям.

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

  1. Корневой узел;
  2. Узлы элементов;
  3. Текстовые узлы;
  4. Узлы атрибутов;
  5. Узлы комментариев.

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

Маршруты поиска

И хотя выражения XPath в Xml могут также возвращать числа, логические и строковые выражения, то есть производить обработку элементов и их содержимого. В Selenium используется лишь подмножество выражений XPath, называемых маршрутами поиска. Маршруты поиска указывают на определенный узел или набор узлов документа (элементов страницы), отвечающих заданным критериям поиска. Каждый маршрут поиска использует как минимум один шаг для идентификации узла или набора узлов документа. Этот набор может быть пустым, содержать один или содержать несколько узлов. Узел может быть корневым, узлом определенного элемента, атрибута, текста или комментария.

Абсолютные пути

Простейшим маршрутом поиска является тот, который указывает на корневой узел документа (страницы). Этот маршрут представляется простой наклонной чертой / и всегда обозначает одно и то же: корневой узел документа. Каждый документ имеет только один корневой узел, являющийся общим корнем дерева узлов. Корневой узел не имеет родительских узлов. Значением корневого узла является значение элемента документа.

С наклонной черты / всегда начинается абсолютный путь к элементу (маршрут поиска). Получить его можно используя либо специальные расширения браузера, либо так как это делается в браузере Chrome. Вызвать окно Chrome DevTools, выделить нужный элемент, кликнув правой клавишей мыши, вызвать контекстное меню, выбрать команду Copy , а затем Copy full XPath.

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

/html/body/div[1]/div[3]/div[3]/div/edvrt/aqwf/ftgr/fdpprt/fgn/dbgrsae/fwap/fwap/ftgr/div/fgn/dhtaq/div/div/div[1]/div[1]/a/div

Если вы используете в качестве тестируемого выражения XPath этот путь, и запустите на выполнение скрипт приведенный выше, то получите ссылку на логотип Яндекса, который находится на главной странице поисковика.

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

Отметим, что корневой элемент страницы имеет абсолютный путь (маршрут) /html и если в качестве XPath выражения мы введем просто ‘/’, то будет возбуждено исключения типа InvalidSelectorException с сообщением «Селектор некоректен. Результат поиска с использованием XPath выражения не возвратил объект элемента. Элемент не найден».

Вывод. Символ / объединяет различные шаги в составной маршрут поиска. Каждый шаг в маршруте является относительным по отношению к предшествующему. Если маршрут начинается с /, то путь является абсолютным, а его первый шаг является относительным по отношению к корневому узлу.

Относительные пути

Относительный путь начинается с двух наклонных черт и следующим за ним одиночным тегом нужного нам элемента. Он может идентифицировать элементы в любом месте веб-страницы. И это позволяет избегать необходимости писать весь длинный абсолютный XPath путь, и вы можете начать его с середины структуры документа страницы (DOM). Он позволяет выбрать все элементы, по заданному тегу на странице, удовлетворяющие указанному критерию поиска. Например, выражение XPath //li ссылается на все элементы li находящиеся на странице (в дереве DOM). Так относительный путь к логотипу Яндекса на странице поиска будет выглядеть следующим образом:

//div[@role = 'img']

Пусть вас пока не смущает выражение в квадратных скобках. Они называются предикатами и служат для сужения диапазона поиска элементов, то есть придания специфичности нашему маршруту поиска. Синтаксис предикатов и их использование мы рассмотрим в нашем руководстве далее. А пока отметим, выражение в квадратных скобках говорит нам о том, что искомый элемент div, должен иметь атрибут role со значением img.

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

//ol[@class = 'list news__list']/li/a

Это XPath выражение позволяет получить пути к ссылкам из списка новостей, размещенного на главной странице поисковика Яндекс. Отметим, этот маршрут получился намного короче чем маршрут, использующий абсолютный путь, а также более понятна его логика. Также отметим, что значение атрибута класса элемента упорядоченного списка ol представляет собой строковое значение состоящее из двух имен соответствующих CSS классов. Если вы укажете в выражении наименование только одного из них, например, так //ol[@class = 'news__list'] ,то получите пустой набор элементов.

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

//ol[@class='list news__list']//a

И мы получим точно такой же результат, как и для выражения выше. Фактически мы заменили все промежуточные элементы из пути к ссылкам на две наклонные черты // и упростили его вид. Таким образом можно убирать из пути к искомому элементу любое количество промежуточных шагов (элементов).

Вывод. В начале выражения XPath символы // по сути позволяют выбрать всех потомков корневого узла с указанным тегом. Например, выражение XPath //div выбирает в документе все элементы div. Если мы будем использовать символ // в маршруте для разделения отдельных шагов, то можем опускать промежуточные шаги сокращая при этом запись маршрута. Относительный путь Xpath всегда предпочтительнее, так как он является более логичным и понятным, а также устойчивым к динамическому изменению структуры дерева DOM страницы средствами движка Javascript.

Подстановочные выражения

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

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

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

Приведем некоторые примеры их использования.

  • //* – соответствует всем элементам, находящимся на странице (включая тег html).
  • //div/* – соответствует всем элементам, являющимися непосредственными потомками элемента с тегом div .
  • //input[@*] – соответствует всем элементам с тегом input, которые имеют хотя бы один любой атрибут, при этом значение атрибута может быть любым, присутствовать или отсутствовать.
  • //*[@*] – соответствует всем элементам на странице, имеющим хотя бы один атрибут.

Предикаты

Как мы уже знаем, что в общем случае выражение XPath может ссылаться более чем на один узел (элемент страницы), то есть метод, в котором оно используется будет возвравращать массив элементов. Иногда это именно то, что нам нужно, однако в некоторых случаях приходится «просеивать» его по определенным критериям, чтобы выбрать только некоторые из них. Для этих целей в XPath используется синктаксис предикатов. Каждый шаг в маршруте поиска может иметь свой предикат или даже несколько, который задает свой критерий выбора из текущего списка узлов на каждом шаге маршрута поиска. То есть на каждом шаге поиска могут существовать один или более предикатов. По сути предикат содержит логическое выражение, которое проверяется для каждого узла в полученном по указанному пути наборе элементов страницы. Если выражение ложно, этот узел удаляется из набора, в противном случае соответствено сохраняется.

Предикат – это часть выражения XPath, заключенная в квадратные скобки, которое следует в инструкции для шага поиска за критерием выбора узла (элемента). В общем виде выражение с предикатом будет выглядеть следующим образом:

//выбор_элементов[правило_предиката1][правило_предиката2][правило_предиката3]  

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

//div[@class='search2__button']/button[@role='button'] 

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

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

//а[@title='Корзина на Маркете'][@class='home-link market-cart']

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

Используем индексы для указания позиции элемента

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

//ol[@class='list news__list']/li[2]
//ol[@class='list news__list']/li[2]//span[@class='news__item-content']

Второе выражение XPath позволянт получить у выбранного элемента списка элемент с тегом span, который содержит текстовое содержимое заголовока новости. Отметим, что индексы элементов начинают отсчитываться от 1, а не от 0, как принято для индексации массивов в языке Python.

Используем логические операторы OR и AND в выражения XPath

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

В примере ниже приведены выражения для фильтрации ссылок на новости, которые показывают на странице поиска Яндекс.

//a[@rel='noopener' or @target='_blank']
//a[@rel='noopener' and @target='_blank']
//a[@rel='noopener' and @target='_blank' and contains(@class, 'home-link_black_yes')]

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

Функция text()

Функция XPath text() – это встроенная в синтаксис XPath Selenium функция, которая используется для поиска элементов на основе строкового значения, содержащегося в его текстовом узле. То есть если элемент имеет текстовое содержимое в виде строки, то элемент можно найти следующим образом:

<span class="button__text">Найти</span>
​
//span[text()='Найти']

Функция contains()

Функция contains() часто используется в предикатах выражений XPath для случаев если значение атрибута элемента или его текстовое содержимое может динамически изменяться.

Например в значение атрибута класса элемента //element[@class='class1 class2'] средствами Javascript может быть добавлен для его анимации класс class3, а потом также динамически убран. При этом значение предиката в случае добавления нового класса станет ложным, то есть элемент не будет выбран. Для этого случая мы можем использовать функцию contains() следующим образом:

//element[contains(@class, 'class1 class2')]

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

Функция contains() позволяет находить элемент по текстовой подстроке значения атрибута или его текстового содержимого, как показано в примере XPath ниже.

//a[contains(@title, 'Корзина')]
//span[contains(text(),'Найти')]

В примере мы нашли ссылку на корзину Яндекс маркета из примера выше по части значения атрибута title. А также по части текстового содержимого кнопку отправки запроса поисковику.

Функция starts-with()

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

//a[starts-with(@title, 'Корзина')]
//span[starts-with(text(),'Найти')]

Функция last()

Эта функция позволяет выбрать последний элемент (указанного типа) из набора элементов. Пример ее использования представлен ниже.

//ol[@class='list news__list']/li[last()]//span[@class='news__item-content']

Это выражение возвращает элемент, содержащий наименование последней новости из списка новостей со страницы поисковика Яндекс.

В следующем примере показано как можно получить предпоследнюю новость.

//ol[@class='list news__list']/li[last()-1]//span[@class='news__item-content']

Функция position()

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

//ol[@class='list news__list']/li[position()=1]"))
//ol[@class='list news__list']/li[1]

Используем полные маршруты поиска элементов

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

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

По сути в сокращенном маршруте поиска ось и критерий узла объединены вместе. Например, следующий сокращенный маршрут поиска состоит из трех шагов.

//ol/li/a[@rel='noopener']

Первый шаг выбирает на странице элементы упорядоченных списков ol по оси дочерних узлов, второй – элементы li вдоль оси дочерних узлов, третий шаг – так же по оси дочерних узлов выбирает элементы ссылок a, а затем с помощью предиката выбирает из них только содержащие атрибут rel='noopener' с заданным значением . Если переписать это выражение в полной форме тот же маршрут поиска будет выглядеть следующим образом:

//child::ol/child::li/child::a[@rel='noopener']

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

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

Так сокращенный синтаксис позволяет перемещаться по оси непосредственно дочерних узлов (child), оси атрибутов (attribute) и оси всех его потомков с включением контекстного узла (descendant-or-self). Полный синтаксис добавляет еще восемь осей, которые применимы для использования в XPath выражениях и поиска элементов на страницах с использованием Selenium:

Ось предков (ancestor axis)

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

//div[text()='Маркет']//ancestor::a
//div[text()='Маркет']//ancestor::*

В данном примере мы получаем ссылку на Яндекс Маркет по текстовому содержимому элемента div находящегося внутри нее. А следующее выражение позволяет выбрать последовательность всех предков этого элемента до корня документа /html.

Ось следующих одноуровневых узлов (following-sibling axis)

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

//div[contains(@class, 'home-logo')]//following-sibling::div

В примере выше выражение выбирает блок div по содержимому атрибута класса, который содержит элементы строки ввода слов для поиска.

Ось дочерних элементов (child axis)

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

//div[contains(@class, 'home-arrow__search-wrapper')]//child::*

Ось следующих узлов (following axis)

Все узлы, следующие после контекстного узла, в том же порядке, в каком узлы присутствуют в документе. Отличием поиска вдоль этой оси от оси following-sibling является то, что будут выбраны все узлы (элементы) находящиеся в документе за закрывающим тегом контекстного узла. Так в аналогичном примере ниже будут выбраны все элементы div, следующие в документе за разметкой логотипа Яндекс. Сравните результаты поиска с примером выше.

//div[contains(@class, 'home-logo')]//following::div

Ось предыдущих одноуровневых узлов (preceding-sibling axis)

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

//div[contains(@class, 'search2__input')]//preceding-sibling::input[@type='hidden']

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

Ось предыдущих узлов (preceding axis)

Все узлы, предшествующие началу контекстного узла, в порядке, обратном порядку в документе. Отличием поиска вдоль этой оси от оси following-sibling является то, что будут выбраны все узлы (элементы) находящиеся в документе перед открывающим тегом контекстного узла. Так в аналогичном примере ниже будут выбраны все элементы div, следующие в документе перед разметкой логотипа Яндекс. Сравните результаты поиска с примерами выше.

//div[contains(@class, 'home-logo')]//preceding::div

Ось потомков (descendant axis)

Поиск вдоль оси потомков descendant выбирает все дочерние элементы, а также их дочерние элементы «внуков». В примере ниже мы выбираем все элементы находящиеся в блоке со строкой поиска на главной странице Яндекса.

//div[contains(@class, 'home-arrow__search-wrapper')]//descendant::*

Ось потомков, включая контекстный узел (descendant-or-self axis)

Ее действие аналогично оси потомков descendant за исключением того, в набор будет включен и сам контекстный узел.

//div[contains(@class, 'home-arrow__search-wrapper')]//descendant-or-self ::*

Ось предков, включая контекстный узел (ancestor-or-self axis)

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

//div[contains(@class, 'home-arrow__search-wrapper')]//ancestor-or-self::*

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

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

Надеюсь, что это руководство поможет вам разобраться с принципом использования XPath выражений при работе в Selenium Python. А также в дальнейшем послужит справочным пособием для разработки.

Время на прочтение
4 мин

Количество просмотров 498K

Xpath — это язык запросов к элементам xml или xhtml документа. Также как SQL, xpath является декларативным языком запросов. Чтобы получить интересующие данные, необходимо всего лишь создать запрос, описывающий эти данные. Всю «черную» работу за вас выполнит интерпретатор языка xpath.
Очень удобно, не правда ли? Давайте посмотри какие возможности предлагает xpath для доступа к узлам веб-страниц.

Создание запроса к узлам веб-страниц

Предлагаю вашему вниманию небольшую лабораторную работу, в ходе которой я продемонстрирую создание xpath запросов к веб-странице. Вы сможете повторить приведенные мной запросы и, самое главное, попробуете выполнить свои. Я надеюсь, что благодаря этому статья будет одинаково интересна новичкам и программистам знакомым с xpath по xml.

Для лабораторной нам понадобятся:
— веб-страница xhtml;
— браузер Mozilla Firefox с дополнениями;
— firebug;
— firePath;
(вы можете использовать любой другой браузер с визуальной поддержкой xpath)
— немного времени.

В качестве веб-страницы для проведения эксперимента предлагаю главную страницу сайта консорциума всемирной паутины (‘http://w3.org’). Именно эта организация разрабатывает языки xquery(xpath), спецификацию xhtml и многие другие стандарты интернета.

Задача

Получить из xhtml-кода главной страницы w3.org информацию о конференциях консорциума при помощи запросов xpath.
Приступим к написанию xpath запросов.

Первый Xpath запрос

Открываем закладку Firepath в FireBug, выделяем с селектором элемент для анализа, нажимаем: Firepath создал xpath запрос к выбранному элементу.

Если вы выделили заголовок первого события, то запрос будет таким:

.//*[@id='w3c_home_upcoming_events']/ul/li[1]/div[2]/p[1]/a

После удаления лишних индексов запрос станет соответствовать всем элементам типа «заголовок».

.//*[@id='w3c_home_upcoming_events']/ul/li/div/p/a

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

Идем дальше. Создаем запросы для поиска мест проведения конференций и их спонсоров либо с помощью селектора, либо модифицировав первый запрос.

Запрос для получения информации о местах проведения конференций:
.//*[@id='w3c_home_upcoming_events']/ul/li/div/p[2]

Так мы получим список спонсоров:
.//*[@id='w3c_home_upcoming_events']/ul/li/div/p[3]

Синтаксис xpath

Давайте вернемся к созданным запросам и разберемся в том, как они устроены.
Рассмотрим подробно первый запрос

В этом запросе я выделил три части для демонстрации возможностей xpath. (Деление на части уловное)

Первая часть
.// — рекурсивный спуск на ноль или более уровней иерархии от текущего контекста. В нашем случае текущий контекст это корень документа

Вторая часть
* — любой элемент,
[@id=’w3c_home_upcoming_events’] – предикат, на основе которого осуществляем поиск узла, имеющего атрибут id равным ‘w3c_home_upcoming_events’. Идентификаторы элементов XHTML должны быть уникальны. Поэтому запрос «любой элемент с конкретным ID» должен вернуть единственный искомый нами узел.

Мы можем заменить * на точное имя узла div в этом запросе
div[@id='w3c_home_upcoming_events']

Таким образом, мы спускаемся по дереву документа до нужного нам узла div[@id=’w3c_home_upcoming_events’]. Нас абсолютно не волнует, из каких узлов состоит DOM-дерево и сколько уровней иерархии осталось выше.

Третья часть
/ul/li/div/p/a –xpath-путь до конкретного элемента. Путь состоит из шагов адресации и условия проверки узлов (ul, li и т.д.). Шаги разделяются символом » /»(косая черта).

Коллекции xpath

Не всегда удается получить доступ к интересующему узлу с помощью предиката или шагов адресации. Очень часто на одном уровне иерархии находится насколько узлов одинакового типа и необходимо выбрать «только первые» или «только вторые» узлы. Для таких случаев предусмотрены коллекции.

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

Исходя из того, что «место проведения» всегда второй параграф после «названия конференции», получаем следующий запрос:
.//*[@id='w3c_home_upcoming_events']/ul/li/div/p[2]
Где p[2] – второй элемент в наборе для каждого узла списка /ul/li/div.

Аналогично список спонсоров мы можем получить запросом:
.//*[@id='w3c_home_upcoming_events']/ul/li/div/p[3]

Некоторые функции хpath

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

last():
Возвращает последний элемент коллекции.
Запрос ul/li/div/p[last()] — возвратит последние параграфы для каждого узла списка «ul».
Функция first() не предусмотрена. Для доступа к первому элементу используйте индекс «1».

text():
Возвращает тестовое содержание элемента.
.//a[text() = 'Archive'] – получаем все ссылки с текстом «Archive».

position() и mod:
position() — возвращает позицию элемента в множестве.
mod — остаток от деления.

Комбинацией данных функций можем получить:
— не четные элементы ul/li[position() mod 2 = 1]
— четные элементы: ul/li[position() mod 2 = 0]

Операции сравнения

  • < — логическое «меньше»
  • > — логическое «больше»
  • <= — логическое «меньше либо равно»
  • >= — логическое «больше либо равно»

ul/li[position() > 2] , ul/li[position() <= 2] — элементы списка начиная с 3го номера и наоборот.

Полный список функций

Самостоятельно

Попробуйте получить:
— четные URL ссылки из левого меню «Standards»;
— заголовки всех новостей, кроме первой с главной страницы w3c.org.

Xpath в PHP5

	$dom = new DomDocument();
	$dom->loadHTML( $HTMLCode );

	$xpath = new DomXPath( $dom );

	$_res = $xpath->query(".//*[@id='w3c_home_upcoming_events']/ul/li/div/p/a");

	foreach( $_res => $obj ) {
                echo 'URL: '.$obj->getAttribute('href');
		echo $obj->nodeValue;
        }

В заключение

На простом примере мы увидели возможности xpath для доступа к узлам веб-страниц.
Xpath является отраслевым стандартом для доступа к элементам xml и xhtml, xslt преобразований.
Вы можете применять его для парсинга любой html-страницы. В случае если исходный html-код содержит значительные ошибки в разметке пропустите его через tidy. Ошибки будут исправлены.

Старайтесь отказаться от регулярных выражений при парсинге веб-страниц в пользу xpath.
Это сделает ваш код проще, понятнее. Вы допустите меньше ошибок. Сократиться время отладки.

Ресурсы

Firepath дополнение Mozzilla Firefox
Краткая аннотация языка в википедии
Xороший справочник по xpath. Не обращайте внимание на то, что он для NET Framework. Xpath во всех средах работает одинаково, за исключением пары специфичных функций
Cпецификация xpath 1.0
Cпецификация xpath 1.0 на русском
XQuery 1.0 and XPath 2.0
Tidy
PHP5 tidy::repairFile

Понравилась статья? Поделить с друзьями:
  • Как найти людей проводников
  • Ошибка а01 котел ферроли как исправить
  • Как исправить error with unzip
  • Как найти площадь прямоугольника по заданным координатам
  • Как найти расстояние между двумя проводниками