Comment trouver un élément contenant du texte spécifique dans Selenium Webdriver (Python)?

260

J'essaie de tester une interface javascript compliquée avec Selenium (en utilisant l'interface Python et sur plusieurs navigateurs). J'ai un certain nombre de boutons du formulaire:

<div>My Button</div>

J'aimerais pouvoir rechercher des boutons en fonction de "Mon bouton" (ou des correspondances partielles non sensibles à la casse telles que "mon bouton" ou "bouton")

Je trouve cela incroyablement difficile, dans la mesure où j'ai l'impression de manquer quelque chose d'évident. La meilleure chose que j'ai jusqu'à présent est:

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

Ceci est cependant sensible à la casse. L'autre chose que j'ai essayée est de parcourir toutes les divs de la page et de vérifier la propriété element.text. Cependant, chaque fois que vous obtenez une situation du formulaire:

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

div.outer a également "My Button" comme texte. Pour corriger cela, j'ai essayé de voir si div.outer est le parent de div.inner, mais je n'ai pas pu comprendre comment le faire (element.get_element_by_xpath ('..') renvoie le parent d'un élément, mais il tests différents de div.outer). De plus, parcourir tous les éléments de la page semble être très lent, du moins en utilisant le pilote Web Chrome.

Des idées?

Edit: Cette question est sortie un peu vague. A demandé (et répondu) une version plus spécifique ici: Comment obtenir le texte d'un élément dans Selenium WebDriver (via l'API Python) sans inclure le texte de l'élément enfant?

Josh
la source
Les réponses actuelles n'ont pas fonctionné pour moi. Celui-ci l'a fait: sqa.stackexchange.com/a/2486
alejandro

Réponses:

328

Essayez ce qui suit:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")
Ricky
la source
3
Merci pour la réponse, c'était 50% de ce dont j'avais besoin (j'ai commencé). La forme à laquelle je suis arrivé est la suivante: "(// * [contient (text (), '" + text + "')] | // * [@ value = '" + text + "'])" qu'il recherchera du texte donné non seulement à l'intérieur des nœuds d'élément, mais également à l'intérieur des éléments d'entrée dont le texte a été défini via l'attribut 'value', c'est-à-dire <button value = "My Button" />. Bien que notez, la valeur doit être une correspondance stricte, pas seulement contenir le texte.
Ivan Koshelev
9
Il convient également de mentionner pour les autres visiteurs des moteurs de recherche: si vous recherchez un lien, il existe find_element(s)_by_link_textet des find_element(s)_by_partial_link_textméthodes
Dan Passaro
3
Et si le texte est dynamique? Autrement dit, peut contenir des citations. Cela ne briserait-il pas cette solution?
IcedDante
3
La recherche de certains noms semble casser cela. Prenons l'exemple suivant: "// * [contient (text (), '" + username + "')]" if username = "O'Reilly"; alors le xpath deviendrait invalide. Y a-t-il un moyen de contourner ceci?
Sakamoto Kazuma
Cela ne semble pas fonctionner lorsque le texte cible a plusieurs lignes.
Shawn
29

vous pouvez essayer un xpath comme:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)
andréen
la source
Merci ... donc cela aide à distinguer l'intérieur de l'extérieur, mais cela fonctionne très bien avec xpath, je n'avais que ce problème à répéter à travers toutes les divs. Mon problème avec xpath est que je ne peux pas comprendre comment le rendre insensible à la casse?
josh
2
xpath 2.0 a une fonction en minuscules, donc cela devrait fonctionner: '// div [contient (minuscules (texte ()), "{0}")]'. format (texte)
andrean
Merci! bien que je comprenne que xpath 2.0 n'est pas pris en charge par les principaux navigateurs ...
josh
selenium évalue les expressions xpath directement avec les propres méthodes du navigateur, cela dépend donc du navigateur que vous utilisez avec sélénium. généralement, c'est-à-dire que 6,7 et 8 ne devraient pas prendre en charge xpath 2.0.
andrean
.formatn'est pas reconnu dans mon éclipse. il donne et erreur. une idée, pourquoi?
anujin
16

Vous pouvez également l'utiliser avec le modèle d'objet de page, par exemple:

Essayez ce code:

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

