从lxml xpath查询中操作列表

3 投票
2 回答
8361 浏览
提问于 2025-04-16 02:41

今天我尝试了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表达式可以返回一个包含divtd配对的节点集合(按照文档顺序):

/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=***&amp;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=***&amp;sql=xxx:yyy">string</a>
          </td>

--
<div id="title">
              <span>string</span>
            </div>

<td class="text" style="padding-left:5px;">
            <a href="/***/***.dll?p=***&amp;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 "--"

撰写回答