如何在 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()
:您可以识别父元素即<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,如下:
使用 xpath 和 firstChild:
parent_element = driver.find_element_by_xpath("//div[@id='a']")
print(driver.execute_script('return arguments[0].firstChild.textContent;', parent_element).strip())
使用 xpath 和 childNodes[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()) > 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中设置标头