如何在lxml中找到元素的直接子元素

时间:2018-01-31 17:40:34

标签: python xpath lxml

我找到了一个具有特定类的对象:

THREAD = TREE.find_class('thread')[0]

现在我希望得到所有直接孩子的<p>元素。

我累了:

THREAD.findall("p")

THREAD.xpath("//div[@class='thread']/p")

但所有这些都会返回此<p>内的所有<div>个元素,无论<div>是否是他们最亲近的父级。

我怎样才能让它发挥作用?

修改

示例html:

<div class='thread'>
   <p> <!-- 1 -->
      <!-- Can be some others <p> objects inside, which should not be counted -->
   </p> 
   <p><!-- 2 --></p>
</div>
<div class='thread'>
   <p>[...]</p>
   <p>[...]</p>
</div>

脚本应该找到两个<p>对象,它们是THREAD的子对象。我应该收到两个对象的列表,在示例HTML的注释中标记为“1”和“2”。

编辑2:

又一个澄清,因为人们感到困惑:

THREAD是存储在变量中的一些对象,可以是任何html元素。我想找到<p>的直接子项的THREAD个对象。那些<p>不能在THREAD之外或在THREAD内的任何元素内。

3 个答案:

答案 0 :(得分:2)

我不确定,但似乎您的问题出在HTML本身:请注意there are couple Tag omission cases applicable for p nodes,因此关闭段落标记

<div class='thread'>
    <p>first
        <p>second</p>
    </p>
</div>

简单地被解析器忽略,并且两个节点都被识别为兄弟节点,但不是父节点和子节点,例如

<div class='thread'>
    <p>first
    <p>second
</div>

所以XPath //div[@class="thread"]/p会返回两段

您只需使用p标记替换div代码,您就会看到不同的行为:

<div class='thread'>
    <div>first
        <div>second</div>
    </div>
</div>

此处//div[@class="thread"]/div将仅返回第一个节点

如果我的假设不正确,请纠正我

答案 1 :(得分:1)

试试这个XPath表达式:

//p[parent::div[@class='thread']]

或者在完整的Python表达式中:

THREAD.xpath("//p[parent::div[@class='thread']]")

另一种(逆)方法是这个XPath表达式:

div[@class='thread']/child::p"

使用直接child::轴,仅选择直接子节点。

要点:
两个表达式中哪一个更快取决于XPath编译器。 child::是默认轴,如果没有给出其他轴,则使用它。

仅供参考:XPath计数从 1 开始,不是0
关于XML示例,请使用以下表达式

count(//div[@class='thread'][1]/child::p)

会产生值2 - 计算<p> <!-- 1 --> + <p><!-- 2 --></p>的结果。

答案 2 :(得分:0)

您可以尝试 PARENT.getchildren()

>>> root = etree.fromstring(xml)
>>> root.xpath("//div[@class='thread']")[0].getchildren()
[<Element p at 0x10b3110e0>, <Element p at 0x10b311ea8>]