¿Cómo encuentro un elemento que contiene texto específico en Selenium Webdriver (Python)?

260

Estoy tratando de probar una interfaz javascript complicada con Selenium (usando la interfaz Python y en varios navegadores). Tengo varios botones de la forma:

<div>My Button</div>

Me gustaría poder buscar botones basados ​​en "Mi botón" (o coincidencias parciales que no distinguen entre mayúsculas y minúsculas, como "mi botón" o "botón")

Me parece increíblemente difícil, en la medida en que siento que me falta algo obvio. Lo mejor que tengo hasta ahora es:

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

Sin embargo, esto distingue entre mayúsculas y minúsculas. La otra cosa que he intentado es iterar a través de todos los divs en la página y verificar la propiedad element.text. Sin embargo, cada vez que tenga una situación de la forma:

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

div.outer también tiene "Mi botón" como texto. Para solucionar ESO, intenté ver si div.outer es el padre de div.inner, pero no pude averiguar cómo hacerlo (element.get_element_by_xpath ('..') devuelve el elemento primario de un elemento, pero pruebas no iguales a div.outer). Además, la iteración a través de todos los elementos de la página parece ser muy lenta, al menos con el controlador web de Chrome.

Ideas?

Editar: Esta pregunta salió un poco vaga. Se le preguntó (y respondió) una versión más específica aquí: ¿Cómo obtener el texto de un elemento en Selenium WebDriver (a través de la API Python) sin incluir el texto del elemento hijo?

josh
fuente
Las respuestas actuales no me funcionaron. Este sí: sqa.stackexchange.com/a/2486
alejandro

Respuestas:

328

Intenta lo siguiente:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")
Ricky
fuente
3
Gracias por la respuesta, era el 50% de lo que necesitaba (me ayudó a comenzar). La forma a la que llegué es esta "(// * [contiene (texto (), '" + texto + "')] | // * [@ valor = '" + texto + "'])" buscará texto dado no solo dentro de los nodos del elemento, sino también dentro de los elementos de entrada cuyo texto se estableció mediante el atributo 'value', es decir, <button value = "My Button" />. Aunque tenga en cuenta, el valor debe ser una coincidencia estricta, no solo contener el texto.
Ivan Koshelev
9
También vale la pena mencionar para otros visitantes de los motores de búsqueda: si está buscando un enlace, existen find_element(s)_by_link_texty find_element(s)_by_partial_link_textmétodos
Dan Passaro
3
¿Qué pasa si el texto es dinámico? Es decir, puede contener comillas. ¿No rompería eso esta solución?
IcedDante
3
La búsqueda de ciertos nombres parece romper esto. Tome lo siguiente como ejemplo: "// * [contiene (texto (), '" + nombre de usuario + "')]" if username = "O'Reilly"; entonces el xpath se volvería inválido. ¿Hay alguna forma de evitar esto?
Sakamoto Kazuma
No parece funcionar cuando el texto de destino tiene varias líneas.
Shawn
29

podrías probar un xpath como:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)
andrean
fuente
Gracias ... así que eso ayuda a distinguir lo interno de lo externo, pero eso realmente funciona bien con xpath, solo estaba teniendo ese problema iterando a través de todos los divs. Mi problema con xpath es que no puedo entender cómo hacer que no distinga entre mayúsculas y minúsculas.
josh
2
xpath 2.0 tiene una función en minúsculas, por lo que debería funcionar: '// div [contiene (minúsculas (text ()), "{0}")]'. format (text)
andrean
¡Gracias! aunque, entiendo que xpath 2.0 no es compatible con los principales navegadores ...
josh
selenium evalúa las expresiones xpath directamente con los propios métodos del navegador, por lo que depende de qué navegador esté utilizando con selenium. generalmente solo es decir, 6,7 y 8 no deberían admitir xpath 2.0.
andrean
.formatno se reconoce en mi eclipse. da y error. alguna idea, ¿por qué?
anujin
16

También puede usarlo con el patrón de objeto de página, por ejemplo:

Prueba este código:

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

// * buscará cualquier etiqueta HTML. Donde si algún texto es común para Button y div tag y si // * son categorías, no funcionará como se esperaba. Si necesita seleccionar algún específico, puede obtenerlo declarando la etiqueta Elemento HTML. Me gusta:

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

