Jak znaleźć element zawierający określony tekst w Selenium Webdriver (Python)?


259

Próbuję przetestować skomplikowany interfejs javascript z Selenium (przy użyciu interfejsu Python i wielu przeglądarek). Mam kilka przycisków formularza:

<div>My Button</div>

Chciałbym móc wyszukiwać przyciski na podstawie „Mojego przycisku” (lub nie uwzględniających wielkości liter, częściowych dopasowań, takich jak „mój przycisk” lub „przycisk”)

Uważam to za niezwykle trudne do tego stopnia, że ​​wydaje mi się, że brakuje mi czegoś oczywistego. Najlepsze, co do tej pory mam, to:

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

Uwzględnia to jednak wielkość liter. Inną rzeczą, której próbowałem, jest iterowanie przez wszystkie elementy div na stronie i sprawdzanie właściwości element.text. Jednak za każdym razem, gdy pojawia się sytuacja w formularzu:

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

div.outer ma również „My Button” jako tekst. Aby to naprawić, próbowałem sprawdzić, czy div.outer jest rodzicem div.inner, ale nie mogłem dowiedzieć się, jak to zrobić (element.get_element_by_xpath ('..') zwraca element nadrzędny elementu, ale testy nie są równe div.outer). Również iteracja po wszystkich elementach strony wydaje się być bardzo powolna, przynajmniej przy użyciu webdrivera Chrome.

Pomysły?

Edycja: To pytanie było trochę niejasne. Zapytano (i odpowiedział) o bardziej szczegółową wersję tutaj: Jak uzyskać tekst elementu w Selenium WebDriver (za pośrednictwem interfejsu API Python) bez dołączania tekstu elementu potomnego?


Obecne odpowiedzi nie działały dla mnie. Ten zrobił: sqa.stackexchange.com/a/2486
— alejandro

Odpowiedzi:


328

Spróbuj wykonać następujące czynności:

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

