Как мне найти элемент, который содержит определенный текст в Selenium Webdriver (Python)?

260

Я пытаюсь протестировать сложный интерфейс javascript с Selenium (используя интерфейс Python и через несколько браузеров). У меня есть несколько кнопок формы:

<div>My Button</div>

Я хотел бы иметь возможность поиска кнопок на основе «Моя кнопка» (или не учитывающих регистр, частичных совпадений, таких как «моя кнопка» или «кнопка»)

Я нахожу это удивительно трудным, до такой степени, что я чувствую, что упускаю что-то очевидное. Лучшее, что у меня есть, это:

driver.find_elements_by_xpath('//div[contains(text(), "' + text + '")]')

Это чувствительно к регистру, однако. Другая вещь, которую я пробовал, - это перебирать все элементы div на странице и проверять свойство element.text. Тем не менее, каждый раз, когда вы получаете ситуацию в форме:

<div class="outer"><div class="inner">My Button</div></div>

У div.outer также есть «Моя кнопка» в качестве текста. Чтобы исправить это, я попытался выяснить, является ли div.outer родителем div.inner, но не смог понять, как это сделать (element.get_element_by_xpath ('..') возвращает родителя элемента, но это тесты не равны div.outer). Кроме того, итерация по всем элементам на странице кажется очень медленной, по крайней мере с использованием веб-драйвера Chrome.

Идеи?

Изменить: этот вопрос вышел немного расплывчатым. Спросил (и ответил) более конкретную версию здесь: Как получить текст элемента в Selenium WebDriver (через API Python) без включения текста дочернего элемента?

мистифицировать
источник
Текущие ответы не работают для меня. Это один сделал: sqa.stackexchange.com/a/2486
Alejandro

Ответы:

328

Попробуйте следующее:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")
Рики
источник
3
Спасибо за ответ, это было 50% того, что мне было нужно (я начал). Форма, к которой я прибыл, это "(// * [содержит (text (), '" + text + "')] | // * [@ value = '" + text + "'])", он будет искать заданный текст не только внутри узловых элементов, но и внутри входных элементов, текст которых был установлен с помощью атрибута 'value', т.е. <button value = "My Button" />. Обратите внимание, что значение должно строго соответствовать, а не просто содержать текст.
Иван Кошелев,
9
Также стоит упомянуть для других посетителей поисковой системы: если вы ищете ссылку, есть find_element(s)_by_link_textи find_element(s)_by_partial_link_textметоды
Дан Пассаро
3
Что если текст динамический? То есть может содержать кавычки. Не нарушит ли это решение?
IcedDante
3
Поиск определенных имен, кажется, сломал это. Для примера возьмем следующее: "// * [содержит (text (), '" + username + "')]" if username = "O'Reilly"; тогда xpath станет недействительным. Есть ли способ обойти это?
Сакамото Казума
Кажется, это не работает, когда целевой текст имеет несколько строк.
Шон
29

Вы можете попробовать xpath как:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)
Andrean
источник
Спасибо ... так что это помогает отличить внутреннее от внешнего, но на самом деле это прекрасно работает с xpath, у меня была только эта проблема, перебирающая все элементы div. Моя проблема с xpath в том, что я не могу понять, как сделать его без учета регистра?
Джош
2
В xpath 2.0 есть функция нижнего регистра, поэтому она должна работать: '// div [содержит (нижний регистр (text ()), "{0}")]'. format (text)
andrean
Спасибо! хотя, насколько я понимаю, xpath 2.0 не поддерживается во всех основных браузерах ...
Джош
selenium оценивает выражения xpath напрямую с помощью собственных методов браузера, поэтому зависит, какой браузер вы используете с selenium. как правило, только 6,7 и 8 не должны поддерживать xpath 2.0.
Андреан
.formatне узнается в моем затмении это дает и ошибка. любая идея, почему?
Anujin
16

Вы также можете использовать его с шаблоном объекта страницы, например:

Попробуйте этот код:

@FindBy(xpath = "//*[contains(text(), 'Best Choice')]")
WebElement buttonBestChoice;
Кшиштоф Вальчевский
источник
13

// * будет искать любой тег HTML. Где, если какой-то текст является общим для кнопки и тега div, а если // * - категории, он не будет работать должным образом. Если вам нужно выбрать какой-то конкретный объект, вы можете получить его, объявив тег HTML-элемента. Подобно:

driver.find_element_by_xpath("//div[contains(text(),'Add User')]")
driver.find_element_by_xpath("//button[contains(text(),'Add User')]")
Ишита Шах
источник
5

