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

获得“这是一些”并非易事。例如,这会返回“这是一些文本”:

driver.find_element_by_id('a').text

一般而言,如何获取特定元素的文本而不包括其子元素的文本?

(我在下面提供了一个答案,但如果有人能想出一个不那么可怕的解决方案,我会留下这个问题)。

4

5 回答 5

29

这是一个通用的解决方案:

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)

传递给函数的元素可以是从find_element...()方法中获得的东西(即它可以是一个WebElement对象)。

或者,如果您没有 jQuery 或不想使用它,您可以将上述函数的主体替换为:

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) 

我实际上在测试套件中使用此代码。

于 2013-09-26T23:26:51.443 回答
7

在您共享的 HTML 中:

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

文本This is some文本节点内。以结构化的方式描述文本节点

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

这个用例

要使用SeleniumThis is some客户端从文本节点中提取和打印文本,您有以下两种方法:

  • 使用splitlines():您可以识别父元素即<div id="a">,提取innerHTML然后使用splitlines()如下:

    • 使用xpath

      print(driver.find_element_by_xpath("//div[@id='a']").get_attribute("innerHTML").splitlines()[0])
      
    • 使用xpath

      print(driver.find_element_by_css_selector("div#a").get_attribute("innerHTML").splitlines()[0])
      
  • 使用execute_script():你也可以使用execute_script()可以在当前窗口/框架中同步执行JavaScript的方法,如下:

    • 使用xpathfirstChild

      parent_element = driver.find_element_by_xpath("//div[@id='a']")
      print(driver.execute_script('return arguments[0].firstChild.textContent;', parent_element).strip())
      
    • 使用xpathchildNodes[n]

      parent_element = driver.find_element_by_xpath("//div[@id='a']")
      print(driver.execute_script('return arguments[0].childNodes[1].textContent;', parent_element).strip())
      
于 2020-01-11T19:57:40.563 回答
4
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
于 2012-09-07T21:11:59.563 回答
3

您不必进行替换,您可以获取子文本的长度并将其从总长度中减去,然后切片为原始文本。那应该快得多。

于 2012-09-12T23:56:56.217 回答
2

不幸的是,Selenium 仅适用于Elements,而不适用于Text节点。

如果您尝试使用诸如get_element_by_xpath定位文本节点之类的函数,Selenium 将抛出一个InvalidSelectorException.

一种解决方法是使用 Selenium 获取相关的 HTML,然后使用可以更优雅地处理文本节点的 BeautifulSoup 之类的 HTML 解析库。

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')

从那里,有几种方法可以搜索文本内容。您必须进行试验以查看最适合您的用例的方法。

这是一个简单的单行,可能就足够了:

inner_soup.find(text=True)

如果这不起作用,那么您可以使用 .contents() 遍历元素的子节点并检查它们的对象类型。

BeautifulSoup 有四种类型的元素,您会感兴趣的一种是NavigableString类型,它由 Text 节点生成。相比之下, Elements 将具有Tag类型。

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.")

请注意,BeautifulSoup 不支持 Xpath 表达式。如果您需要这些,那么您可以使用此线程中的一些解决方法。

于 2020-04-15T04:52:58.733 回答