Wie finde ich ein Element, das bestimmten Text in Selenium Webdriver (Python) enthält?

259

Ich versuche, eine komplizierte Javascript-Oberfläche mit Selenium zu testen (über die Python-Oberfläche und über mehrere Browser hinweg). Ich habe eine Reihe von Schaltflächen des Formulars:

<div>My Button</div>

Ich möchte in der Lage sein, nach Schaltflächen zu suchen, die auf "Meine Schaltfläche" basieren (oder nach nicht übereinstimmenden Teilübereinstimmungen wie "Meine Schaltfläche" oder "Schaltfläche").

Ich finde das erstaunlich schwierig, in dem Maße, in dem ich das Gefühl habe, etwas Offensichtliches zu vermissen. Das Beste, was ich bisher habe, ist:

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

Dies unterscheidet jedoch zwischen Groß- und Kleinschreibung. Das andere, was ich versucht habe, ist, alle Divs auf der Seite zu durchlaufen und die Eigenschaft element.text zu überprüfen. Jedes Mal, wenn Sie eine Situation des Formulars erhalten:

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

div.outer hat auch "My Button" als Text. Um DAS zu beheben, habe ich versucht zu prüfen, ob div.outer das übergeordnete Element von div.inner ist, konnte jedoch nicht herausfinden, wie dies zu tun ist (element.get_element_by_xpath ('..') gibt das übergeordnete Element eines Elements zurück, aber es Tests ungleich div.outer). Außerdem scheint das Durchlaufen aller Elemente auf der Seite sehr langsam zu sein, zumindest mit dem Chrome-Webdriver.

Ideen?

Edit: Diese Frage kam etwas vage heraus. Hier wurde eine spezifischere Version gefragt (und beantwortet): Wie kann man Text eines Elements in Selenium WebDriver (über die Python-API) abrufen, ohne untergeordneten Elementtext einzuschließen?

Josh
quelle
Die aktuellen Antworten haben bei mir nicht funktioniert. Dieser tat: sqa.stackexchange.com/a/2486
alejandro

Antworten:

328

Versuche Folgendes:

driver.find_elements_by_xpath("//*[contains(text(), 'My Button')]")
Ricky
quelle
3
Vielen Dank für die Antwort, es waren 50% von dem, was ich brauchte (ich habe angefangen). Das Formular, zu dem ich gekommen bin, ist das "(// * [enthält (text (), '" + text + "')] | // * [@ value = '" + text + "'])", nach dem gesucht wird gegebener Text nicht nur innerhalb von Elementknoten, sondern auch innerhalb von Eingabeelementen, deren Text über das Attribut 'value' festgelegt wurde, dh <button value = "My Button" />. Beachten Sie jedoch, dass der Wert streng übereinstimmen muss und nicht nur den Text enthalten muss.
Ivan Koshelev
9
Auch für andere Suchmaschinenbesucher erwähnenswert: Wenn Sie nach einem Link suchen, gibt es find_element(s)_by_link_textund find_element(s)_by_partial_link_textMethoden
Dan Passaro
3
Was ist, wenn der Text dynamisch ist? Das heißt, könnte Anführungszeichen enthalten. Würde das diese Lösung nicht brechen?
IcedDante
3
Die Suche nach bestimmten Namen scheint dies zu brechen. Nehmen Sie als Beispiel Folgendes: "// * [enthält (text (), '" + Benutzername + "')]" if username = "O'Reilly"; dann würde der xpath ungültig werden. Gibt es einen Weg, dies zu umgehen?
Sakamoto Kazuma
Es scheint nicht zu funktionieren, wenn der Zieltext mehrere Zeilen enthält.
Shawn
29

Sie könnten einen xpath versuchen wie:

'//div[contains(text(), "{0}") and @class="inner"]'.format(text)
andrean
quelle
Danke ... das hilft also, das Innere vom Äußeren zu unterscheiden, aber das funktioniert tatsächlich gut mit xpath. Ich hatte nur das Problem, alle Divs zu durchlaufen. Mein Problem mit xpath ist, dass ich nicht herausfinden kann, wie ich die Groß- und Kleinschreibung nicht berücksichtigen kann.
Josh
2
xpath 2.0 hat eine Kleinbuchstabenfunktion, daher sollte dies funktionieren: '// div [enthält (Kleinbuchstaben (text ()), "{0}")]'. format (text)
andrean
Vielen Dank! Mein Verständnis ist jedoch, dass xpath 2.0 in den wichtigsten Browsern nicht unterstützt wird ...
Josh
Selenium bewertet xpath-Ausdrücke direkt mit den eigenen Methoden des Browsers. Es hängt also davon ab, welchen Browser Sie mit Selenium verwenden. Im Allgemeinen sollten nur 6,7 und 8 xpath 2.0 nicht unterstützen.
andrean
.formatwird in meiner Sonnenfinsternis nicht erkannt. es gibt und Fehler. Irgendeine Idee, warum?
Anujin
16

Sie können es auch mit dem Seitenobjektmuster verwenden, z.

Versuchen Sie diesen Code:

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

