从lxml xpath查询中操作列表
今天我尝试了lxml这个库,因为我从某个网络服务获取的HTML输出很糟糕,我不想用正则表达式模块,想换个方式并学习一些新东西。于是我边浏览http://codespeak.net/lxml/,边在http://stackoverflow.com上查资料。
我不打算详细解释上面的HTML模板,但简单说一下,它里面有很多故意嵌套的表格。
我用HTML解析器提取了我感兴趣的部分,然后用find_class()方法和xpath遍历TR(而且这些TR里面还有表格)。现在我想根据类和ID属性提取数据对:
- 名字的子元素类是“title”
- 值的子元素ID是“text”
代码大致是这样的:
fragment = root.find_class('foo')
for node in fragment[0].xpath('table[2]/tr'):
name = node.xpath('//div[@id="title"]')
value = node.xpath('//td[@class="text"]')
问题是,并不是每个我遍历的TR都有这对数据:有些只有名字(ID是“title”),所以后来当我尝试把它们配对时,就会出现错误的数据配对。
我尝试了几种想到的方法,但都没有成功:我试着比较名字和值的列表长度,如果不匹配就跳过名字的查找,然后如果还是不匹配,就删除最后一个列表项(尝试了很多种方式),但都没用。例如:
if not len(name) == len(value):
name.pop()
或者
if len(name) == len(value):
name = node.xpath('//div[@id="title"]')
value = node.xpath('//td[@class="text"]')
有没有更有经验的人能给点建议?
2 个回答
0
现在,有了输入示例,你的问题变得更清楚了。
这个XPath 1.0表达式可以返回一个包含div
和td
配对的节点集合(按照文档顺序):
/table/tr/td/table[tr/td/div[@id='title']]
[tr/td[@class='text']]
/tr//*[self::div[@id='title'] or self::td[@class='text']]
作为证明,这个样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<result>
<xsl:copy-of
select="/table/tr/td/table[tr/td/div[@id='title']]
[tr/td[@class='text']]
/tr//*[self::div[@id='title'] or
self::td[@class='text']]"/>
</result>
</xsl:template>
</xsl:stylesheet>
输出结果(需要有正确的输入示例,因为你缺少一个关闭的td
标签):
<result>
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
<a href="/***/***.dll?p=***&sql=xxx:yyy">string</a>
</td>
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
Gospodar of Lutaka
</td>
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
1986
</td>
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
Sep 1985-Dec 1985
</td>
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
Elektra
</td>
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
54:51
</td>
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;"></td>
</result>
6
这个怎么样?
from lxml import etree
doc = etree.HTML(open('test.data').read())
for t in doc.xpath('//table[.//div[@id="title"] and .//td[@class="text"]]'):
print etree.tostring(t.xpath('.//div[@id="title"]')[0])
print etree.tostring(t.xpath('.//td[@class="text"]')[0])
print "--"
结果是:
<div id="title">
<span class="Browse">string</span>
</div>
<td class="text" style="padding-left:5px;">
<a href="/***/***.dll?p=***&sql=xxx:yyy">string</a>
</td>
--
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
<a href="/***/***.dll?p=***&sql=xxx:yyy">string</a>
</td>
--
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
Gospodar of Lutaka
</td>
--
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
1986
</td>
--
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
Sep 1985-Dec 1985
</td>
--
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
Elektra
</td>
--
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
54:51
</td>
--
<div id="title">
<span>string</span>
</div>
<td class="text" style="padding-left:5px;">
</td>
--
更新一下,扩展了xpath表达式的前半部分,以去掉一个不想要的结果。感谢Alejandro指出这个问题,并建议了一个看起来对otrova没有效果的解决办法。
from urllib2 import urlopen
from lxml import etree
doc = etree.HTML(urlopen('http://pastebin.com/download.php?i=cg5HHJ6x').read())
for t in doc.xpath('//table/tr/td/table[.//div[@id="title"] and .//td[@class="text"]]'):
print etree.tostring(t.xpath('.//div[@id="title"]')[0])
print etree.tostring(t.xpath('.//td[@class="text"]')[0])
print "--"