解析缺少“/”的自闭合标签

Parse self-closing tags missing the '/'

我正在尝试使用 BeautifulSoup4 解析一些旧的 SGML 代码并使用数据构建元素树。它大部分工作正常,但一些应该自动关闭的标签没有这样标记。例如:

<element1>
    <element2 attr="0">
    <element3>Data</element3>
</element1>

当我解析数据时,结果如下:

<element1>
    <element2 attr="0">
        <element3>Data</element3>
    </element2>
</element1>

我想要的是假设如果它没有找到此类元素的结束标记,它应该将其视为自关闭标记而不是假设它之后的所有内容都是子元素并放置结束标记尽可能晚,像这样:

<element1>
    <element2 attr="0"/>
    <element3>Data</element3>
</element1>

任何人都可以向我指出可以执行此操作的解析器,或者可以通过某种方式修改现有的解析器以执行此操作吗?我研究了一些解析器(lxml、lxml-xml、html5lib),但我不知道如何获得这些结果。

我最后做的是提取所有可以从 DTD 中省略结束标记的空元素(例如 <!ELEMENT elem_name - o EMPTY >),从这些元素创建一个列表,然后使用正则表达式关闭所有标记在列表中。然后将生成的文本传递给 XML 解析器。

这是我正在做的事情的简化版本:

import re
from lxml.html import soupparser
from lxml import etree as ET

empty_tags = ['elem1', 'elem2', 'elem3']

markup = """
<elem1 attr="some value">
<elem2/>
<elem3></elem3>
"""

for t in empty_tags:
    markup = re.sub(r'(<{0}(?:>|\s+[^>/]*))>\s*(?:</{0}>)?\n?'.format(t), r'/>\n', markup)

tree = soupparser.fromstring(markup)
print(ET.tostring(tree, pretty_print=True).decode("utf-8"))

输出应该是:

<elem1 attr="some value"/>
<elem2/>
<elem3/>

(这实际上将包含在标签中,但解析器会添加这些标签。)

它将单独保留属性,并且不会触及已经 self-closed 的标签。如果标签有一个结束标签,但它是空的,它会删除结束标签并取而代之 self-close 标签,这样它就标准化了。

这不是一个非常通用的解决方案,但据我所知,在不知道应该关闭哪些标签的情况下没有其他方法可以做到这一点。甚至 OpenSP 也需要 DTD 来知道应该关闭哪些标签。