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.