Блог LearnQA

XPath для Playwright, Selenium и Appium

Введение

Инженеры по автоматизации тестирования на Selenium, Playwright и Appium должны уверенно находить элементы на веб-страницах. XPath и CSS - самые мощные стратегии поиска элементов, используемые в Selenium/Playwright/Appium (далее – UI-тесты), по сравнению с другими стратегиями (например, id, name, className, linkText, partialLinkText и tagName).
Освоение XPath и/или CSS критически важно для инженеров по автоматизации на Selenium/Playwright/Appium, чтобы находить динамические веб-элементы, элементы без id или name, а также элементы с динамическими id и name. tagName - не самая полезная стратегия, потому что на странице обычно есть много элементов с одним и тем же тегом. linkText полезен только для тегов-ссылок <a>. Кроме того, этот подход неудобен, когда видимый текст меняется, например, в многоязычных системах.
Часто начинающие инженеры по автоматизации на Selenium или Playwright не уделяют достаточно внимания стратегиям поиска элементов. Подход «record & playback» плохо работает в приложениях с динамическими элементами. Из-за этого автотесты становятся хрупкими и начинают падать при автоматизации страниц с динамическим содержимым. Многие тестировщики полагаются на извлечение XPath через плагины браузера. Такие инструменты имеют ограничения и не всегда генерируют лучший XPath для динамических элементов.
Дальше мы подробно разберём XPath на примерах и рассмотрим несколько инструментов, которые помогают быстро генерировать XPath.
Если ты хочешь перейти на CSS, посмотри наш пост в блоге про CSS для Playwright и Selenium.

Термины, используемые в XPath

Давайте познакомимся с базовыми терминами, которые используются в синтаксисе XPath.
У нас есть:

  • Имя тега (Tag name)
  • Атрибут (Attribute)
  • Значение атрибута (Value)
  • Сам тег чаще всего делится на две части: открывающий тег (Opening) и закрывающий (End).
  • Внутри тега у нас находится текст тега (Inner text)
  • И все вместе это называется элемент (Element)

Использование XPath для поиска элементов в HTML

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

HTML устроен очень похоже: страница тоже состоит из вложенных тегов и имеет понятную «деревянную» структуру. Поэтому те же правила XPath отлично подходят и для HTML.

Отсюда простой вывод: XPath можно использовать, чтобы находить элементы на веб-странице, и именно поэтому в UI-автоматизации (Selenium / Playwright / Appium) XPath часто используется как один из основных видов локаторов.

Зачем нужно осваивать разные варианты синтаксиса XPath?

Мы не всегда можем найти элемент по id или name, потому что у некоторых элементов нет уникальных атрибутов (id или name). Некоторые атрибуты могут динамически меняться. Бывают элементы и вовсе без атрибутов. Поэтому такие элементы приходится искать иначе, чем статические.
XPath можно использовать для:
  • поиска элементов относительно известного элемента;
  • поиска элементов с частично статическими значениями атрибутов;
  • поиска элементов без атрибутов или без уникальных атрибутов;
  • двунаправленной навигации (вперёд и назад).
XPath - одна из самых гибких и сильных стратегий поиска элементов.

Типы XPath

Мы сгруппировали XPath на три типа.

Абсолютный XPath

Абсолютный XPath начинается от корня HTML-страницы.
В большинстве случаев не рекомендуется использовать абсолютные XPath по следующим причинам:
  • Абсолютные XPath длиннее, поэтому их сложнее читать.
  • Абсолютные XPath неустойчивы: они часто ломаются при небольших структурных изменениях на веб-странице.
Абсолютные XPath стоит использовать только тогда, когда относительный XPath построить невозможно (что крайне маловероятно). В UI-тестах не рекомендуется использовать абсолютный XPath.
Синтаксис: абсолютные XPath начинаются с /html.
Пример: /html/body/div[1]/div/div[2]/form/div[2]/input

Относительный XPath

