使用lxml html从嵌套元素中提取特定元素

时间:2010-04-14 04:40:08

标签: python html parsing xpath lxml

大家好我有一些问题,我认为可以归结为xpath问题。我正在使用lxml包中的html模块来尝试获取一些数据。我提供下面最简化的情况,但请记住我正在使用的HTML更加丑陋。

<table>
    <tr>
    <td>
        <table>
            <tr><td></td></tr>
            <tr><td>
                <table>
                    <tr><td><u><b>Header1</b></u></td></tr> 
                    <tr><td>Data</td></tr>
                </table>
            </td></tr>
        </table>
     </td></tr>
</table>

我真正想要的是深度嵌套的表,因为它有标题文本“Header1”。 我这样想:

from lxml import html
page = '...'
tree = html.fromstring(page)
print tree.xpath('//table[//*[contains(text(), "Header1")]]')

但是这给了我所有的表元素。我只想要包含此文本的一个表。我明白发生了什么,但是除了打破一些讨厌的正则表达式之外我很难搞清楚如何做到这一点。 有什么想法吗?

4 个答案:

答案 0 :(得分:3)

使用

//td[text() = 'Header1']/ancestor::table[1]

答案 1 :(得分:2)

找到您感兴趣的标题,然后拉出表格。

//u[b = 'Header1']/ancestor::table[1]

//td[not(.//table) and .//b = 'Header1']/ancestor::table[1]

请注意,// 始终从文档根目录(!)开始。你做不到:

//table[//*[contains(text(), "Header1")]]

并期望内部谓词(//*…)神奇地从正确的上下文开始。使用.//从上下文节点开始。即便如此,这个:

//table[.//*[contains(text(), "Header1")]]

将无法工作,因为即使最外层的表在内部某处包含文本'Header1',因此对于示例中的每个表,谓词的计算结果为true。像我一样使用not()来确保没有嵌套其他表。

此外,不要在每个节点.//*上测试条件,因为每个节点都不能开始。具体而言,效率更高。

答案 2 :(得分:0)

也许这对你有用:

tree.xpath("//table[not(descendant::table)]/*[contains(., 'Header1')]")

not(descendant::table)位确保您获得最里面的表。

答案 3 :(得分:0)

table, = tree.xpath('//*[.="Header1"]/ancestor::table[1]')
  • //*[text()="Header1"]在文档中的任意位置选择文字Header1
  • ancestor::table[1]选择元素table的第一个祖先。

完整示例

#!/usr/bin/env python
from lxml import html

page = """
<table>
    <tr>
    <td>
        <table>
            <tr><td></td></tr>
            <tr><td>
                <table>
                    <tr><td><u><b>Header1</b></u></td></tr> 
                    <tr><td>Data</td></tr>
                </table>
            </td></tr>
        </table>
     </td></tr>
</table>
"""

tree = html.fromstring(page)
table, = tree.xpath('//*[.="Header1"]/ancestor::table[1]')
print html.tostring(table)