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?

josh
źródło
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')]")
Ricky
źródło
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)
andrean
źródło
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;
Krzysztof Walczewski
źródło
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')]")
Ishita Shah
źródło
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).

Todor Minakow
źródło
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 + "']")
DebanjanB
źródło
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"));
Mike Oganyan
źródło
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();
Reto Höhener
źródło
0

Zastosowanie driver.find_elements_by_xpath i mecze regex funkcji dopasowania do sprawy niewrażliwego wyszukiwania elementu przez jego tekstu.

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")
Andrij Ivaneyko
źródło
matches()jest funkcją xpath 2.0, a przeglądarki niestety obsługują tylko wersję 1.0.
Todor Minakov
-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.

Amit
źródło
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,