Относительный XPath используется для поиска элементов относительно элемента с известным (надёжным) XPath. Иными словами, выбранный вами элемент задаётся относительно «опорного» элемента.
Синтаксис: относительные XPath начинаются с двух слэшей //.
Примеры:
//div[@id=’divUsername’]/input
//form/div[@id=’divUsername’]/input
//form/*/input
Между контекстным элементом (начальным элементом с известным XPath) и целевым элементом может находиться ноль или больше элементов.
Заметки:
  • Абсолютные XPath быстрее, чем относительные.
  • Используйте максимально короткие относительные XPath.

Точный XPath (Exact XPath)

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

Что учитывать при выборе XPath?

Важно учитывать следующее при выборе XPath из доступных вариантов.
Хороший локатор должен быть:
  • уникальным;
  • описательным;
  • устойчивым;
  • коротким.
Если вы хотите найти один конкретный элемент, XPath должен соответствовать только одному кандидату (то есть быть уникальным). Элемент легче идентифицировать, если локатор описательный и короткий (это важно для поддержки тестов). XPath, сгенерированные инструментами, часто не очень удобны для человека. Важно, чтобы по выбранному XPath элемент находился и при повторном запуске тестов в следующих релизах. XPath нужно выбирать так, чтобы он оставался валидным даже после изменений в DOM (то есть был устойчивым). Обычно у вас будет несколько вариантов XPath - выбирайте более короткий, чтобы он был читабельнее в тестовых скриптах.

Пример HTML-кода

Следующий HTML (DOM) будет использоваться для объяснения большинства синтаксиса XPath в этой статье.

Поиск элементов по известному атрибуту

Следующий синтаксис можно использовать для поиска элементов, когда значение хотя бы одного атрибута уникально и статично.
У нас есть три валидных XPath.
Примеры:
//*[@id=’txtUsername’]
//*[@name=’txtUsername’]
//*[@type=’text’]
Примечание: третий XPath не стоит использовать, хотя он и валидный. В большинстве случаев он не будет уникальным: элементов с type=’text’ может быть много. Поэтому UI-тест не сможет однозначно найти целевой элемент.

Как работают UI-тесты, если по XPath находится несколько элементов?

Поведение UI-тестов в ситуации, когда один и тот же XPath подходит сразу нескольким элементам, зависит от конкретного инструмента и его настроек.
Обычно есть варианты:
  • Методы, которые возвращают коллекцию (массив/список) всех подходящих элементов.
  • Методы и режимы, при которых фреймворк молча берёт первый найденный элемент (например, «первое совпадение»).
  • Строгие настройки, когда тест падает, если локатор оказался не уникальным и соответствует нескольким элементам (то есть возникает ошибка из-за неоднозначности).
Отдельно стоит помнить, что если по локатору не найдено ни одного элемента, поведение тоже зависит от инструмента, метода и настроек. В одних случаях вызов завершается ошибкой/исключением, а в других — вы просто получаете пустую коллекцию (пустой массив/список) без ошибок. Если в вашем сценарии элемент может отсутствовать, обычно используют «безопасный» подход: запрашивают коллекцию совпадений и затем проверяют, что она не пустая (и при необходимости — что совпадение ровно одно).

Поиск элементов по имени тега и атрибуту

Синтаксис:
//tagName[@attributeName=’value’]
Рассмотрим снова поиск поля ввода username.
Примеры:
//input[@id=’txtUsername’]
//input[@name=’txtUsername’]
Это один из самых часто используемых XPath. Большинство плагинов умеют автоматически генерировать подобные XPath.

Поиск элементов по статическому видимому тексту (точное совпадение)

Следующий синтаксис используется для поиска элементов по точному тексту между открывающим и закрывающим тегом (inner text).
Синтаксис:
//tagName[text()=’exact text’]
//*[text()=’exact text’]
Рассмотрим поиск следующей ссылки.
Примеры:
//a[text()=’Pragmatic’]
//*[text()=’Pragmatic’]
Примечание: inner text чувствителен к регистру.
Поиск элементов по видимому тексту обычно не рекомендуется:
  • если вы тестируете многоязычное приложение;
  • когда один и тот же текст встречается в нескольких местах.

Поиск элементов, когда часть видимого текста статична (частичное совпадение)

