【问题标题】:Lenient XML python parser: Resolve xml tags overlap宽松的 XML python 解析器:解决 xml 标签重叠
【发布时间】:2019-10-21 07:59:10
【问题描述】:

我正在寻找“坏”XML 输入的友好错误(在 BeautifulSoup 的术语中是宽松的)python 解析器。问题是标签重叠。一个示例输入是:

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

我想得到什么以及符合 XML 的结果,例如(good result I wish)

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

BeautifulSouphtml.parserhtml5lib 给了我别的东西(我不想要的结果):

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

注意&lt;i&gt;&lt;b&gt;标签的顺序。如果我将&lt;i&gt; 标记为斜体,&lt;b&gt; 标记为粗体,则答案是

choya - 仙人掌 纬度 Cylindropuntia

不好的答案是

choya - cholla 仙人掌 纬度。 圆柱体

我也尝试过旧的tidyhtml,无法获得必要的结果。而对于新的tidy-html5 找不到python 接口。 你能帮帮我吗

  • 找到可以完成这项工作的解析器
  • 如果没有,建议算法或与此类算法相关的任何知识来源

谢谢!

【问题讨论】:

    标签: python xml parsing


    【解决方案1】:

    html.parser.HTMLParser擅长解析标签汤,SAXXMLGenerator类有一个方便的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)
    

    【讨论】:

    • 感谢您的回答。然而,输出是 bad 结果。正如我所说,带有 html.parser 的 BeautifulSoup 给了我同样的效果。我将缩短并清除任务以强调问题。
    • 但是这个结果是可定制的——参见代码中的 TODO 注释。
    • 我明白了...是的,正确的handle_endtag 逻辑函数将是我问题的答案。谢谢,我会在成功(或失败)时再次发表评论。
    猜你喜欢
    • 2011-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    • 2013-11-19
    • 2016-04-24
    相关资源
    最近更新 更多