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.