Синтаксис:
//tagName[contains(text(),’substring’)]
//tagName[contains(.,’substring’)]
//*[contains(text(),’substring’)]
Примеры:
//a[contains(text(),’Pragmatic’)]
//a[contains(., ‘Test Labs‘)]
//*[contains(text(), ‘Test Labs‘)]
Перед запуском тестов проверяйте XPath на валидность. Проверка XPath будет рассмотрена в отдельном разделе.

Поиск элементов, когда статичен префикс inner text

Вы можете находить элементы, если часть начала inner text остаётся неизменной.
Синтаксис:
//tagName[starts-with(text(),’Prefix of Inner Text’)]
//*[starts-with(text(),’Prefix of Inner Text’)]
Примеры:
//a[starts-with(text(),’Pragmatic’)]
//*[starts-with(text(), ‘Prag‘)]

Поиск input-элементов по видимому тексту

Поиск input-элементов по «видимому тексту» не стоит путать с поиском по inner text, как в разделах выше. Для элементов типа input «видимый текст» обычно задаётся значением атрибута, поэтому для поиска нужно использовать значение атрибута.
Синтаксис:
//tagName[@value=’visibleText’]
Пример:
//input[@value=’Janesh’]
Мы уже обсуждали подход поиска элементов по имени тега и атрибуту.

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

Иногда невозможно уникально найти элемент по одному атрибуту, потому что по нему подходит несколько кандидатов. В реальной жизни похожая ситуация: нельзя однозначно идентифицировать человека только по имени или только по фамилии. Чтобы не перепутать, приходится использовать комбинацию имени и фамилии.
Такая же техника применяется и в Selenium: когда по одному атрибуту находятся несколько элементов, используют два или больше атрибутов, чтобы сделать локатор уникальным.
Синтаксис:
//*[attribute1=’value1’][attribute2=’value2’]…[attributeN=’valueN’]
//tagName[attribute1=’value1’][attribute2=’value2’]…[attributeN=’valueN’]
//*[attribute1=’value1’ and attribute2=’value2]
//tagName[attribute1=’value1’ and attribute2=’value2]
Примеры:
//*[@type=’submit’][@value=’LOGIN’]
//input[@class=’button’][@type=’submit’][@value=’LOGIN’][@name=’Submit’]
//*[@type=’submit’ and @value=’LOGIN’]

Поиск элементов с динамическими значениями атрибутов

Следующий синтаксис можно использовать, когда часть значения атрибута не меняется. Тогда для поиска можно опираться на неизменяемую часть.
Синтаксис:
//elementName[contains(@attributeName,’substring of the value’)]
//*[contains(@attributeName,’substring of the value’)]
//elementName[starts-with(@attributeName,’fixed prefix of the value’)]
Примеры:
//a[contains(@href,’pragmatic’)]
//*[contains(@href,’testlabs’)]
//a[starts-with(@href,’pragmatic’)]

Поиск элементов относительно известного элемента

Если у целевого элемента нет уникальных атрибутов и/или статичного innerHTML, приходится искать его относительно элемента, у которого XPath не меняется.
В реальной жизни мы делаем так постоянно: объясняем дорогу до неизвестного места относительно хорошо известной точки (ориентира) - магазина, памятника, железнодорожной станции и т.д.
В XPath есть тринадцать осей (axes). В этой статье мы рассмотрим только часть наиболее полезных осей, которые можно применять в Playwright и Selenium.
Чтобы XPath получился короче, устойчивее и читабельнее, целевой элемент должен находиться как можно ближе к известному элементу (контекстному элементу).

Поиск родительского элемента (parent)

Ось parent возвращает родителя контекстного узла. У каждого контекстного элемента есть только один родитель (кроме корневого элемента html).
Синтаксис:
//<knownXpath>/parent::*
//<knownXpath>/parent::elementName
//<knownXpath>/..
Посмотрим, как найти элемент form относительно поля username. Нам нужно выбрать элемент с неизменяемым XPath. В данном случае возьмём поле username.
XPath известного элемента: //input[@id=’txtUsername’]
Примеры:
//input[@id=’txtUsername’]/parent::form
//input[@id=’txtUsername’]/parent::*
//input[@id=’txtUsername’]/..
У контекстного (известного) элемента может быть только один родитель, поэтому указывать имя тега необязательно. Но для читаемости полезно явно указывать имя элемента.