Интересно, что практически все ответы вращаются вокруг функции xpath contains(), игнорируя тот факт, что она чувствительна к регистру - вопреки запросу OP.
Если вам нужна нечувствительность к регистру, это достижимо в xpath 1.0 (версия, поддерживаемая современными браузерами) , хотя это и не красиво - с помощью translate()функции. Он заменяет исходный символ в его желаемой форме, используя таблицу перевода.

Построение таблицы всех символов в верхнем регистре будет эффективно преобразовывать текст узла в его нижнюю () форму - позволяя сопоставление без учета регистра (вот только прерогатива) :

[
  contains(
    translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),
    'my button'
  )
]
# will match a source text like "mY bUTTon"

Полный вызов Python:

driver.find_elements_by_xpath("//*[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZЙ', 'abcdefghijklmnopqrstuvwxyzй'), 'my button')]")

Естественно, у этого подхода есть свои недостатки - как дано, он будет работать только для латинского текста; если вы хотите охватить символы юникода - вам придется добавить их в таблицу перевода. Я сделал это в примере выше - последний символ - это символ кириллицы "Й".


И если бы мы жили в мире, где браузеры поддерживали xpath 2.0 и выше (🤞, но скоро это не произойдет ☹️) , мы могли бы использовать функции lower-case()(пока не полностью ориентированные на локали), и matches(для поиска регулярных выражений, с case -insensitive ( 'i') флаг).

Тодор Минаков
источник
4

В HTML, который вы предоставили:

<div>My Button</div>

Текст My Buttonявляется innerHTMLи не имеет пробелов вокруг него, так что вы можете легко использовать text()следующее:

my_element = driver.find_element_by_xpath("//div[text()='My Button']")

Примечание : text()выбирает все дочерние текстовые узлы узла контекста


Текст с начальными / конечными пробелами

Включите соответствующий текст, содержащий пробелы в начале:

<div>   My Button</div>

или в конце:

<div>My Button   </div>

или на обоих концах:

<div> My Button </div>  

В этих случаях у вас есть 2 варианта:

  • Вы можете использовать contains()функцию, которая определяет, содержит ли строка первого аргумента строку второго аргумента, и возвращает логическое значение true или false следующим образом:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • Вы можете использовать normalize-space()функцию, которая удаляет начальные и конечные пробелы из строки, заменяет последовательности пробельных символов одним пробелом и возвращает полученную строку следующим образом:

    driver.find_element_by_xpath("//div[normalize-space()='My Button']]")

xpath для переменной Text

Incase the text - это переменная, которую вы можете использовать:

foo= "foo_bar"
my_element = driver.find_element_by_xpath("//div[.='" + foo + "']")
DebanjanB
источник
1
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(text(), 'YourTextHere')]")));
    assertNotNull(driver.findElement(By.xpath("//*[contains(text(), 'YourTextHere')]")));
    String yourButtonName=driver.findElement(By.xpath("//*[contains(text(), 'YourTextHere')]")).getAttribute("innerText");
    assertTrue(yourButtonName.equalsIgnoreCase("YourTextHere"));
Майк Оганян
источник
1

Аналогичная проблема: Найти <button>Advanced...</button>

Возможно, это даст вам некоторые идеи (пожалуйста, перенесите концепцию с Java на Python):

wait.until(ExpectedConditions.elementToBeClickable(//
    driver.findElements(By.tagName("button")).stream().filter(i -> i.getText().equals("Advanced...")).findFirst().get())).click();
Reto Höhener
источник
0

Используйте driver.find_elements_by_xpath и спички Regex соответствия функции для случая нечувствительного поиска элемента по его тексту.

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")
Андрей Иванейко
источник
matches()является функцией xpath 2.0, и браузеры, к сожалению, поддерживают только 1.0.
Тодор Минаков
-19

Попробуй это. Это очень просто:

driver.getPageSource().contains("text to search");

Это действительно работает для меня в веб-драйвере селена.

Amit
источник
9
Это не работает, если текст генерируется JavaScript.
Palacsint
2
Это очень хороший способ проверить это, потому что вы передаете все содержимое страницы по проводам. Для очень маленьких страниц это приемлемо, но для очень больших страниц вы переносите все содержимое файла и проверяете на стороне сервера. Лучшим подходом было бы сделать это на стороне клиента с помощью xpath, javascript или css.
thomas.han
Я думаю, что весь исходный код страницы уже должен быть передан по проводам, чтобы браузер отображал его?
Рене
3
Джош спрашивает, как найти элемент по тексту, чтобы не проверять, присутствует ли текст в источнике страницы.
Седрик
1
Для случаев, когда нужно найти статический текст на странице, это решение достаточно хорошо. (Это помогло в моем случае).
Карлт