我对以下内容有些(或非常)困惑:
from selenium.webdriver import Chrome
driver = Chrome()
html_content = """
<html>
<head></head>
<body>
<div class='first'>
Text 1
</div>
<div class="second">
Text 2
<span class='third'> Text 3
</span>
</div>
<div class='first'>
Text 4
</div>
<my_tag class="second">
Text 5
<span class='third'> Text 6
</span>
</my_tag>
</body>
</html>
"""
driver.get("data:text/html;charset=utf-8,{html_content}".format(html_content=html_content))
我想做的是使用xpath找到每个span
元素,打印出其文本,然后打印出该元素的父级文本。最终输出应类似于:
Text 3
Text 2
Text 6
Text 5
我可以这样获得span
的文本:
el = driver.find_elements_by_xpath("*//span")
for i in el:
print(i.text)
输出为:
Text 3
Text 6
但是当我尝试使用以下方法获取父级(和仅父级)的文本时:
elp = driver.find_elements_by_xpath("*//span/..")
for i in elp:
print(i.text)
输出为:
Text 2 Text 3
Text 5 Text 6
xpath表达式*//span/..
和//span/../text()
通常(但并非总是如此,取决于所使用的xpath测试站点)的计算结果为:
Text 2
Text 5
这是我的for
循环所需要的。
引起混乱。所以我想我正在寻找的是一个for
循环,用伪代码看起来像:
el = driver.find_elements_by_xpath("*//span")
for i in el:
print(i.text)
print(i.parent.text) #trying this in real life raises an error....
答案 0 :(得分:2)
我知道我已经接受@JeffC的回答,但是在研究这个问题的过程中,我发生了一些事情。这很可能是一种矫kill过正,但这是一种有趣的方法,为了子孙后代,我认为我也应该将其张贴在这里。
该想法涉及使用BeautifulSoup。原因是BS有两种从树中删除节点的方法。其中一种可能有用的方法(据我所知Selenium没有等效方法)是decompose()
(see more here)。我们可以使用decompose()
来抑制打印父级text
的第二部分,它通过消除标记及其内容而包含在span
标记内。因此,我们导入BS并从@JeffC的答案开始:
from bs4 import BeautifulSoup
elp = driver.find_elements_by_css_selector("span.third")
for i in elp:
print(i.text)
s = i.find_element_by_xpath("./..").get_attribute("innerHTML")
在这里切换到bs4
content = BeautifulSoup(s, 'html.parser')
content.find('span').decompose()
print(content.text)
没有字符串操作,正则表达式或其他任何东西的输出是...:
Text 3
Text 2
Text 6
Text 5
答案 1 :(得分:1)
i.parent.text无法正常工作,在Java中我曾经写过类似的东西
ele.get(i).findElement("here path to parent may be parent::div ").getText();
答案 2 :(得分:1)
这是仅从父节点检索文本的python方法。
def get_text_exclude_children(element):
return driver.execute_script(
"""
var parent = arguments[0];
var child = parent.firstChild;
var textValue = "";
while(child) {
if (child.nodeType === Node.TEXT_NODE)
textValue += child.textContent;
child = child.nextSibling;
}
return textValue;""",
element).strip()
这是在您的情况下使用该方法的方法:
elements = driver.find_elements_by_css_selector("span.third")
for eleNum in range(len(elements)):
print(driver.find_element_by_xpath("(//span[@class='third'])[" + str(eleNum+1) +"]").text)
print(get_text_exclude_children(driver.find_element_by_xpath("(//span[@class='third'])[" + str(eleNum+1) +"]/parent::*")))
答案 3 :(得分:0)
可能有几种方法可以做到这一点。这是一种方法
elp = driver.find_elements_by_css_selector("span.third")
for i in elp:
print(i.text)
s = i.find_element_by_xpath("./..").get_attribute("innerHTML")
print(s.split('<')[0].strip())
我使用了一个简单的CSS选择器来找到子元素(“文本3”和“文本6”)。我遍历这些元素并打印其.text
,并向上导航至上一级以找到父级并打印其文本。如OP所述,打印父文本也将打印子文本。要解决此问题,我们需要获取innerHTML,将其拆分并去除空格。
详细解释XPath
./..
^ start at an existing node, the 'i' in 'i.find_element_*'. If you skip/remove this '.', you will start at the top of the DOM instead of at the child element you've already located.
^ go up one level, to find the parent