Разборчивый синтаксический анализатор XML Python: разрешение перекрывающихся тегов XML - PullRequest
0 голосов
/ 21 октября 2019

Я ищу ошибки дружественные (снисходительно в терминологии BeautifulSoup) анализатор "плохого" ввода XML. Проблема заключается в перекрытии тегов. Пример ввода:

<trn>choya - <i><b>a cholla cactus </i> lat. <i>Cylindropuntia</b></trn></i>

Что я хотел бы получить и XML-совместимый результат, такой как ( хорошо желаемый результат)

<trn>choya - <b><i>a cholla cactus </i> lat. <i>Cylindropuntia</i></b></trn>

The BeautifulSoup с html.parser или html5lib дает мне что-то еще ( плохой результат, который я не хочу):

<trn>choya - <i><b>a cholla cactus </b></i> lat. <i>Cylindropuntia</i></trn>

Обратите внимание на последовательность <i> и<b> тегов. Если я выделю <i> курсивом и <b> жирным шрифтом, то ответ good будет

choya - cholla cactus лат. Цилиндропунция

и Плохой ответ

Чойя - кактус чолла лат. Цилиндропунция

Я пробовал также старый tidyhtml, не смог получить необходимый результат. И для новых tidy-html5 не удалось найти интерфейс Python. Можете ли вы помочь мне, пожалуйста, либо

  • , чтобы найти анализатор, который может сделать эту работу
  • , если такового нет, чтобы посоветовать алгоритм или любой источник знаний, который касается этихвиды алгоритмов

Спасибо!

1 Ответ

0 голосов
/ 21 октября 2019

html.parser.HTMLParser хорош в разборе супа тега, а класс SAX XMLGenerator имеет удобный API для генерации XML на основе событий.

Здесь реализованы не все биты, особенно "ограничения жесткости "/" веса "для тегов (сейчас все, что мы делаем, это просто закрываем тег с тем, что мы ожидаем, чтобы сделать правильное вложение), но основная идея, похоже, работает.

Выводэто

<trn>choya - <i><com>a cholla cactus </com> lat. <i>Cylindropuntia</i></i> native to US</trn>

, который является действительным XML, с точки зрения вложенности.

Удачи!


import html.parser
import io
from xml.sax.saxutils import XMLGenerator


class Reconstructor(html.parser.HTMLParser):

    def __init__(self):
        super().__init__()
        self.op_stream = []
        self.tag_stack = []

    def handle_startendtag(self, tag, attrs):
        self.op_stream.append(('startendtag', (tag, attrs)))

    def handle_starttag(self, tag, attrs):
        self.op_stream.append(('starttag', (tag, attrs)))
        self.tag_stack.append(tag)

    def handle_endtag(self, tag):
        expected_tag = self.tag_stack[-1]
        if tag != expected_tag:
            print('mismatch closing <{}>, expected <{}>'.format(tag, expected_tag))
            # TODO: implement logic to figure out the correct order for the tags here
            #       and reorder tag_stack accordingly.
        stack_tag = self.tag_stack.pop(-1)
        self.op_stream.append(('endtag', (stack_tag, tag)))

    def handle_charref(self, name):
        self.op_stream.append(('charref', (name,)))

    def handle_entityref(self, name):
        self.op_stream.append(('entityref', (name,)))

    def handle_data(self, data):
        self.op_stream.append(('data', (data,)))

    def handle_comment(self, data):
        self.op_stream.append(('comment', (data,)))

    def handle_decl(self, decl):
        self.op_stream.append(('decl', (decl,)))

    def handle_pi(self, data):
        self.op_stream.append(('pi', (data,)))

    def generate_xml(self):
        stream = io.StringIO()
        xg = XMLGenerator(stream, encoding='utf-8')
        for op, args in self.op_stream:
            if op in ('startendtag', 'starttag'):
                tag, attrib = args
                xg.startElement(tag, dict(attrib))
                if op == 'startendtag':
                    xg.endElement(tag)
            elif op == 'endtag':
                tag = args[0]
                xg.endElement(tag)
            elif op == 'data':
                xg.characters(args[0])
            else:
                raise NotImplementedError('Operator not implemented: %s' % op)
        xg.endDocument()
        return stream.getvalue()


xr = Reconstructor()
xr.feed('<trn>choya - <i><com>a cholla cactus </i> lat. <i>Cylindropuntia</com></trn> native to US</i>')
y = xr.generate_xml()
print(y)
...