// * recherchera n'importe quelle balise HTML. Où si du texte est commun pour Button et la balise div et si // * est des catégories, cela ne fonctionnera pas comme prévu. Si vous devez sélectionner un élément spécifique, vous pouvez l'obtenir en déclarant la balise HTML Element. Comme:

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

Fait intéressant, pratiquement toutes les réponses tournent autour de la fonction de xpath contains(), négligeant le fait qu'il soit sensible à la casse - contrairement à la demande de OP.
Si vous avez besoin d'une insensibilité à la casse, cela est réalisable dans xpath 1.0 (la version supportée par les navigateurs contemporains) , bien que ce ne soit pas joli - en utilisant la translate()fonction. Il substitue un caractère source à sa forme souhaitée, en utilisant une table de traduction.

La construction d'un tableau de tous les caractères majuscules transformera efficacement le texte du nœud dans sa forme inférieure () - permettant une correspondance insensible à la casse (voici juste la prérogative) :

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

L'appel complet de python:

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

Naturellement, cette approche a ses inconvénients - comme indiqué, cela ne fonctionnera que pour le texte latin; si vous souhaitez couvrir les caractères unicode, vous devrez les ajouter à la table de traduction. Je l'ai fait dans l'exemple ci-dessus - le dernier caractère est le symbole cyrillique "Й".


Et si nous vivions dans un monde où les navigateurs prenaient en charge xpath 2.0 et versions ultérieures (not, mais ne se produiront pas de si tôt ☹️) , nous aurions pu utiliser les fonctions lower-case()(pour l'instant, pas totalement adaptées aux paramètres régionaux), et matches(pour les recherches d'expression régulière, avec cas -indicateur insensible ( 'i')).

Todor Minakov
la source
4

Dans le code HTML que vous avez fourni:

<div>My Button</div>

Le texte My Buttonest le innerHTMLet n'a aucun espace autour de lui afin que vous puissiez facilement l'utiliser text()comme suit:

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

Remarque : text()sélectionne tous les enfants de nœuds de texte du nœud de contexte


Texte avec espaces de début / fin

Insérez le texte correspondant contenant des espaces blancs au début:

<div>   My Button</div>

ou à la fin:

<div>My Button   </div>

ou aux deux extrémités:

<div> My Button </div>  

Dans ces cas, vous avez 2 options:

  • Vous pouvez utiliser une contains()fonction qui détermine si la première chaîne d'arguments contient la deuxième chaîne d'arguments et renvoie la valeur booléenne true ou false comme suit:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • Vous pouvez utiliser une normalize-space()fonction qui supprime les espaces blancs de début et de fin d'une chaîne, remplace les séquences de caractères d'espaces par un seul espace et renvoie la chaîne résultante comme suit:

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

xpath pour le texte variable

Dans le cas où le texte est une variable que vous pouvez utiliser:

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

Problème similaire: Rechercher <button>Advanced...</button>

Peut-être que cela vous donnera quelques idées (veuillez transférer le concept de Java vers Python):

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

Utilisez driver.find_elements_by_xpath et correspondances à l' expression rationnelle fonction correspondant pour le cas de recherche insensible de l'élément par son texte.

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")
Andriy Ivaneyko
la source
matches()est une fonction xpath 2.0, et les navigateurs ne prennent malheureusement en charge que 1.0.
Todor Minakov
-19

Essaye ça. C'est très facile:

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

Cela a vraiment fonctionné pour moi dans le pilote Web sélénium.

Amit
la source
9
Cela ne fonctionne pas si le texte est généré par JavaScript.
palacsint
2
C'est une façon très simple de le vérifier, car vous transférez tout le contenu de la page sur le fil. Pour les très petites pages, cela est acceptable, mais pour les très grandes pages, vous transférez tout le contenu du fichier et vérifiez côté serveur. Une meilleure approche serait de le faire côté client avec xpath, javascript ou css.
thomas.han
Je pense que la source de la page entière devrait déjà être transférée sur le fil pour que le navigateur la rende?
René
3
Josh demande comment trouver l'élément par texte, pas pour tester si le texte est présent dans la source de la page.
Cédric
1
Pour les cas où tout ce qui est nécessaire est de trouver un texte statique sur une page, cette solution est assez bonne. (Cela a aidé dans mon cas).
Karlth