迭代解析HTML(使用lxml?)
我现在正在尝试用 lxml.etree.iterparse
来逐步解析一个非常大的HTML文档(我知道,这听起来很糟糕)。
增量解析器。将XML解析成树,并以类似SAX的方式生成元组(事件,元素)。
我使用增量/迭代/SAX的方法来 减少内存使用(因为文件很大,我不想把HTML加载到DOM/树中)。
我遇到的问题是,出现了XML语法错误,比如:
lxml.etree.XMLSyntaxError: 属性名称重新定义,行 134,列 59
这导致整个过程停止了。
有没有办法在解析HTML时不被语法错误卡住呢?
目前我是在从XML语法错误的异常中提取行号,然后把那一行从文档中删除,再重新开始这个过程。这个方法听起来真让人不舒服。有没有更好的办法呢?
编辑:
这是我现在正在做的:
context = etree.iterparse(tfile, events=('start', 'end'), html=True)
in_table = False
header_row = True
while context:
try:
event, el = context.next()
# do something
# remove old elements
while el.getprevious() is not None:
del el.getparent()[0]
except etree.XMLSyntaxError, e:
print e.msg
lineno = int(re.search(r'line (\d+),', e.msg).group(1))
remove_line(tfilename, lineno)
tfile = open(tfilename)
context = etree.iterparse(tfile, events=('start', 'end'), html=True)
except KeyError:
print 'oops keyerror'
5 个回答
1
在使用 iterparse
时,可以把 html
和 huge_tree
的参数设置为 True
。
6
现在,lxml的etree.iterparse支持一个叫做recover=True的参数。这意味着你不需要自己写一个HTML解析器来处理那些有问题的HTML代码,只需要在使用iterparse的时候加上这个参数就可以了。
如果你想正确解析那些又大又有问题的HTML,只需要做以下几步:
etree.iterparse(tfile, events=('start', 'end'), html=True, recover=True)
12
最终的完美解决方案是使用Python自带的HTMLParser
[文档]。
这是我最终使用的(其实不太好)代码:
class MyParser(HTMLParser):
def __init__(self):
self.finished = False
self.in_table = False
self.in_row = False
self.in_cell = False
self.current_row = []
self.current_cell = ''
HTMLParser.__init__(self)
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
if not self.in_table:
if tag == 'table':
if ('id' in attrs) and (attrs['id'] == 'dgResult'):
self.in_table = True
else:
if tag == 'tr':
self.in_row = True
elif tag == 'td':
self.in_cell = True
elif (tag == 'a') and (len(self.current_row) == 7):
url = attrs['href']
self.current_cell = url
def handle_endtag(self, tag):
if tag == 'tr':
if self.in_table:
if self.in_row:
self.in_row = False
print self.current_row
self.current_row = []
elif tag == 'td':
if self.in_table:
if self.in_cell:
self.in_cell = False
self.current_row.append(self.current_cell.strip())
self.current_cell = ''
elif (tag == 'table') and self.in_table:
self.finished = True
def handle_data(self, data):
if not len(self.current_row) == 7:
if self.in_cell:
self.current_cell += data
有了这段代码,我就可以做到这个:
parser = MyParser()
for line in myfile:
parser.feed(line)