Итеративный разбор HTML (с lxml?) - PullRequest
4 голосов
/ 12 декабря 2011

В настоящее время я пытаюсь итеративно анализировать очень большой HTML-документ (я знаю ... хм), чтобы уменьшить объем используемой памяти.У меня проблема в том, что я получаю ошибки синтаксиса 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 ]

6 голосов
/ 13 декабря 2011

Идеальным решением стало собственное 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)
4 голосов
/ 17 августа 2015

В настоящий момент lxml etree.iterparse поддерживает аргумент ключевого слова recovery = True , поэтому вместо написания пользовательского подкласса HTMLParser, исправляющего поврежденный html, вы можете просто передать этот аргумент в iterparse.

Для правильного разбора огромного и испорченного html вам нужно только сделать следующее:

etree.iterparse(tfile, events=('start', 'end'), html=True, recover=True)
0 голосов
/ 12 декабря 2011

Используйте True для iterparse аргументов html и huge_tree.

0 голосов
/ 12 декабря 2011

Попробуйте проанализировать ваш HTML-документ с помощью lxml.html :

Начиная с версии 2.0, lxml поставляется с выделенным пакетом Python для работы с HTML: lxml.html. Он основан на синтаксическом анализаторе HTML lxml, но предоставляет специальный Element API для элементов HTML, а также ряд утилит для общих задач обработки HTML.

...