6

我目前正在尝试迭代解析一个非常大的 HTML 文档(我知道.. yuck)以减少使用的内存量。我遇到的问题是我收到了 XML 语法错误,例如:

lxml.etree.XMLSyntaxError: Attribute name redefined, line 134, column 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'
4

5 回答 5

8

完美的解决方案最终成为 Python 自己的HTMLParser [docs]

这是我最终使用的(非常糟糕的)代码:

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)
于 2011-12-13T04:18:23.987 回答
5

目前 lxml etree.iterparse支持关键字参数 recover=True,因此无需编写 HTMLParser 的自定义子类来修复损坏的 html,您只需将此参数传递给 iterparse。

要正确解析巨大且损坏的 html,您只需要执行以下操作:

etree.iterparse(tfile, events=('start', 'end'), html=True, recover=True)
于 2015-08-17T11:52:16.280 回答
1

用于iterparseTrue的参数和.htmlhuge_tree

于 2011-12-12T17:09:52.777 回答
0

很抱歉重新提出一个老问题,但是对于正在寻找解决方案的后来者来说,Lxml 3.3 有HTMLPullParser 和 XMLPullParser可以进行增量解析。您还可以查看Lxml 解析介绍以获取更多示例。

如果正如 OP 所说,需要解析一个非常大的文档并且需要节省内存,则可以编写一个自定义类作为事件处理程序以避免构建元素树。就像是:

class MyParserTarget:
    def start(self, tag, attrib, nsmap) -> None:
        # do something
    def end((self, tag) -> None:
        # do something
    def data(self, data) -> None:
        # do something
    def close(self):
        # return your result

mytarget = MyParserTarget()
parser = lxml.etree.HTMLPullParser(target=mytarget)
parser.feed(next(content))
# Do other stuff
result = parser.close()
于 2021-08-18T14:39:20.053 回答
-1

尝试使用lxml.html解析您的 HTML 文档:

从 2.0 版本开始,lxml 附带了一个专门用于处理 HTML 的 Python 包:lxml.html。它基于 lxml 的 HTML 解析器,但为 HTML 元素提供了一个特殊的 Element API,以及一些用于常见 HTML 处理任务的实用程序。

于 2011-12-12T16:51:47.530 回答