如何在 Selenium WebDriver 中获取元素的文本,而不包括子元素文本?

Posted

技术标签:

【中文标题】如何在 Selenium WebDriver 中获取元素的文本,而不包括子元素文本?【英文标题】:How to get text of an element in Selenium WebDriver, without including child element text? 【发布时间】:2012-09-01 18:30:23 【问题描述】:
<div id="a">This is some
   <div id="b">text</div>
</div>

获得“这是一些”并非易事。例如,这会返回“This is some text”:

driver.find_element_by_id('a').text

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

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

【问题讨论】:

所以为了记录,我最终做的是在 javascript 中做它......我在我正在测试的页面上有 jQuery,所以我利用了 Selenium 自动转换返回的 dom 元素这一事实从 javascript 到 WebElements:my_result = driver.execute_script('return [...call to my jquery function..]') 【参考方案1】:

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

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) 

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

【讨论】:

对,我基本意识到的是……不要用selenium的搜索方式,用jquery就行了 @josh,我不同意这一点...... Seleniums 方法旨在模拟来自用户 POV 的交互,而 jQuery 不是。是的,您可以同时使用两者来抓取元素,但一般而言,您需要执行 javascript 的情况应该相对较少。 第一个代码 sn-p 假定 jQuery 已加载到页面中。无论是否加载 jQuery,第二个代码 sn-p 都有效。【参考方案2】:

在您共享的 html 中:

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

文本This is some 在text node 内。以结构化的方式描述text node:

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

这个用例

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

使用splitlines():您可以识别父元素即&lt;div id="a"&gt;,提取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())

【讨论】:

【参考方案3】:
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

【讨论】:

这运行速度慢得令人作呕,不过……必须有更好的方法?? 您应该始终尝试获取最具体的子元素。在这种情况下,如果您有很多子元素,它将运行缓慢。为什么不在返回之前检查元素是否真的有文本,即使 XPath:*[string-length(text()) &gt; 1] 或使 for 循环检查 child.text 不为空且不为空。另外,CSS选择器呢? XPath 查询无论如何都非常慢,所以也许 CSS 选择器会更快。【参考方案4】:

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

【讨论】:

【参考方案5】:

不幸的是,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 有four types of elements,你会感兴趣的是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 表达式。如果您需要这些,那么您可以使用一些解决方法in this thread。

【讨论】:

以上是关于如何在 Selenium WebDriver 中获取元素的文本,而不包括子元素文本?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 在 Selenium WebDriver (Selenium 2) 中最大化浏览器窗口?

如何使用selenium webdriver来判断一个网页加载完毕

如何在 ruby​​ 中使用 Selenium WebDriver (selenium 2.0) 客户端设置选项

如何在python selenium chrome webdriver中设置标头

如何在 Java 中使用 Selenium WebDriver (Selenium 2) 输入文本框?

如何在 selenium-webdriver 中为 phantomjs 驱动程序设置一个用户代理?