如何使用XPath选择具有相同id属性的多个表元素?
我现在正在尝试从一个格式很糟糕的网页中提取信息。具体来说,这个页面对多个表格元素使用了相同的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 个回答
count(//div[@id = "random_div"]/table[@id= "table_1"])
这个xpath对于你提供的示例输入返回的是3。所以你的问题不在于xpath本身,而是在于你用来提取节点的函数。
使用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不太了解,所以不能确定这是否会造成问题,但这可能也是你遇到的问题之一吧?