Время на прочтение
6 мин
Количество просмотров 29K
Будущих студентов курса «Java QA Automation Engineer» и всех интересующихся приглашаем посмотреть подарочное демо-занятие в формате открытого вебинара.
А также делимся переводом полезной статьи.
В данной статье рассматриваются примеры использования функций XPath для идентификации элементов.
Автоматизация взаимодействия с любым сайтом начинается с корректной идентификации объекта, над которым будет выполняться какая-либо операция. Как нам известно, легче всего идентифицировать элемент по таким атрибутам, как ID, Name, Link, Class, или любому другому уникальному атрибуту, доступному в теге, в котором находится элемент.
Но правильно идентифицировать объект можно только в том случае, если такие атрибуты присутствуют и (или) являются уникальными.
=> Руководство по Selenium для новичков см. здесь
Чему вы научитесь: [показать]
Обзор функций XPath
Обсудим сценарий, при котором атрибуты недоступны напрямую.
Постановка задачи
Как идентифицировать элемент, если такие локаторы, как ID, Name, Class и Link, недоступны в теге элемента?
Суть проблемы демонстрирует следующий пример:
Авторизация в Twitter
Как видно из скриншота выше, заголовок «Log in to Twitter» не имеет дополнительных атрибутов. Поэтому мы не можем использовать ни один из локаторов, таких как ID
, Class
, Link
или Name
, для идентификации этого элемента.
Плагин Firepath для Firefox сгенерировал следующий путь XPath:
//[@id=’page-container’]/div/div[1]/h1
Мы бы не рекомендовали использовать указанный выше путь XPath, поскольку структура страницы или id могут меняться динамически. Если все же использовать этот нестабильный XPath, вероятно, потребуется чаще его обновлять, что подразумевает лишнюю трату времени на поддержку. Это один из случаев, когда мы не можем использовать общее выражение XPath с такими локаторами, как ID, Class, Link или Name.
Решение
Идентификация элемента с помощью функций XPath по тексту
Поскольку у нас есть видимый текст «Log in to Twitter», мы могли бы использовать следующие функции XPath
для идентификации уникального элемента.
-
contains() [по тексту]
-
starts-with() [по тексту]
-
text()
Функции XPath
, такие как contains()
, starts-with()
и text()
, при использовании с текстом «Log in to Twitter» помогут нам корректно идентифицировать элемент, после чего мы сможем произвести над ним дальнейшие операции.
1. Метод Contains()
Синтаксис. Чтобы найти на веб-странице элемент «Log in to Twitter», воспользуйтесь одним из следующих выражений XPath на основе метода contains()
.
Поиск по тексту:
-
//h1[contains(text(),’ Log in to’)]
-
//h1[contains(text(),’ in to Twitter’)]
Примечание. Наличие одного совпадающего узла свидетельствует об успешной идентификации веб-элемента.
Из примера выше видно, что методу contains()
не требуется весь текст для правильной идентификации элемента. Достаточно даже части текста, но эта часть текста должна быть уникальной. Используя метод contains()
, пользователь может легко идентифицировать элемент, даже после смены ориентации страницы.
Обратите внимание, что при указании всего текста «Log in to Twitter» с методом contains()
элемент будет также идентифицирован корректно.
2. Метод starts-with()
Синтаксис. Чтобы найти на веб-странице элемент «Log in to Twitter», используйте следующие выражения XPath на основе метода starts-with().
Поиск по тексту:
-
//h1[starts-with(text(),’Log in’)]
-
//h1[starts-with(text(),’Log in to’)]
Из приведенного выше примера видно, что XPath-функции starts-with()
требуется по крайней мере первое слово («Log») видимого текста для однозначной идентификации элемента. Функция работает даже с неполным текстом, но он должен как минимум включать первое слово частично видимого текста.
Обратите внимание, что при использовании всего текста «Log in to Twitter» с методом starts-with()
элемент будет также идентифицирован корректно.
Недействительный XPath для starts-with()
: //h1[starts-with(text(),’in to Twitter’)]
Примечание. Отсутствие совпадающих узлов свидетельствует о том, что элемент на веб-странице не был идентифицирован.
3. Метод text()
Синтаксис. Чтобы найти на веб-странице элемент «Log in to Twitter», воспользуйтесь следующим выражением XPath
на основе метода text()
.
В этом выражении мы указываем весь текст, содержащийся между открывающим тегом <h1>
и закрывающим тегом </h1>
. Если использовать функцию text()
с частью текста, как в случае с методами contains()
и starts-with()
, то мы не сможем найти данный элемент.
Недействительное выражение Xpath для text()
:
Идентификация элемента с помощью функций XPath по атрибуту
Мы используем функции XPath
(contains
или starts-with
) с атрибутом в тех случаях, когда в теге содержатся уникальные значения атрибутов. Доступ к атрибутам производится с помощью символа «@
».
Для лучшего понимания рассмотрим следующий пример:
Авторизация в Google
1. Метод Contains()
Синтаксис. Чтобы точно идентифицировать кнопку «I’m Feeling Lucky» («Мне повезет») с помощью XPath-функции contains()
, можно указать следующие атрибуты.
Вариант А — поиск по значению атрибута Value
-
//input[contains(@value,’Feeling’)]
-
//input[contains(@value,’Lucky’)]
На скриншотах выше видно, что поиск по атрибуту Value
слов «Feeling» или «Lucky» с помощью функции contains()
позволяет однозначно идентифицировать данный элемент. Стоит отметить, что, даже если мы используем полное содержимое атрибута Value
, мы сможем корректно идентифицировать элемент.
Вариант Б — поиск по содержимому атрибута Name
//input[contains(@name=’btnI’)]
Неправильное использование функции XPath с атрибутом:
Нужно быть крайне внимательным при выборе атрибута для поиска с помощью методов contains()
и starts-with()
. Если значение атрибута не уникальное, мы не сможем однозначно идентифицировать элемент.
Если мы воспользуемся атрибутом type
при идентификации кнопки «I’m Feeling Lucky», то XPath не сработает.
Наличие двух совпадающих узлов свидетельствует о том, что нам не удалось корректно идентифицировать элемент. В данном случае значение атрибута type
не является уникальным.
2. Метод starts-with()
Метод starts-with()
в сочетании с атрибутом может пригодиться для поиска элементов, у которых начало атрибута постоянное, а окончание изменяется. Такой метод позволяет работать с объектами, динамически изменяющими значения своих атрибутов. Его также можно использовать для поиска однотипных элементов.
Перейдите на страницу авторизации Facebook.
Изучите первое текстовое поле First Name (Имя) и второе текстовое поле Surname (Фамилия) формы авторизации.
Первое текстовое поле First Name идентифицировано.
Второе текстовое поле Surname идентифицировано.
В случае обоих текстовых полей, из которых состоит форма авторизации Facebook, первая часть атрибута id
всегда остается неизменной.
First Name id=»u02″
Surname id=»u04″
Это тот случай, когда мы можем использовать атрибут вместе с функцией starts-with()
, чтобы получить все элементы такого типа с атрибутом id
. Обратите внимание, что мы рассматриваем эти два поля только для примера. На экране может быть больше полей с id
, которые начинаются с «u0».
Starts-with() [по атрибуту id]
//input[starts-with(@id,"u0")]
Важное примечание. Здесь мы использовали двойные кавычки вместо одинарных. Но одиночные кавычки тоже будут работать с методом
starts-with
.
11 найденных узлов указывают на то, что данное выражение XPath позволило идентифицировать все элементы, id
которых начинается с «u0». Вторая часть id
(«2» для имени, «4» для фамилии и т. д.) позволяет однозначно идентифицировать элемент.
Мы можем использовать атрибут с функцией starts-with
там, где нам нужно собрать элементы похожего типа в список и динамически выбрать один из них, передавая аргумент в обобщенный метод, чтобы однозначно идентифицировать элемент.
На примере ниже показано использование функции starts-with
.
Пример кода
/ <strong>Generic Method</strong> /
public void xpathLoc(String identifier){
//The below step identifies the element “First Name” uniquely when the argument is “2”
WebElement E1=d1.findElement(By.xpath(«//input[starts-with(@id,”u0”+identifier )]»));
E1.sendKeys(“Test1”); / This step enters the value of First Name as “Test 1” /
}
/ <strong>Main Method</strong>*/
public static void main(String[] args) {
xpathLoc(“2”); — This step calls the xpathLoc() method to identify the first name.
}
Примечание. Eclipse может не допускать использование двойных кавычек. Возможно, вам придется прибегнуть к другому коду, чтобы сформировать динамический XPath.
Приведенный код является лишь примером. Вы можете усовершенствовать его, чтобы он работал со всеми элементами, выполняемыми операциями и вводимыми значениями (в случае текстовых полей), сделав код более универсальным.
Заключение
В этой статье мы рассмотрели, как можно использовать функции XPath contains()
, starts-with()
и text()
с атрибутом и текстом для однозначной идентификации элементов в структуре HTML DOM.
Ниже приведены некоторые замечания касательно функций XPath:
-
Используйте метод
contains()
в XPath, если известна часть постоянно видимого текста или атрибута. -
Используйте метод
starts-with()
в XPath, если известна первая часть постоянно видимого текста или атрибута. -
Вы также можете использовать методы
contains()
иstarts-with()
со всем текстом или полным атрибутом. -
Используйте метод
text()
в XPath, если вам известен весь видимый текст. -
Нельзя использовать метод
text()
с частичным текстом. -
Нельзя использовать метод
starts-with()
, если начальный текст не используется в XPath или если начальный текст постоянно изменяется.
В следующем уроке мы узнаем, как использовать оси XPath с функциями XPath для более точного определения расположения элементов на веб-странице.
Все знают как написать хороший тест, а может быть даже несколько. Но вот что делать, когда этих тестов у вас больше 100 или возможно даже несколько тысяч?
Об этом расскажем на открытом вебинаре. Регистрируйтесь
Узнать подробнее о курсе «Java QA Automation Engineer» можно
здесь.
ЗАБРАТЬ СКИДКУ
Простой вариант — таблица как на картинке из 3 столбцов, поиск состоит из двух этапов:
-
находим столбец ячейки, который содержит имя «Lois»
.//tr/td[text()[.="Lois"]]
-
Принимаем, что структура таблицы постоянна и получаем второй элемент-родственник относительно столбца «Lois» и его кнопку
.//tr/td[text()[.="Lois"]]/following-sibling::td[position()=2]/button
Второй вариант — если длина таблицы неограничена (кроме имени и фамилии будет куча других параметров), но «Имя» будет начинать строку, а «Кнопка» — заканчивать, мы можем взять последний элемент, который соответствует кнопке:
.//tr/td[text()[.="Lois"]]/following-sibling::td[last()]/button
Самый универсальный вариант, когда мы не зависим от положения ячейки имени и кнопки
.//tr[td[text()[.="Lois"]]]/td/button
На еще более универсальный вариант натолкнул комментарий lospejos.
Учитываем, что имя «Lois» соответствует столбцу «Firstname», а не какому-нибудь другому. Мы находим позицию заголовка «Firstname» (позиция 1) с помощью запроса
count(tr/th/text()[.="Firstname"]/preceding-sibling::*)+1
и используем ее для поиска имени «Lois» в строке
.//tr[td[count(tr/th/text()[.="Firstname"]/preceding-sibling::*)+1][text()[.="Lois"]]]/td/button
что, с учетом вычисленного count, для машины выглядит так
.//tr[td[1][text()[.="Lois"]]]/td/button
то есть наш элемент должен соответствовать двум условиям — находится под заголовком «Firstname» и иметь имя «Lois»
При работе с 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 такого нет. Кроме того существуют определенные различия в синктаксисе инструкций этих внешне схожих языков.
Однако есть у этих двух языков одно основное сходство, которое, в нашем случае, мы можем эффективно использовать для поиска маршрутов к нужным элементам на странице.
- HTML и XML документы состоят из элементов, каждый из которых включает «начальный тэг» (<element>), «конечный тэг» (</element>), а также информацию, заключенную между этими двумя тэгами (содержимое элемента).
- Элементы могут быть аннотированы атрибутами, содержащими метаданные об элементе и его содержимом.
- Любой документ представляет собой дерево, состоящее из узлов (элементов). Некоторые типы узлов могут содержать другие узлы.
- Существует единственный корневой узел, который в конечном счете включает в себя все остальные узлы.
Для выбора узлов и наборов узлов дерева документа и последующей обработки Xml использует особый язык XPath. XPath – это отличный от XML язык, используемый для идентификации определенных частей XML документов (элементов страницы). Применительно к html страницам XPath позволяет писать выражения, позволяющие получить, например, ссылку на первый элемент li
неупордоченного списка ul
, седьмой дочерний элемент третьего элемента div
, ссылку а
, содержащую строку «Купить по акции» и т. д. XPath позволяет получать ссылки на элементы по их положению на странице (дереве документа), положению относительно другого элемента, тегу, текстовому содержимому и другим критериям.
Согласно методологии XPath существует пять типов узлов, которые могут находиться в дереве документа на обычной html странице:
- Корневой узел;
- Узлы элементов;
- Текстовые узлы;
- Узлы атрибутов;
- Узлы комментариев.
В дальнейшем при формировании путей поиска к искомым элементам страницы мы будем иметь дело с первыми четырьмя типами узлов. И хотя технически мы можем обратиться, также и к узлу комментариев, расположенного в определенном элементе, рационального применения этой возможности при парсинге страниц нет и поэтому далее рассматриваться не будут.
Маршруты поиска
И хотя выражения 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. А также в дальнейшем послужит справочным пособием для разработки.
Improve Article
Save Article
Like Article
Improve Article
Save Article
Like Article
In this article, let’s discuss how to find a button by text using selenium. See the below example to get an idea about the meaning of the finding button by text.
Example:
URL: https://html.com/tags/button/
We need to find the “CLICK ME!” button using the text “Click me!”.
Module Needed:
Selenium: The selenium package is used to automate web browser interaction from Python. It is an open-source tool primarily used for testing. Run the following command in the terminal to install this library:
pip install selenium
Setup Web Drivers:
Web Driver is a package to interact with a Web Browser. You can install any Web Driver according to your browser choice. Install any one of them using the given links-
Here, we are going to use ChromeDriver.
Find xpath of the button:
- Method 1: Using Inspect Element
Right Click on the element you are trying to find the xpath. Select the “Inspect” option.
- Right click on the highlighted area on the console. Go to Copy xpath
- Method 2: Using Chrome Extension to find xpath easily:
We can easily find xpath of an element using a Chrome extension like SelectorGadget.
Approach:
- Import Selenium and time library
- Set the Web Driver path with the location where you have downloaded the WebDriver
Example- “C:\chromedriver.exe” - Call driver.get() function to navigate to a particular URL.
- Call time.sleep() function to wait for the driver to completely load the webpage.
- Use driver.find_element_by_xpath() method to find the button using xpath.
- Finding button by text-
(i) Using normalize-space() method:
driver.find_element_by_xpath(‘//button[normalize-space()=”Click me!”]’)
(ii) Using text() method:
driver.find_element_by_xpath(‘//button’)
Note: It is recommended to use normalize-space() method because it trim the left and right side spaces. It is possible that there can be spaces present at the start or at the end of the target text. - Lastly close the driver using driver.close() function.
Implementation:
Python3
from
selenium
import
webdriver
import
time
driver
=
webdriver.Chrome(executable_path
=
r
"C:\chromedriver.exe"
)
time.sleep(
5
)
driver.find_element_by_xpath(
'//button[normalize-space()="Click me!"]'
).click()
time.sleep(
5
)
driver.close()
Output:
Last Updated :
03 Mar, 2021
Like Article
Save Article
In this article, let’s discuss how to find a button by text using selenium. See the below example to get an idea about the meaning of the finding button by text.
Example:
URL: https://html.com/tags/button/
We need to find the “CLICK ME!” button using the text “Click me!”.
Module Needed:
Selenium: The selenium package is used to automate web browser interaction from Python. It is an open-source tool primarily used for testing. Run the following command in the terminal to install this library:
pip install selenium
Setup Web Drivers:
Web Driver is a package to interact with a Web Browser. You can install any Web Driver according to your browser choice. Install any one of them using the given links-
Here, we are going to use ChromeDriver.
Find xpath of the button:
- Method 1: Using Inspect Element
Right Click on the element you are trying to find the xpath. Select the “Inspect” option.
- Right click on the highlighted area on the console. Go to Copy xpath
- Method 2: Using Chrome Extension to find xpath easily:
We can easily find xpath of an element using a Chrome extension like SelectorGadget.
Approach:
- Import Selenium and time library
- Set the Web Driver path with the location where you have downloaded the WebDriver
Example- “C:\chromedriver.exe” - Call driver.get() function to navigate to a particular URL.
- Call time.sleep() function to wait for the driver to completely load the webpage.
- Use driver.find_element_by_xpath() method to find the button using xpath.
- Finding button by text-
(i) Using normalize-space() method:
driver.find_element_by_xpath(‘//button[normalize-space()=”Click me!”]’)
(ii) Using text() method:
driver.find_element_by_xpath(‘//button’)
Note: It is recommended to use normalize-space() method because it trim the left and right side spaces. It is possible that there can be spaces present at the start or at the end of the target text. - Lastly close the driver using driver.close() function.
Implementation:
Python3
from
selenium
import
webdriver
import
time
driver
=
webdriver.Chrome(executable_path
=
r
"C:\chromedriver.exe"
)
time.sleep(
5
)
driver.find_element_by_xpath(
'//button[normalize-space()="Click me!"]'
).click()
time.sleep(
5
)
driver.close()
Output: