【问题标题】:Extracting table cell text contents with xpath in rows for consumption?使用xpath在行中提取表格单元格文本内容以供消费?
【发布时间】:2012-03-12 06:35:20
【问题描述】:

在 HTML 方面,我有以下几点。我想提取表格单元格的各种内容,但是我发现单元格中偶尔会有一些嵌入的 div,也许还有其他我不确定的奇怪之处:

<p align="center">
    <img src="some_image.gif" alt="Some Title">
</p>
<TABLE WIDTH=500 BORDER=1 class=textwhite ALIGN=center CELLPADDING=0 CELLSPACING=0>
<TR>
<TD colspan=4 ALIGN=center><b>Title</b></TD>
</TR>
<TR>
<TD ALIGN=center>Title</TD>
<TD ALIGN=center>date</TD>
<TD ALIGN=center>value</TD>
<TD ALIGN=center>value</TD>
</TR><TR>
  <TD ALIGN=center>Title2</TD>
  <TD ALIGN=center></TD>
  <TD ALIGN=center><div class=redtext>----</div></TD>
  <TD>&nbsp;</TD>
</TR><TR>
  <TD ALIGN=center>Title3</TD>
  <TD ALIGN=center><div class=yellowtext>value</div></TD>
  <TD ALIGN=center><div class=redtext>value</div></TD>
  <TD ALIGN=center>value<SUP>6</SUP></TD>
</TR><TR>
  <TD ALIGN=center>Title4</TD>
  <TD ALIGN=center><div class=bluetext>value</div></TD>
  <TD ALIGN=center><div class=redtext>value</div></TD>
  <TD>&nbsp;</TD>
</TR></TABLE>

<blockquote>
    <p class="textstyle">
        Text.
    </p>
</blockquote>

我的第一个冲动是提取所有元素文本并以编程方式对其进行切片。我会注意 Title1、Title2 等,以了解一行何时开始,然后如果发现“----”表示没有价值,则跳过这一行并继续前进。但是,我意识到直接使用 xpath 可能有更好的方法来处理这个问题。

如何使用 xpath 解决这个问题,以便从本质上给出每个单元格的最终子文本内容,而不必走进每个 div(如果存在)?还是有更类似于 xpath 的方法来解决这个问题?

显然,我正在尝试提供最灵活的解决方案,即使出现其他意外元素,即使它们不太可能出现,也不会变得脆弱。

【问题讨论】:

  • 为什么不告诉我们想要的输出呢?
  • 所需的输出只是每个单元格的内容。最终,这将存储在一个哈希数组中,这样“Title1”、“Title2”等将是每个哈希的名称。然后,如果行中的每个单元格中都有一个值,它们将是该哈希中的附加值。

标签: php python html ruby xpath


【解决方案1】:

提供的文本不是格式良好的 XML 文档,因此 XPath 不适用

如果您将其更正并转换为格式良好的 xml 文档,如下所示,这样的表达式可能会很有用:

