【问题标题】:Manipulating list from lxml xpath queries从 lxml xpath 查询操作列表
【发布时间】:2010-08-12 11:39:22
【问题描述】:

今天我尝试了 lxml,因为我从特定的 web 服务中得到了非常讨厌的 html 输出,我不想使用 re 模块,只是为了改变和学习新的东西。我做到了,同时浏览 http://codespeak.net/lxml/http://stackoverflow.com

我不会尝试解释上面的 html 模板,但只是为了概述它充满了故意嵌套的表格。

我使用 html 解析器提取了部分感兴趣的内容,然后使用 find_class() 并使用 xpath 遍历 TR(甚至这个 TR 内部也有表格)。 现在我正在尝试根据类和 id 属性提取数据对:

  • name child 有类“title”
  • 值孩子的 ID 为“文本”

代码如下所示:

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"]')

一些经验丰富的cmets?

【问题讨论】:

  • 您正在解析的样本可能会有所帮助
  • 我已经删除了我的答案,因为你显然不知道你在问什么。我建议的表达式返回的值与您问题中的表达式返回的值相同。
  • 我在这里上传了稍微修改的示例:pastebin.com/cg5HHJ6x - 这是迭代的 TR。 @Dimitre Novatchev:如果您认为是这样,请返回您的答案并讨论它 - 1. 它有语法错误和 2. 它不起作用。如果你不能支持它,我认为你不应该试图提供答案
  • 收到您的样品。现在你想从样本中提取什么?
  • 我想配对
    子元素和 子元素,它们位于主 分支内(已发布示例中有 13 个,尽管它们在现实中有所不同)。并非每个主要的 元素都具有两者,有些则两者都没有。例如:第一个 元素没有任何元素,第二个 元素只有
    并且第三个 元素具有有效对。

标签: python xpath lxml


【解决方案1】:

怎么样?

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 指出这一点并提出了一个似乎对 otrov 不起作用的修复方法。

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 "--"

【讨论】:

  • 太棒了!感谢您的正确答案和精彩的课程。现在我可以继续剩下的代码了 :)
  • @otrov:不客气!我个人发现 xpath 是一个陡峭的学习曲线,这个网站上的问题示例非常方便,有一些 XLST/XPATH 大师潜伏在 SO 上。谢谢你让我超过2000! :)
  • 嗯,那太好了,看来你已经为自己提供了不久的将来的门票:) 祝你好运
  • @MattH:检查答案,我觉得有问题:div[@id="title"][span/@class="Browse"]没有td[@class="text"]
  • @Alejandro,抱歉我今天有点密集。你是说这个解决方案没有返回一些应该返回的数据?
【解决方案2】:

现在,有了输入样本,你的要求就更清楚了。

仅这一个 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>

【讨论】:

  • 关于丢失的:我很快查了一下,确实有奇数个关闭标签和偶数个打开标签——这正是Web服务产生的,我不想在这里公开,但如果你想检查我的写作,我可以通过电子邮件或类似方式向你发送链接。
  • 最后,我相信您的 xpath 表达式在您使用 XSLT 检查时可以正常工作,但当我尝试将其放入我的代码时却不行。例如,我使用了 MattH sn-p 然后插入了 for(): 块,我输入了 "node = doc.xpath('/table/tr/td/table[tr/td/div[@id="title" ]][tr/td[@class="text"]]/tr//*[self::div[@id="title"] 或 self::td[@class="text"]]')" 不产生结果,类似于 Dimitrie 的已删除答案。所以我可能应该用已知的正则表达式模块来做这件事,而不是在这样不舒服的例子上开始学习 lxml
  • @otrov,我已经用受 Alejandro 启发的更具体的表达方式更新了我的解决方案,我希望它能更好地为您服务。老实说,任何使用 xpath 而不是正则表达式来处理 HTML 或 XML 的时间都是值得的!
  • @Matt:您的初始代码非常适合我的使用,但我现在使用稍微修改过的版本,它的代码基本相同:) 感谢您对 XPATH 的鼓励,我试图在XML/XSLT 的过去但失败了,我想我需要为这个主题投入更多的脑细胞@Alejandro:感谢您的回答,我将更多地关注将一般 XPATH 表达式转换为 lxml xpath 表达式:)
  • @otrov: 如果table 元素不是您的根元素(就像在您发布的输入示例中一样),那么您可以将缺少的路径添加到table 元素(如/html/body/ 等.)。我不建议使用 // 运算符作为路径,因为它会导航所有树。
猜你喜欢
相关资源
最近更新 更多
热门标签