Wie kann ich den Text eines Elements in Selenium WebDriver abrufen, ohne den Text des untergeordneten Elements einzuschließen?

Lesezeit: 5 Minuten

Benutzeravatar von Josh
Josch

In Betracht ziehen:

<div id="a">This is some
   <div id="b">text</div>
</div>

“This is some” zu bekommen ist nicht trivial. Zum Beispiel gibt dies “This is some text” zurück:

driver.find_element_by_id('a').text

Wie erhält man im Allgemeinen den Text eines bestimmten Elements, ohne den Text seiner Kinder einzubeziehen?

  • Also fürs Protokoll, ich habe es letztendlich in Javascript gemacht … Ich habe jQuery auf den Seiten, die ich teste, also habe ich die Tatsache ausgenutzt, dass Selenium automatisch Dom-Elemente konvertiert, die von Javascript in WebElements zurückgegeben werden: my_result = driver .execute_script(‘zurück […call to my jquery function..]’)

    – Josch

    10. September 2012 um 19:03 Uhr

Benutzeravatar von Louis
Ludwig

Hier ist eine allgemeine Lösung:

def get_text_excluding_children(driver, element):
    return driver.execute_script("""
    return jQuery(arguments[0]).contents().filter(function() {
        return this.nodeType == Node.TEXT_NODE;
    }).text();
    """, element)

Das an die Funktion übergebene Element kann aus der abgerufen werden find_element...() Methoden (d.h. es kann a WebElement Objekt).

Oder wenn Sie jQuery nicht haben oder es nicht verwenden möchten, können Sie den Hauptteil der Funktion oben durch diesen ersetzen:

return self.driver.execute_script("""
var parent = arguments[0];
var child = parent.firstChild;
var ret = "";
while(child) {
    if (child.nodeType === Node.TEXT_NODE)
        ret += child.textContent;
    child = child.nextSibling;
}
return ret;
""", element)

Ich verwende diesen Code tatsächlich in einer Testsuite.

  • Richtig, was mir im Grunde klar wurde, ist … verwenden Sie nicht die Suchmethoden von Selenium, verwenden Sie einfach jquery

    – Josch

    26. September 2013 um 23:59 Uhr

  • @josh, dem würde ich nicht zustimmen … Seleniums-Methoden sollen Interaktionen aus dem POV eines Benutzers nachahmen, während jQuery dies nicht ist. Ja, Sie können beide verwenden, um Elemente zu erfassen, aber im Allgemeinen sollte es relativ wenige Situationen geben, in denen Sie Javascript ausführen müssen.

    – Wlingke

    16. Dezember 2013 um 15:46 Uhr


  • Das erste Code-Snippet geht davon aus, dass jQuery auf der Seite geladen wird. Das zweite Code-Snippet funktioniert unabhängig davon, ob jQuery geladen ist oder nicht.

    – Ludwig

    21. April 2016 um 12:40 Uhr

unentdeckter Benutzeravatar von Selenium
unentdecktes Selen

In dem von Ihnen geteilten HTML:

<div id="a">This is some
   <div id="b">text</div>
</div>

Der Text This is some ist innerhalb von a Textknoten. Zur Darstellung der Textknoten strukturiert:

<div id="a">
    This is some
   <div id="b">text</div>
</div>

Dieser Anwendungsfall

Zum Extrahieren und Drucken des Textes This is some von dem Textknoten Mit dem Python-Client von Selenium haben Sie zwei Möglichkeiten:

  • Verwenden splitlines(): Sie können das übergeordnete Element identifizieren, dh <div id="a">extrahieren Sie die innerHTML und dann verwenden splitlines() folgendermaßen:

  • verwenden XPath:

    print(driver.find_element_by_xpath("//div[@id='a']").get_attribute("innerHTML").splitlines()[0])
    
  • verwenden css_selector:

    print(driver.find_element_by_css_selector("div#a").get_attribute("innerHTML").splitlines()[0])
    
  • Verwenden execute_script(): Sie können auch die verwenden execute_script() Methode, die JavaScript im aktuellen Fenster/Frame wie folgt synchron ausführen kann:

  • verwenden XPath und erstes Kind:

    parent_element = driver.find_element_by_xpath("//div[@id='a']")
    print(driver.execute_script('return arguments[0].firstChild.textContent;', parent_element).strip())
    
  • verwenden XPath und untergeordnete Knoten[n]:

    parent_element = driver.find_element_by_xpath("//div[@id='a']")
    print(driver.execute_script('return arguments[0].childNodes[1].textContent;', parent_element).strip())
    

  • Auf das zweite „using xpath“ folgt „find_element_by_css_selector“.

    – Peter Mortensen

    15. November um 2:48 Uhr

