Come trovo un elemento che contiene testo specifico in Selenium Webdriver (Python)?


259

Sto provando a testare un'interfaccia javascript complicata con Selenium (utilizzando l'interfaccia Python e su più browser). Ho un numero di pulsanti del modulo:

<div>My Button</div>

Mi piacerebbe poter cercare pulsanti basati su "Il mio pulsante" (o corrispondenze parziali senza distinzione tra maiuscole e minuscole come "il mio pulsante" o "pulsante")

Lo trovo incredibilmente difficile, nella misura in cui mi sembra di perdere qualcosa di ovvio. La cosa migliore che ho finora è:

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

Questo è sensibile al maiuscolo / minuscolo, tuttavia. L'altra cosa che ho provato è scorrere tutte le div sulla pagina e controllare la proprietà element.text. Tuttavia, ogni volta che si verifica una situazione del modulo:

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

div.outer ha anche "My Button" come testo. Per risolvere il problema, ho provato a vedere se div.outer è il genitore di div.inner, ma non sono riuscito a capire come farlo (element.get_element_by_xpath ('..') restituisce il genitore di un elemento, ma esso test non uguali a div.outer). Inoltre, iterare tutti gli elementi della pagina sembra essere molto lento, almeno usando il webdriver di Chrome.

Idee?

Modifica: questa domanda è emersa un po 'vaga. Chiesto (e risposto) una versione più specifica qui: Come ottenere il testo di un elemento in Selenium WebDriver (tramite l'API Python) senza includere il testo dell'elemento figlio?


Le risposte attuali non hanno funzionato per me. Questo ha fatto: sqa.stackexchange.com/a/2486
— alejandro,

Risposte:


328

Prova quanto segue:

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

3
Grazie per la risposta, era il 50% di ciò di cui avevo bisogno (mi ha fatto iniziare). Il modulo a cui sono arrivato è questo "(// * [contiene (text (), '" + text + "')] | // * [@ value = '" + text + "'])" cercherà dato testo non solo all'interno dei nodi degli elementi, ma anche all'interno degli elementi di input il cui testo è stato impostato tramite l'attributo 'valore', ovvero <button value = "My Button" />. Sebbene si noti, il valore deve essere una corrispondenza rigorosa, non solo contenere il testo.
— Ivan Koshelev,

9
Vale anche la pena menzionare per gli altri visitatori dei motori di ricerca: se stai cercando un link, ci sono find_element(s)_by_link_texte find_element(s)_by_partial_link_textmetodi
— Dan Passaro,

3
Cosa succede se il testo è dinamico? Cioè, potrebbe contenere virgolette. Ciò non spezzerebbe questa soluzione?
— IcedDante,

3
La ricerca di determinati nomi sembra interrompere questo. Prendi un esempio per quanto segue: "// * [contiene (text (), '" + username + "')]" se username = "O'Reilly"; quindi xpath diventerebbe non valido. C'è un modo per aggirare questo?
— Sakamoto Kazuma,

Non sembra funzionare quando il testo di destinazione ha più righe.
— Shawn,

29

potresti provare un xpath come:

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

Grazie ... in modo da aiutare a distinguere l'interno dall'esterno, ma in realtà funziona benissimo con xpath, avevo solo quel problema che scorreva attraverso tutti i div. Il mio problema con xpath è che non riesco a capire come renderlo senza distinzione tra maiuscole e minuscole?
— Josh,

2
xpath 2.0 ha una funzione minuscola, quindi dovrebbe funzionare: '// div [contiene (lettere minuscole (testo ()), "{0}")]'. format (testo)
— andrean

Grazie! tuttavia, la mia comprensione è che xpath 2.0 non è supportato nei principali browser ...
— josh

selenium valuta le espressioni xpath direttamente con i metodi propri del browser, quindi dipende dal browser che stai utilizzando con selenium. generalmente solo 6,7 e 8 non dovrebbero supportare xpath 2.0.
— andrean,

.formatnon è riconosciuto nella mia eclissi. dà ed errore. qualche idea, perché?
— Anujin,