3
Dziękuję za odpowiedź, to było 50% tego, czego potrzebowałem (zacząłem). Forma, do której przybyłem, to ta „(// * [zawiera (tekst (),„ ”+ tekst +„ ”)] | // * [@ wartość = '” + tekst + „”]) „będzie szukał podany tekst nie tylko wewnątrz węzłów elementów, ale także wewnątrz elementów wejściowych, których tekst ustawiono za pomocą atrybutu „wartość”, tj. <wartość przycisku = „Mój przycisk” />. Pamiętaj jednak, że wartość musi być ściśle dopasowana, a nie tylko zawierać tekst.
— Ivan Koshelev,

9
Warto również wspomnieć o innych odwiedzających wyszukiwarkę: jeśli szukasz linku, istnieją find_element(s)_by_link_texti find_element(s)_by_partial_link_textmetody
— Dan Passaro,

3
Co jeśli tekst jest dynamiczny? Oznacza to, że może zawierać cytaty. Czy to nie złamałoby tego rozwiązania?
— IcedDante

3
Wyszukiwanie niektórych nazwisk wydaje się to zaburzać. Weźmy na przykład: „// * [zawiera (text (),” „+ nazwa użytkownika +„ ”)]„ jeśli nazwa użytkownika = „O'Reilly”; wtedy xpath straci ważność. Czy jest na to jakiś sposób?
— Sakamoto Kazuma,

Nie działa, gdy tekst docelowy ma wiele wierszy.
— Shawn,

29

możesz wypróbować xpath:

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

Dzięki ... więc to pomaga odróżnić wewnętrzny od zewnętrznego, ale to faktycznie działa dobrze z xpath, miałem problem tylko z iteracją przez wszystkie divy. Mój problem z xpath polega na tym, że nie mogę się dowiedzieć, jak sprawić, by nie uwzględniała wielkości liter?
— josh

2
xpath 2.0 ma funkcję małych liter, więc powinno to działać: „// div [zawiera (małe litery (tekst ()),„ {0} ”)]. format (tekst)
— andrean

dzięki! chociaż rozumiem, że xpath 2.0 nie jest obsługiwany w głównych przeglądarkach ...
— josh

selenium ocenia wyrażenia xpath bezpośrednio własnymi metodami przeglądarki, więc zależy to od używanej przeglądarki z selenem. generalnie tylko 6,7 i 8 nie powinny obsługiwać xpath 2.0.
— andrean

.formatnie jest rozpoznawany w moim zaćmieniu. daje i błąd. jakiś pomysł, dlaczego?
— anujin

16

Możesz go również używać z Wzorem Obiektów Strony, np .:

Wypróbuj ten kod:

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

13

// * będzie szukał dowolnego tagu HTML. Gdzie, jeśli jakiś tekst jest wspólny dla przycisku i znacznika div, a jeśli // * to kategorie, nie będzie działać zgodnie z oczekiwaniami. Jeśli musisz wybrać jakiś konkretny, możesz go uzyskać, deklarując znacznik HTML Element. Lubić:

driver.find_element_by_xpath("//div[contains(text(),'Add User')]")
driver.find_element_by_xpath("//button[contains(text(),'Add User')]")

5

Co ciekawe, praktycznie wszystkie odpowiedzi dotyczą funkcji xpath contains(), pomijając fakt, że rozróżnia duże i małe litery - w przeciwieństwie do zapytania OP.
Jeśli potrzebujesz rozróżniać małe i wielkie litery, jest to możliwe w Xpath 1.0 (wersja obsługująca współczesne przeglądarki) , choć nie jest to ładne - za pomocą tej translate()funkcji. Zastępuje znak źródłowy pożądaną formą za pomocą tabeli translacji.

Konstruowanie tabeli zawierającej wszystkie wielkie litery skutecznie przekształci tekst węzła w jego niższą () formę - umożliwiając dopasowanie bez rozróżniania wielkości liter (oto tylko przywilej) :

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

Pełne wywołanie Pythona:

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

Oczywiście takie podejście ma swoje wady - jak podano, będzie działać tylko w przypadku tekstu łacińskiego; jeśli chcesz zakryć znaki Unicode - musisz dodać je do tabeli tłumaczeń. Zrobiłem to w powyższym przykładzie - ostatnim znakiem jest cyrylica "Й".


A gdybyśmy żyli w świecie, w którym przeglądarki obsługiwały xpath 2.0 i nowsze wersje (🤞, ale nie dzieje się to w najbliższym czasie ☹️) , moglibyśmy korzystać z funkcji lower-case()(jeszcze nie w pełni świadomych ustawień regionalnych) i matches(do wyszukiwania wyrażeń regularnych, z uwzględnieniem wielkości liter -insensitive ( 'i') flag).


3

W dostarczonym pliku HTML:

<div>My Button</div>

Tekst My Buttonjest innerHTMLi nie ma wokół niego żadnych białych znaków, dzięki czemu można łatwo używać text()w następujący sposób:

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

Uwaga : text()zaznacza wszystkie potomne węzły tekstowe węzła kontekstu


Tekst ze spacjami wiodącymi / końcowymi

Dodaj odpowiedni tekst zawierający białe spacje na początku:

<div>   My Button</div>

lub na końcu:

<div>My Button   </div>

lub na obu końcach:

<div> My Button </div>  

W takich przypadkach masz 2 opcje:

  • Możesz użyć contains()funkcji, która określa, czy pierwszy ciąg argumentu zawiera drugi ciąg argumentu i zwraca wartość logiczną true lub false w następujący sposób:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • Możesz użyć normalize-space()funkcji, która usuwa początkowe i końcowe białe znaki z ciągu, zastępuje ciąg znaków białych znaków pojedynczą spacją i zwraca wynikowy ciąg w następujący sposób:

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

xpath dla zmiennej Text

Incase tekst jest zmienną, której możesz użyć:

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

Podobny problem: Znajdź <button>Advanced...</button>

Może da ci to kilka pomysłów (proszę przenieść koncepcję z Javy do Pythona):

wait.until(ExpectedConditions.elementToBeClickable(//
    driver.findElements(By.tagName("button")).stream().filter(i -> i.getText().equals("Advanced...")).findFirst().get())).click();


-19

Spróbuj tego. To jest bardzo łatwe:

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

To naprawdę działało dla mnie w sterowniku sieci selenium.


9
Nie działa, jeśli tekst jest generowany przez JavaScript.
— palacsint

2
Jest to bardzo sposób na sprawdzenie tego, ponieważ przenosisz całą zawartość strony za pomocą drutu. W przypadku bardzo małych stron jest to akceptowalny problem, ale w przypadku bardzo dużych stron przesyłana jest cała zawartość pliku i sprawdzana po stronie serwera. Lepszym rozwiązaniem byłoby zrobienie tego po stronie klienta za pomocą xpath, javascript lub css.
— thomas.han

Myślałbym, że całe źródło strony musiałoby już być przesyłane przewodowo, aby przeglądarka je renderowała?
— René

3
Josh pyta, jak znaleźć element po tekście, aby nie sprawdzać, czy tekst jest obecny w źródle strony.
— Cedric

1
W przypadkach, gdy wszystko jest potrzebne do znalezienia statycznego tekstu na stronie, to rozwiązanie jest wystarczająco dobre. (Pomogło w moim przypadku).
— Karlth,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.