使用行中的xpath提取表格单元格文本内容以供使用?

时间:2012-02-21 22:21:48

标签: php python html ruby xpath

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

<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方法可以解决这个问题?

显然,我试图拥有最灵活的解决方案,即使不太可能出现其他意外因素也不会太脆弱。

3 个答案:

答案 0 :(得分:3)

提供的文本不是格式良好的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>

答案 1 :(得分:0)

所以也许你不想走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)

答案 2 :(得分:0)

我相信你的程序在操作输入数据时会遇到很多问题 - 如果'标题'的情况发生变化,或者有错字会怎样?

真的不可能制定严格的解决方案来刮别人的网站,因为他们可以毫不通知地彻底改变一切。通常更好的是编写容忍且灵活的代码,至少尝试验证其输出是否合理。在这种情况下,最好迭代'// 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又是表的直接后代。

(简单地询问所有text()元素会在HTML上运行时产生一些有趣的错误,其中包含“&lt; td&gt; Foo&lt; b&gt; bar&lt; / b&gt;&lt; / td&gt;”或类似内容。)