Benutzeravatar von Josh
Josch

Verwenden:

def get_true_text(tag):
    children = tag.find_elements_by_xpath('*')
    original_text = tag.text
    for child in children:
        original_text = original_text.replace(child.text, '', 1)
    return original_text

  • das läuft aber ekelhaft langsam… das muss doch besser gehen??

    – Josch

    7. September 2012 um 21:39 Uhr

  • Sie sollten immer versuchen, ein möglichst spezifisches untergeordnetes Element zu erhalten. In diesem Fall wird es langsam laufen, wenn Sie viele untergeordnete Elemente haben. Warum prüfen Sie nicht, ob das Element tatsächlich Text enthält, bevor Sie zurückkehren, dh machen Sie den XPath: *[string-length(text()) > 1] oder machen Sie die for-Schleife auf prüfen child.text nicht null und nicht leer sein. Und was ist mit dem CSS-Selektor? XPath-Abfragen sind sowieso sehr langsam, daher ist ein CSS-Selektor möglicherweise schneller.

    – Arran

    7. September 2012 um 23:53 Uhr

Benutzeravatar von kreativitea
kreativitea

Du musst keinen Ersatz machen. Sie können die Länge des untergeordneten Textes ermitteln, diese von der Gesamtlänge subtrahieren und in den ursprünglichen Text schneiden. Das sollte wesentlich schneller gehen.

Benutzeravatar von Pikamander2
Pikamander2

Leider wurde Selenium nur dafür gebaut, damit zu arbeiten Elementenicht Text Knoten.

Wenn Sie versuchen, eine Funktion wie z get_element_by_xpath Um auf die Textknoten abzuzielen, wirft Selenium eine InvalidSelectorException.

Eine Problemumgehung besteht darin, den relevanten HTML-Code mit Selenium abzurufen und dann eine HTML-Parsing-Bibliothek wie z Schöne Suppe die Textknoten eleganter behandeln kann.

import bs4
from bs4 import BeautifulSoup

inner_html = driver.find_elements_by_css_selector('#a')[0].get_attribute("innerHTML")
inner_soup = BeautifulSoup(inner_html, 'html.parser')

outer_html = driver.find_elements_by_css_selector('#a')[0].get_attribute("outerHTML")
outer_soup = BeautifulSoup(outer_html, 'html.parser')

Von dort aus gibt es mehrere Möglichkeiten, nach dem Textinhalt zu suchen. Sie müssen experimentieren, um zu sehen, was für Ihren Anwendungsfall am besten funktioniert.

Hier ist ein einfacher Einzeiler, der ausreichen könnte:

inner_soup.find(text=True)

Wenn das nicht funktioniert, können Sie die untergeordneten Knoten des Elements mit .contents() durchlaufen und ihren Objekttyp überprüfen.

Schöne Suppe hat vier Arten von Elementenund derjenige, an dem Sie interessiert sein werden, ist der NavigierbarerString -Typ, der von Textknoten erzeugt wird. Im Gegensatz dazu haben Elemente einen Typ von Schild.

contents = inner_soup.contents

for bs4_object in contents:

    if (type(bs4_object) == bs4.Tag):
        print("This object is an Element.")

    elif (type(bs4_object) == bs4.NavigableString):
        print("This object is a Text node.")

Beachten Sie, dass Beautiful Soup keine XPath-Ausdrücke unterstützt. Wenn Sie diese benötigen, können Sie einige der Problemumgehungen in dieser Frage verwenden.

1436740cookie-checkWie kann ich den Text eines Elements in Selenium WebDriver abrufen, ohne den Text des untergeordneten Elements einzuschließen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy