Як знайти елемент із певним текстом у веб-версії Selenium (Python)?


259

Я намагаюся перевірити складний інтерфейс javascript із Selenium (за допомогою інтерфейсу Python та для багатьох браузерів). У мене є кілька кнопок форми:

<div>My Button</div>

Мені хочеться шукати кнопки на основі "Моєї кнопки" (або нечутливих до регістру, часткових збігів, таких як "моя кнопка" або "кнопка")

Мені це здається дивовижно важким, настільки, наскільки я відчуваю, що пропускаю щось очевидне. Найкраще, що у мене є:

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

Однак це враховує регістри. Інше, що я спробував - це повторити всі діви на сторінці та перевірити властивість 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 (через apt Python), не включаючи дочірнього елемента елемента?


Поточні відповіді для мене не спрацювали. Це зробив: sqa.stackexchange.com/a/2486
alejandro

Відповіді:


328

Спробуйте наступне:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")

3
Дякую за відповідь, це було на 50% від того, що мені потрібно (запустив мене). Форма, до якої я прийшов, це така "(// * [містить (текст (), '" + текст + "')] | // * [@ value = '" + текст + "'])" він шукатиме заданий текст не тільки всередині вузлів елементів, але і всередині елементів вводу, текст яких був встановлений за допомогою атрибута "value", тобто <кнопка значення = "Моя кнопка" />. Хоча зауважте, значення повинно суворо відповідати, а не містити текст.
Іван Кошелєв

9
Також варто згадати інших відвідувачів пошукової системи: якщо ви шукаєте посилання, існують find_element(s)_by_link_textі find_element(s)_by_partial_link_textметоди
Dan Passaro

3
Що робити, якщо текст динамічний? Тобто, може містити цитати. Хіба це не порушило б це рішення?
IcedDante

3
Пошук певних імен, здається, порушує це. Для прикладу візьміть наступне: "// * [містить (текст (), '" + ім'я користувача + "')]", якщо ім'я користувача = "O'Reilly"; тоді xpath стане недійсним. Чи є шлях до цього?
Сакамото Казума

Схоже, це не працює, якщо у тексті цілі є кілька рядків.
Шон

29

ви можете спробувати xpath на зразок:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)

Дякую ... так що це допомагає відрізнити внутрішнє від зовнішнього, але це справді добре працює з xpath, у мене була лише ця проблема, ітераційна через усі діви. Моя проблема з xpath полягає в тому, що я не можу зрозуміти, як зробити це нечутливим до регістру?
Джош

2
xpath 2.0 має малі регістри, тому це повинно працювати: '// div [містить (малий регістр (текст ()), "{0}")] "формат (текст)
andrean

Дякую! хоча я розумію, що xpath 2.0 не підтримується в основних браузерах ...
Джош

selenium оцінює вирази xpath безпосередньо власними методами браузера, тому залежить, який браузер ви використовуєте з селеном. зазвичай лише 6,7 та 8 не повинні підтримувати xpath 2.0.
Андрій

.formatне впізнається в моєму затемненні. це дає і помилку. будь-яка ідея, чому?
anujin

16

Ви також можете використовувати його з "Об'єктом сторінки", наприклад:

Спробуйте цей код:

@FindBy(xpath = "//*[contains(text(), 'Best Choice')]")
WebElement buttonBestChoice;

13

// * шукатиме будь-який тег HTML. Якщо, якщо якийсь текст є загальним для тегів Button та div, і якщо // * є категоріями, він не працюватиме, як очікувалося. Якщо вам потрібно вибрати якийсь конкретний, ви можете отримати це, оголосивши тег HTML Element. Подібно до:

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"

Повний дзвінок пітона:

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

Природно, у цього підходу є свої недоліки - як це дано, він працюватиме лише для латинського тексту; якщо ви хочете охопити символи unicode - вам доведеться додати їх до таблиці перекладу. Я зробив це на прикладі вище - останній символ є символом кирилиці "Й".


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


3

У наданому вами 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>  

У цих випадках у вас є два варіанти:

  • Ви можете використовувати 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 для змінного тексту

Текст тексту є змінною, яку ви можете використовувати:

foo= "foo_bar"
my_element = driver.find_element_by_xpath("//div[.='" + foo + "']")

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();

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");

Це справді працювало для мене у веб-драйвері з селеном.


9
Це не працює, якщо текст генерується JavaScript.
palacsint

2
Це дуже спосіб перевірити це, оскільки ви переносите весь вміст сторінки по дроту. Для дуже маленьких сторінок це прийнятно, але для дуже великих сторінок ви переносите весь вміст файлу і перевіряєте на стороні сервера. Кращим підходом було б це зробити на стороні клієнта за допомогою xpath, javascript або css.
thomas.han

Я б подумав, що все джерело сторінки вже потрібно буде перенести через дріт, щоб браузер його вивів?
Рене

3
Джош запитує, як знайти елемент за текстом, а не перевіряти, чи текст присутній у джерелі сторінки.
Седрік

1
У випадках, коли все необхідне - це знайти статичний текст на сторінці, це рішення є досить хорошим. (Це допомогло в моєму випадку).
Карлт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.