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

时间:2011-10-25 11:15:41

标签: python xpath html-parsing web-scraping scrapy

我目前正在尝试从格式错误的网页中提取信息。具体来说,页面对多个表元素使用了相同的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'Important text 1.'],即第一个表中与id值“table_1”匹配的内容。在我看来,一旦遇到具有某个id的元素,它就会忽略标记中将来出现的任何事件。谁能证实这一点?

更新

感谢下面的快速回复。我已在本地托管的页面上测试了我的代码,该页面具有与上述相同的测试格式并返回正确的响应,即

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

因此,Xpath表达式或我正在进行的Python调用都没有错。

我想这意味着网页本身存在问题,无论是搞砸了XPath还是html解析器,都是libxml2

有没有人对如何深入研究这一点有任何建议?

更新2

我已经成功地解决了这个问题。它实际上是底层解析库,lxml(为libxml2 C库提供Python绑定。

问题是解析器无法处理垂直选项卡。我不知道是谁编写了我正在处理的网站,但它是完整的垂直标签。 Web浏览器似乎能够忽略这些,这就是为什么在相关站点上运行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 :(得分: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所以我不能说这是否有所作为,但这也不是你的问题吗?

答案 1 :(得分:0)

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

此xpath为您的示例输入返回3。所以你的问题不在于xpath本身,而在于你用来提取节点的函数。