16

Puoi anche usarlo con Pattern oggetto pagina, ad es .:

Prova questo codice:

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

13

// * cercherà qualsiasi tag HTML. Se un po 'di testo è comune per Button e tag div e se // * è categorie, non funzionerà come previsto. Se devi selezionare uno specifico, puoi ottenerlo dichiarando il tag HTML Element. Piace:

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

5

È interessante notare che praticamente tutte le risposte ruotano attorno alla funzione di xpath contains(), trascurando il fatto che è case sensitive - contrariamente alla richiesta di OP.
Se hai bisogno di distinzione tra maiuscole e minuscole, ciò è ottenibile in xpath 1.0 (la versione supportata dai browser contemporanei) , anche se non è carina - usando iltranslate() funzione. Sostituisce un carattere sorgente nella sua forma desiderata, usando una tabella di traduzione.

La costruzione di una tabella con tutti i caratteri maiuscoli trasformerà efficacemente il testo del nodo nella sua forma inferiore (), consentendo una corrispondenza senza distinzione tra maiuscole e minuscole (ecco solo la prerogativa) :

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

La chiamata full python:

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

Naturalmente questo approccio ha i suoi svantaggi: come indicato, funzionerà solo per il testo latino; se vuoi coprire i caratteri unicode, dovrai aggiungerli alla tabella di traduzione. L'ho fatto nell'esempio sopra - l'ultimo personaggio è il simbolo cirillico "Й".


E se vivessimo in un mondo in cui i browser supportano xpath 2.0 e versioni successive (🤞, ma non si verificano in qualsiasi momento presto ☹️) , avremmo potuto usare le funzioni lower-case()(ancora, non completamente a conoscenza delle impostazioni locali) e matches(per le ricerche regex, con case -insensitive ( 'i') flag).


3

Nel codice HTML che hai fornito:

<div>My Button</div>

Il testo My Buttonè il innerHTMLe non ha spazi bianchi attorno ad esso, quindi puoi facilmente usarlo text()come segue:

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

Nota : text()seleziona tutti i figli del nodo di testo del nodo di contesto


Testo con spazi iniziali / finali

Incassa il testo pertinente contenente spazi bianchi all'inizio:

<div>   My Button</div>

o alla fine:

<div>My Button   </div>

o ad entrambe le estremità:

<div> My Button </div>  

In questi casi hai 2 opzioni:

  • È possibile utilizzare la contains()funzione che determina se la prima stringa di argomenti contiene la seconda stringa di argomenti e restituisce vero o falso booleano come segue:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • È possibile utilizzare la normalize-space()funzione che rimuove gli spazi bianchi iniziali e finali da una stringa, sostituisce le sequenze di caratteri di spazi bianchi con un singolo spazio e restituisce la stringa risultante come segue:

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

xpath per testo variabile

Incase il testo è una variabile che puoi usare:

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

Problema simile: Trova <button>Advanced...</button>

Forse questo ti darà alcune idee (per favore, trasferisci il concetto da Java a Python):

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


-19

Prova questo. È molto facile:

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

Questo ha funzionato davvero per me nel web driver del selenio.


9
Non funziona se il testo è generato da JavaScript.
— palacsint,

2
Questo è un modo molto semplice per controllarlo, perché stai trasferendo l'intero contenuto della pagina tramite il filo. Per pagine molto piccole questo è accettabile ma per pagine molto grandi si trasferisce tutto il contenuto del file e si controlla sul lato server. Un approccio migliore sarebbe quello di farlo sul lato client con xpath, javascript o css.
— Thomas.

Penserei che l'intera fonte della pagina avrebbe già bisogno di essere trasferita via cavo per essere visualizzata dal browser?
— René,

3
Josh sta chiedendo come trovare l'elemento tramite il testo, non per verificare se il testo è presente nella fonte della pagina.
— Cedric,

1
Per i casi in cui tutto ciò che serve è trovare un testo statico su una pagina, questa soluzione è abbastanza buona. (Mi ha aiutato nel mio caso).
— Karlth
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.