Поиск дочернего элемента (child)

Ось child возвращает дочерние элементы контекстного узла.
Синтаксис:
//<xpathOfContextElement>/child::<elementName> или
//<xpathOfContextElement>/child::*
//<xpathOfContextElement>/<elementName>
Примеры:
В следующих примерах XPath контекстного элемента: div[@id=’divUsername’]
//div[@id=’divUsername’]/child::input
//div[@id=’divUsername’]/input
На практике вместо child:: чаще используют обычный / от известного XPath.

Поиск «внуков» (grand children)

Синтаксис:
//<xpathOfContextElement>/*/<elementName>
//<xpathOfContextElement>/child/<elementName>
Примеры:
//form/*/input
//form/div/input

Поиск предков известного элемента (ancestor)

Ось ancestor возвращает предков известного элемента: родителя, родителя родителя и так далее.
Синтаксис:
//<xpathOfContextElement>/ancestor::<elementName>
//<xpathOfContextElement>/ancestor::*
Примеры:
//input[@id=’txtUsername’]/ancestor::form - выберет элемент form.
//input[@id=’txtUsername’]/ancestor::* - если использовать findElement, будет выбран div из доступных кандидатов (div, form и т.д.), потому что он встречается первым по пути.

Поиск потомков известного элемента (descendant)

Ось descendant возвращает потомков известного элемента: детей контекстного элемента, детей этих детей и так далее.
Синтаксис:
//<xpathOfContextElement>/descendant::<elementName>
//<xpathOfContextElement>/descendant::*
Примеры:
//form[@id=’frmLogin’]/descendant::input
//form[@id=’frmLogin’]//input
Вместо descendant:: можно использовать // для поиска потомков.

Поиск следующих элементов (following)

Ключевое слово following используется для поиска элемента(ов) в любом месте ниже по дереву относительно известного (контекстного) элемента.
Синтаксис:
//<xpathOfContextElement>/following::<elementName>
//<xpathOfConextElement>/following::*
Примеры:
//input[@id=’txtUsername’]/following::input
//input[@id=’txtUsername’]/following::*
Здесь есть два кандидата. Любые элементы-потомки после первого кандидата по пути исключаются Selenium или Playwright, если вы используете findElement.
Чтобы выбрать кнопку login (input) относительно поля username:
//input[@id=’txtUsername’]/following::input[last()]
//input[@id=’txtUsername’]/following::input[2]

Поиск предыдущего элемента (preceding)

Ключевое слово preceding используется для поиска элемента, который идёт до известного (XPath) элемента.
Ось preceding включает все узлы, которые находятся в том же дереве, идут перед контекстным узлом в порядке документа, при этом не являются предками контекстного узла.
Синтаксис:
//<xpathOfContextElement>/preceding::<elementName>
//<xpathOfContextElement>/preceding::*
Пример:
//span[text()=’Password’]/preceding::input
Будет два кандидата (username и password). Selenium выберет поле password, если используется findElement. Элементы упорядочены относительно контекстного элемента (span).
//span[text()=’Password’]/preceding::input[2]
Это можно использовать, чтобы выбрать поле username.

Поиск следующего «соседа» (following-sibling)

Ключевое слово following-sibling используется для поиска элемента(ов), которые идут после контекстного элемента на том же уровне иерархии. Следующие соседи - это дети одного и того же родителя, которые идут после контекстного элемента в порядке документа.
Синтаксис:
//<xpathOfContextElement>/following-sibling::<elementName>
//<xpathOfContextElement>/following-sibling::*
Примеры:
//*[@id=’txtUsername’]/following-sibling::span
//*[@id=’txtUsername’]/following-sibling::*

Поиск предыдущего «соседа» (preceding-sibling)