Curiosamente, prácticamente todas las respuestas giran en torno a la función de xpath contains(), descuidando el hecho de que distingue entre mayúsculas y minúsculas , al contrario de lo que pregunta OP.
Si necesita insensibilidad a mayúsculas y minúsculas, eso se puede lograr en xpath 1.0 (la versión compatible con los navegadores modernos) , aunque no es bonito, mediante el uso de la translate()función. Sustituye un carácter fuente a su forma deseada, mediante el uso de una tabla de traducción.

La construcción de una tabla de todos los caracteres en mayúscula transformará efectivamente el texto del nodo a su forma más baja (), lo que permite la coincidencia entre mayúsculas y minúsculas (esta es solo la prerrogativa) :

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

La llamada completa a Python:

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

Naturalmente, este enfoque tiene sus inconvenientes: como se indica, funcionará solo para texto latino; Si desea cubrir caracteres Unicode, deberá agregarlos a la tabla de traducción. Lo hice en la muestra anterior: el último carácter es el símbolo cirílico "Й".


Y si viviéramos en un mundo en el que los navegadores admitían xpath 2.0 y versiones posteriores (🤞, pero que no sucedería pronto ☹️) , podríamos haber utilizado las funciones lower-case()(aún, no totalmente conscientes de la ubicación) y matches(para búsquedas de expresiones regulares, con mayúsculas y minúsculas). -insensible ( 'i') bandera).

Todor Minakov
fuente
4

En el HTML que ha proporcionado:

<div>My Button</div>

El texto My Buttones el innerHTMLy no tiene espacios en blanco a su alrededor para que pueda usarlo fácilmente de la text()siguiente manera:

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

Nota : text()selecciona todos los elementos secundarios del nodo de texto del nodo de contexto


Texto con espacios iniciales / finales

En caso de que el texto relevante contenga espacios en blanco al principio:

<div>   My Button</div>

o al final:

<div>My Button   </div>

o en ambos extremos:

<div> My Button </div>  

En estos casos tienes 2 opciones:

  • Puede usar la contains()función que determina si la primera cadena de argumento contiene la segunda cadena de argumento y devuelve boolean verdadero o falso de la siguiente manera:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • Puede usar la normalize-space()función que elimina los espacios en blanco iniciales y finales de una cadena, reemplaza las secuencias de caracteres de espacios en blanco por un solo espacio y devuelve la cadena resultante de la siguiente manera:

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

xpath para texto variable

En caso de que el texto sea una variable que puede usar:

foo= "foo_bar"
my_element = driver.find_element_by_xpath("//div[.='" + foo + "']")
DebanjanB
fuente
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
fuente
1

Problema similar: encontrar <button>Advanced...</button>

Tal vez esto le dará algunas ideas (transfiera el concepto de Java a Python):

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

Utilice driver.find_elements_by_xpath y hace coincidir la función de coincidencia de expresiones regulares para la búsqueda de mayúsculas y minúsculas del elemento por su texto.

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")
Andriy Ivaneyko
fuente
matches()es una función de xpath 2.0, y los navegadores lamentablemente solo admiten 1.0.
Todor Minakov
-19

Prueba esto. Es muy fácil:

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

Esto realmente funcionó para mí en el controlador web de selenio.

Amit
fuente
9
No funciona si el texto es generado por JavaScript.
palacsint
2
Esta es una forma muy efectiva de verificarlo, porque está transfiriendo todo el contenido de la página a través del cable. Para páginas muy pequeñas, esto es aceptable, pero para páginas muy grandes se transfiere todo el contenido del archivo y se verifica en el lado del servidor. Un mejor enfoque sería hacerlo en el lado del cliente con xpath, javascript o css.
thomas.han
¿Creo que toda la fuente de la página ya debería transferirse por cable para que el navegador la procese?
René
3
Josh pregunta cómo encontrar el elemento por texto, no para probar si el texto está presente en la fuente de la página.
Cedric
1
Para los casos en que todo lo que se necesita es encontrar un texto estático en una página, esta solución es lo suficientemente buena. (Ayudó en mi caso).
Karlth