// * sucht nach einem HTML-Tag. Wenn für Button- und Div-Tags Text gemeinsam ist und // * Kategorien sind, funktioniert dies nicht wie erwartet. Wenn Sie ein bestimmtes auswählen müssen, können Sie es durch Deklarieren des HTML-Element-Tags erhalten. Mögen:

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

Interessanterweise drehen sich praktisch alle Antworten um die Funktion von xpath und contains()vernachlässigen die Tatsache, dass zwischen Groß- und Kleinschreibung unterschieden wird - im Gegensatz zu OPs Fragen.
Wenn Sie die Groß- und Kleinschreibung nicht berücksichtigen müssen, ist dies in xpath 1.0 (der Version, die moderne Browser unterstützen) möglich , obwohl es nicht schön ist - mithilfe der translate()Funktion. Mithilfe einer Übersetzungstabelle wird ein Quellzeichen durch die gewünschte Form ersetzt.

Durch das Erstellen einer Tabelle mit allen Großbuchstaben wird der Text des Knotens effektiv in seine Kleinbuchstabenform () umgewandelt, sodass eine Übereinstimmung ohne Berücksichtigung der Groß- und Kleinschreibung möglich ist (hier nur das Vorrecht) :

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

Der vollständige Python-Aufruf:

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

Natürlich hat dieser Ansatz seine Nachteile - wie angegeben, funktioniert er nur für lateinischen Text. Wenn Sie Unicode-Zeichen behandeln möchten, müssen Sie sie der Übersetzungstabelle hinzufügen. Ich habe das im obigen Beispiel gemacht - das letzte Zeichen ist das kyrillische Symbol "Й".


Und wenn wir in einer Welt gelebt hätten, in der Browser xpath 2.0 und höher unterstützen (🤞, aber nicht in Kürze ☹️) , hätten wir die Funktionen lower-case()(noch nicht vollständig länderspezifisch) und matches(für Regex-Suchen mit Groß- und Kleinschreibung) verwenden können -insensitive ( 'i') flag).

Todor Minakov
quelle
3

In dem von Ihnen bereitgestellten HTML:

<div>My Button</div>

Der Text My Buttonist der innerHTMLund hat keine Leerzeichen, sodass Sie ihn text()wie folgt verwenden können:

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

Hinweis : text()Wählt alle untergeordneten Textknoten des Kontextknotens aus


Text mit führenden / nachfolgenden Leerzeichen

Fügen Sie den relevanten Text mit Leerzeichen entweder am Anfang ein:

<div>   My Button</div>

oder am Ende:

<div>My Button   </div>

oder an beiden Enden:

<div> My Button </div>  

In diesen Fällen haben Sie zwei Möglichkeiten:

  • Sie können eine contains()Funktion verwenden, die bestimmt, ob die erste Argumentzeichenfolge die zweite Argumentzeichenfolge enthält und wie folgt den booleschen Wert true oder false zurückgibt:

    my_element = driver.find_element_by_xpath("//div[contains(., 'My Button')]")
  • Sie können eine normalize-space()Funktion verwenden, mit der führende und nachfolgende Leerzeichen von einer Zeichenfolge entfernt, Sequenzen von Leerzeichen durch ein einzelnes Leerzeichen ersetzt und die resultierende Zeichenfolge wie folgt zurückgegeben werden:

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

xpath für die Variable Text

Wenn der Text eine Variable ist, die Sie verwenden können:

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

Ähnliches Problem: Finden <button>Advanced...</button>

Vielleicht gibt Ihnen dies einige Ideen (bitte übertragen Sie das Konzept von Java auf Python):

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

Verwenden driver.find_elements_by_xpath und Streichhölzer REGEX für die passende Funktion Groß- und Kleinschreibung Suche des Elements durch den Text.

driver.find_elements_by_xpath("//*[matches(.,'My Button', 'i')]")
Andriy Ivaneyko
quelle
matches()ist eine xpath 2.0-Funktion, und die Browser unterstützen leider nur 1.0.
Todor Minakov
-19

Versuche dies. Es ist sehr leicht:

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

Das hat bei mir im Selen-Webtreiber wirklich funktioniert.

Amit
quelle
9
Es funktioniert nicht, wenn der Text von JavaScript generiert wird.
Palacsint
2
Dies ist eine sehr gute Möglichkeit, dies zu überprüfen, da Sie den gesamten Inhalt der Seite über das Kabel übertragen. Für sehr kleine Seiten ist dies akzeptabel, aber für sehr große Seiten übertragen Sie den gesamten Inhalt der Datei und überprüfen auf der Serverseite. Ein besserer Ansatz wäre es, dies auf der Clientseite mit xpath, javascript oder css zu tun.
Thomas.han
Ich würde denken, dass die gesamte Seitenquelle bereits über das Kabel übertragen werden muss, damit der Browser sie rendern kann.
René
3
Josh fragt, wie man das Element anhand von Text findet und nicht, ob der Text in der Quelle der Seite vorhanden ist.
Cedric
1
In Fällen, in denen lediglich ein statischer Text auf einer Seite gefunden werden muss, ist diese Lösung ausreichend. (Es hat in meinem Fall geholfen).
Karlth