我在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> </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> </TD>
</TR></TABLE>
<blockquote>
<p class="textstyle">
Text.
</p>
</blockquote>
我的第一个冲动是提取所有元素文本,并以编程方式将其切片。我会注意Title1,Title2等知道何时开始行,然后如果找到“----”意味着没有值,则跳过此行然后继续。但是,我意识到可能有一种更好的方法直接用xpath处理它。
如何通过xpath解决这个问题,以便基本上给每个单元格的最终子文本内容与必须走进每个div(如果存在)?或者是否有更多的xpath方法可以解决这个问题?
显然,我试图拥有最灵活的解决方案,即使不太可能出现其他意外因素也不会太脆弱。
答案 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> </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> </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> </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> </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;”或类似内容。)