如何使用XPath选择具有相同id属性的多个表元素?

1 投票
2 回答
7154 浏览
提问于 2025-04-17 04:57

我现在正在尝试从一个格式很糟糕的网页中提取信息。具体来说,这个页面对多个表格元素使用了相同的id属性。这个网页的结构大概是这样的:

<body>
    <div id="random_div">
        <p>Some content.</p>
        <table id="table_1">
            <tr>
                <td>Important text 1.</td>
            </tr>
        </table>
        <h4>Some heading in between</h4>
        <table id="table_1">
            <tr>
                <td>Important text 2.</td>
                <td>Important text 3.</td>
            </tr>
        </table>
        <p>How about some more text here.</p>
        <table id="table_1">
            <tr>
                <td>Important text 4.</td>
                <td>Important text 5.</td>
            </tr>
        </table>
    </div>
</body>

显然,这种HTML格式是错误的,因为同一个元素使用了多个相同的id。

我正在使用XPath来提取各种表格元素中的所有文本,并通过Scrapy框架来实现。

我的调用看起来像这样:

hxs.select('//div[contains(@id, "random_div")]//table[@id="table_1"]//text()').extract()

因此,XPath表达式是: //div[contains(@id, "random_id")]//table[@id="table_1"]//text()

这个表达式返回了:[u'重要文本 1.'],也就是说,它提取了第一个匹配id值为"table_1"的表格的内容。看起来一旦它遇到某个id的元素,就会忽略后面在网页中出现的相同id。有人能确认这一点吗?

更新

感谢下面的快速回复。我在本地测试了我的代码,使用了和上面相同的格式,返回了正确的响应,也就是:

`[u'Important text 1.', u'Important text 2.', . . . . ,u'Important text 5.']`

因此,我的XPath表达式和Python调用都没有问题。

我想这意味着网页本身存在问题,可能是搞坏了XPath或者HTML解析器,也就是libxml2

有没有人能给我一些建议,帮我深入了解这个问题?

更新 2

我已经成功找到了问题所在。实际上是底层的解析库lxml(它为libxml2 C库提供了Python绑定)出了问题。

问题在于解析器无法处理垂直制表符。我不知道这个网站是谁写的,但里面充满了垂直制表符。网页浏览器似乎可以忽略这些,所以在这个网站上用Firebug运行XPath查询时就能成功。

而且,因为上面的简化示例没有垂直制表符,所以运行得很好。对于在Scrapy(或一般Python中)遇到这个问题的人,以下修复方法对我有效,可以从HTML响应中移除垂直制表符:

def parse_item(self, response):
    # remove all vertical tabs from the html response
    response.body = filter(lambda c: c != "\v", response.body)
    hxs = HtmlXPathSelector(response)
    items = hxs.select('//div[contains(@id, \"random_div\")]' \
                       '//table[@id="table_1"]//text()').extract()

2 个回答

0
count(//div[@id = "random_div"]/table[@id= "table_1"])

这个xpath对于你提供的示例输入返回的是3。所以你的问题不在于xpath本身,而是在于你用来提取节点的函数。

1

使用Firebug时,这段代码:

//table[@id='table_1']//td/text()

给我返回了这个结果:

[<TextNode textContent="Important text 1.">,
 <TextNode textContent="Important text 2.">,
 <TextNode textContent="Important text 3.">,
 <TextNode textContent="Important text 4.">,
 <TextNode textContent="Important text 5.">]

我加了td的过滤,这样结果看起来更好,因为如果不加,你会看到标签之间的空格和换行。不过总的来说,这个方法似乎是有效的。

我注意到你查询的是//div[contains(@id, "random_id")],而你的HTML片段中有一个标签是<div id="random_div">——这里的_id_div是不一样的。我对Scrapy不太了解,所以不能确定这是否会造成问题,但这可能也是你遇到的问题之一吧?

撰写回答