如何在for循环中使用'find_elements_by_xpath'

时间:2019-04-29 17:43:01

标签: selenium xpath beautifulsoup

我对以下内容有些(或非常)困惑:

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

4 个答案:

答案 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::*")))

这是输出: enter image description here

答案 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