/*/TABLE//TD//text()

甚至:

//TABLE//TD//text()

这是一个格式良好的 XML 文档,由提供的 HTML 构造而成:

<html>
    <p align="center">
        <img src="some_image.gif" alt="Some Title"/>
    </p>
    <TABLE WIDTH="500" BORDER="1" class="textwhite" ALIGN="center" CELLPADDING="0" CELLSPACING="0">
        <TR>
            <TD colspan="4" ALIGN="center">
                <b>Title</b>
            </TD>
        </TR>
        <TR>
            <TD ALIGN="center">Title</TD>
            <TD ALIGN="center">date</TD>
            <TD ALIGN="center">value</TD>
            <TD ALIGN="center">value</TD>
        </TR>
        <TR>
            <TD ALIGN="center">Title2</TD>
            <TD ALIGN="center"></TD>
            <TD ALIGN="center">
                <div class="redtext">----</div>
            </TD>
            <TD>&#xA0;</TD>
        </TR>
        <TR>
            <TD ALIGN="center">Title3</TD>
            <TD ALIGN="center">
                <div class="yellowtext">value</div>
            </TD>
            <TD ALIGN="center">
                <div class="redtext">value</div>
            </TD>
            <TD ALIGN="center">value
                <SUP>6</SUP>
            </TD>
        </TR>
        <TR>
            <TD ALIGN="center">Title4</TD>
            <TD ALIGN="center">
                <div class="bluetext">value</div>
            </TD>
            <TD ALIGN="center">
                <div class="redtext">value</div>
            </TD>
            <TD>&#xA0;</TD>
        </TR>
    </TABLE>
    <blockquote>
        <p class="textstyle">         Text.     </p>
    </blockquote>
</html>

【讨论】:

  • 这里的关键是 //text()。谢谢。
【解决方案2】:

所以也许你不想走 div,但这是我使用 lxml 的解决方案,我强烈推荐:

import re
from cStringIO import StringIO
from lxml import etree

def getTable(html, table_xpath, rows_xpath, cells_xpath):
    """Get a table on a webpage"""
    parser = etree.HTMLParser()
    # Build document tree and get table
    root = etree.parse(StringIO(html), parser)
    table = root.find(table_xpath)
    if table == None:
        print 'No table.'
        return []
    rows = table.findall(rows_xpath)
    document = []
    def cleanText(text):
        """Clean up text by replacing line breaks and tabs. """
        return re.sub(r'[\r\n\t]+','',str(text).strip())
    # iterate over the table rows and collect text from each cell.
    for r in rows:
        cells = r.findall(cells_xpath)
        rowdata = []
        for c in cells:
            text = ''
            it = c.itertext()
            for i in it:
                text += cleanText(i) + ' '
            rowdata.append(text)
        document.append(rowdata)
    return document


html = """
<html><head><title></title></head><body>
<p align="center">
    <img src="some_image.gif" alt="Some Title">
    </p>
    <TABLE WIDTH=500 BORDER=1 class=textwhite ALIGN=center CELLPADDING=0 CELLSPACING=0>
    <TR>
    <TD colspan=4 ALIGN=center><b>Title</b></TD>
    </TR>
    <TR>
    <TD ALIGN=center>Title</TD>
    <TD ALIGN=center>date</TD>
    <TD ALIGN=center>value</TD>
    <TD ALIGN=center>value</TD>
    </TR><TR>
    <TD ALIGN=center>Title2</TD>
    <TD ALIGN=center></TD>
    <TD ALIGN=center><div class=redtext>----</div></TD>
    <TD>&nbsp;</TD>
    </TR><TR>
    <TD ALIGN=center>Title3</TD>
    <TD ALIGN=center><div class=yellowtext>value</div></TD>
    <TD ALIGN=center><div class=redtext>value</div></TD>
    <TD ALIGN=center>value<SUP>6</SUP></TD>
    </TR><TR>
    <TD ALIGN=center>Title4</TD>
    <TD ALIGN=center><div class=bluetext>value</div></TD>
    <TD ALIGN=center><div class=redtext>value</div></TD>
    <TD>&nbsp;</TD>
</TR></TABLE>   
</body>
</html>
"""
tp = "//table[@width='500']"
rt = "tr"
cp = "td[@align='center']"

doc = getTable(html, tp, rt, cp)
print repr(doc)

【讨论】:

    【解决方案3】:

    我相信你的程序在输入数据被操纵时会遇到很多问题——如果“标题”的大小写发生变化或有错字怎么办?

    实际上不可能制定严格的解决方案来抓取其他人的网站,因为他们可以在不知不觉中彻底改变一切。更好的通常是编写宽容和灵活的代码,至少尝试验证其输出是否正常。在这种情况下,最好迭代 '//table/tr' 的结果,然后在这个循环中,处理 td 元素:

    import lxml.etree
    tree = lxml.etree.fromstring("<table><tr><td>test</td></tr><tr><td><div>test2</div></td></tr></table>")
    stringify = lambda x : "".join(x.xpath(".//text()"))
    for x in tree.xpath("//table/tr"):
        print "New row"
        for y in x.xpath("td"):
            print stringify(y)
    

    输出:

    New row
    test
    New row
    test2
    

    但是,以下代码将获得您要求的列表:

    print map(stringify, tree.xpath("//table/tr/td"))
    

    输出:

    ['test', 'test2']
    

    这将找到所有从 td 继承的文本元素,td 是 tr 的直接后代,而 tr 又是 table 的直接后代。

    (在包含“

    Foo bar ”或类似内容的 HTML 上运行时,简单地询问所有 text() 元素会产生一些有趣的错误。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-20
      相关资源
      最近更新 更多