Ключевое слово preceding-sibling выбирает соседа(ей), которые идут до контекстного узла с известным XPath: это дети родителя контекстного узла, которые встречаются раньше в порядке документа.
Синтаксис:
//<xpathOfKnownElement>/preceding-sibling::<elementName>
//<xpathOfKnownElement>/preceding-sibling::*
Примеры:
//span[contains(text(),’Username’)]/preceding-sibling::input
//span[contains(text(),’Username’)]/preceding-sibling::*
На этом мы завершаем обсуждение осей XPath. Обратите внимание: в этой статье мы не рассматриваем оси attribute, ancestor-or-self, descendant-or-self, namespace и self, так как они не имеют практической пользы в контексте Selenium или Playwright.

Поиск по нескольким XPath

Можно искать по нескольким XPath, разделяя выражения символом |.
Синтаксис:
XPath1|Xpath2….|XPathN
Если первый XPath доступен, Selenium/Playwright/Appium выберет первый найденный элемент для дальнейших действий. Если доступны оба, то при использовании findElement будет выбран первый. При использовании findElements будут выбраны оба. Если доступен только второй XPath (Xpath2), будет выбран второй элемент. Если ни один не доступен, тот же Selenium или Appium выдаст ошибку NoSuchElementException, если используется findElement.
Пример:
//input[@id=’txtUsername’]|//input[@name=’txtPassword’]
В этом примере можно найти доступные элементы. Это полезно, когда вы знаете, что при загрузке страницы будет существовать один из вариантов.
//*[@id=’txtUsername’]|//*[@name=’txtPassword’]|*[@name=’btnLogin’]

Работа с операторами

Можно использовать разные операторы для сравнения числовых значений в атрибутах и inner text. Доступны сложение (+), вычитание (-), умножение (*), деление (div), равно (=), не равно (!=), меньше (<), меньше или равно (<=), больше (>), больше или равно (>=), and, or, mod.
Рассмотрим пример:
Допустим, нужно найти элемент(ы) с ценой по акции больше 100:
//span[contains(@class,’dealPriceText’) and text()>100]
Вы можете комбинировать изученные техники, чтобы строить сложные XPath и находить любой элемент в DOM.

Поиск элемента по позиции

Поиск по позиции можно использовать, когда по XPath находится много кандидатов, и нужно явно выбрать конкретный элемент.
Синтаксис:
Xpath[n] или Xpath[position()=n]
Xpath[position()>n] - position() можно комбинировать с операторами
Xpath[last()]
Xpath[last()-n]
Примеры:
XPath //a[text()=’0001′]/../following-sibling::td имеет пять кандидатов. Селекторы по порядку можно использовать, чтобы отфильтровать нужный элемент(ы).
//a[text()=’0001′]/../following-sibling::td[1]
//a[text()=’0001′]/../following-sibling::td[position()=1]
//a[text()=’0001′]/../following-sibling::td[last()-3]
//a[text()=’0001′]/../following-sibling::td[last()]
Примечание: для выбора первого элемента относительно контекстного элемента нужно использовать 1.

XPath или CSS

CSS и XPath - самые популярные, широко используемые и мощные стратегии поиска элементов в сообществе автоматизации тестирования на Selenium / Playwright / Appium. CSS получил более широкое распространение среди инженеров по автоматизации по следующим причинам (так обычно говорят сторонники CSS):
  • CSS более читабельный (проще).
  • CSS чуть быстрее
Сторонники XPath обычно подчёркивают его способность перемещаться по странице в любом направлении, в отличие от CSS. XPath может перемещаться вверх и вниз по DOM, тогда как CSS умеет перемещаться только вниз по DOM.
Некоторые исследования показывают, что существенной разницы в скорости нет. Освоить оба подхода - хорошая идея для инженера по автоматизации. При этом для проекта часто удобнее использовать одну стратегию поиска элементов по ряду причин.
Некоторые эксперты советуют гибридный подход: сначала использовать id и name, если они статичны, затем переходить на CSS. XPath применять только тогда, когда это действительно необходимо.
Спасибо.
2026-01-12 18:59