В Selenium WebDriver, поиск элементов по видимому тексту — важная задача, особенно когда у элементов нет уникальных идентификаторов или классов. В этой статье мы рассмотрим различные способы реализации поиска элементов на веб-странице, используя текст, который видит пользователь. Мы обсудим синтаксис XPath, возможности CSS-селекторов, API Selenium, а также лучшие практики и распространенные ошибки.
Почему поиск по тексту важен?
Поиск по тексту становится необходимым в ситуациях, когда:
- Отсутствуют уникальные атрибуты: Элементы не имеют
id,nameили других полезных атрибутов. - Динамический контент: Содержимое элемента меняется, но текст остается консистентным.
- Человеко-ориентированное тестирование: Сценарии тестирования основаны на пользовательском восприятии.
Ограничения и недостатки стандартных методов поиска
Стандартные методы поиска, такие как find_element_by_id или find_element_by_class_name, не всегда применимы, когда идентификаторы динамически генерируются или отсутствуют. В таких случаях, поиск по тексту предлагает более гибкий и надежный подход. Однако, следует учитывать, что поиск по тексту может быть медленнее и менее устойчивым, чем поиск по атрибутам.
Использование XPath для поиска элемента по видимому тексту
XPath предоставляет мощные инструменты для поиска элементов на основе текста. Это наиболее распространенный и гибкий способ.
Базовый синтаксис XPath для поиска по тексту: contains(), text()
Основные функции XPath для поиска по тексту:
text(): Возвращает текстовое содержимое элемента.contains(string1, string2): Проверяет, содержит лиstring1подстрокуstring2.
Примеры XPath запросов с использованием видимого текста
- Поиск элемента, содержащего определенный текст:
xpath
//a[contains(text(), 'Подробнее')]
Этот XPath найдет все элементы<a>, содержащие текст «Подробнее». - Поиск элемента с точным соответствием текста:
xpath
//h1[text()='Заголовок страницы']
Этот XPath найдет элемент<h1>с текстом «Заголовок страницы». - Поиск элемента, содержащего текст и имеющего определенный атрибут:
xpath
//button[contains(text(), 'Купить') and @class='primary']
Этот XPath найдет все элементы<button>, содержащие текст «Купить» и имеющие класс «primary».
Поиск точного соответствия текста vs. частичное соответствие
Использовать text()='exact text' для точного соответствия и contains(text(), 'partial text') для частичного. Точное соответствие чувствительно к пробелам и регистру.
Работа с пробелами и регистром в XPath запросах
- Удаление лишних пробелов:
xpath
//div[normalize-space(text())='Текст без пробелов']
Функцияnormalize-space()удаляет начальные и конечные пробелы, а также заменяет последовательности пробелов одним пробелом. - Игнорирование регистра:
xpath
//a[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'пример')]
Функцияtranslate()преобразует все символы верхнего регистра в нижний, что позволяет выполнить поиск без учета регистра.
Использование CSS-селекторов (с ограничениями) для поиска по тексту
Ограничения CSS-селекторов в поиске по тексту
CSS-селекторы не поддерживают прямой поиск по тексту, как XPath. Это их существенное ограничение.
Комбинирование CSS-селекторов с XPath для более точного поиска
Можно сначала найти родительский элемент с помощью CSS-селектора, а затем внутри него использовать XPath для поиска элемента по тексту. Этот метод может повысить производительность в некоторых случаях.
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://example.com")
# Находим родительский элемент с помощью CSS-селектора
parent_element = driver.find_element(By.CSS_SELECTOR, "div.container")
# Внутри родительского элемента ищем элемент по тексту с помощью XPath
element = parent_element.find_element(By.XPATH, ".//a[contains(text(), 'Ссылка')]")
element.click()
driver.quit()
Поиск элемента по тексту с использованием Selenium API и пользовательских функций
Написание пользовательской функции для фильтрации элементов по тексту
Можно написать функцию, которая получает список элементов и фильтрует их по заданному тексту.
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from typing import List
def find_element_by_text(driver: WebDriver, tag_name: str, text: str) -> WebElement | None:
"""Находит первый элемент с указанным текстом."""
elements: List[WebElement] = driver.find_elements(By.TAG_NAME, tag_name)
for element in elements:
if element.text == text:
return element
return None
# Пример использования
# element = find_element_by_text(driver, "h1", "Заголовок страницы")
# if element:
# print(element.text)
Использование find_elements для получения списка элементов и последующей фильтрации
Получаем список элементов и фильтруем его в цикле или с использованием list comprehension.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from typing import List
driver = webdriver.Chrome()
driver.get("https://example.com")
# Получаем все элементы <a>
links: List[WebElement] = driver.find_elements(By.TAG_NAME, "a")
# Фильтруем список, чтобы найти элементы, содержащие определенный текст
filtered_links: List[WebElement] = [link for link in links if "Подробнее" in link.text]
# Выводим текст найденных ссылок
for link in filtered_links:
print(link.text)
driver.quit()
Примеры реализации поиска с использованием Python и Java Selenium API
Python:
from selenium import webdriver
from selenium.webdriver.common.by import By
from typing import List
driver = webdriver.Chrome()
driver.get("https://example.com")
def find_element_by_visible_text(driver: webdriver, text: str, tag: str = '*') -> List[webdriver.remote.webelement.WebElement]:
xpath = f'//{tag}[contains(text(), "{text}")]'
return driver.find_elements(By.XPATH, xpath)
elements = find_element_by_visible_text(driver, "Some Text", "span")
for element in elements:
print(element.text)
driver.quit()
Java:
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.List;
public class FindElementByText {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
driver.get("https://example.com");
List<WebElement> elements = driver.findElements(By.xpath("//*[contains(text(), 'Some Text')] "));
for (WebElement element : elements) {
System.out.println(element.getText());
}
driver.quit();
}
}
Лучшие практики и распространенные ошибки при поиске элементов по тексту
Оптимизация XPath запросов для повышения производительности
- Используйте более конкретные XPath запросы. Избегайте
//*[contains(text(), '...') ]. - Начинайте поиск с более близкого родительского элемента, найденного по
idилиclass. - По возможности, избегайте использования
normalize-space()иtranslate(), так как они могут замедлить поиск.
Обработка динамически изменяющегося текста
Если текст элемента динамически меняется, используйте более общие условия поиска или регулярные выражения (если возможно). Также можно использовать ожидание (explicit wait) для уверенности, что текст загрузился.
Работа с локализацией и различными языками
Текст может отличаться в зависимости от языка. Используйте ресурсы локализации или параметры конфигурации, чтобы адаптировать тесты к разным языкам. В XPath можно использовать translate() для приведения текста к одному регистру.
Обход распространенных ошибок (No such element exception, Stale element reference exception)
- NoSuchElementException: Элемент не найден. Проверьте правильность XPath и убедитесь, что элемент присутствует на странице в момент поиска. Используйте
try-exceptблоки для обработки исключений. - StaleElementReferenceException: Элемент устарел. Элемент был найден, но DOM обновился после этого. Повторите поиск элемента.
Поиск элементов по видимому тексту – мощный инструмент в арсенале тестировщика Selenium. Правильное использование XPath, Selenium API и соблюдение лучших практик позволит эффективно находить элементы и создавать надежные и стабильные автоматизированные тесты.