在 Python 中使用未声明的前缀解析 XML

Posted

技术标签:

【中文标题】在 Python 中使用未声明的前缀解析 XML【英文标题】:Parsing XML with undeclared prefixes in Python 【发布时间】:2015-08-16 07:20:18 【问题描述】:

我正在尝试使用使用前缀的 Python 解析 XML 数据,但并非每个文件都有前缀声明。示例 XML:

<?xml version="1.0" encoding="UTF-8"?>
<item subtype="bla">
    <thing>Word</thing>
    <abc:thing2>Another Word</abc:thing2>
</item>

我一直在使用 xml.etree.ElementTree 来解析这些文件,但是每当没有正确声明前缀时,ElementTree 就会引发解析错误。 (unbound prefix,就在&lt;abc:thing2&gt; 的开头) 搜索此错误会导致我找到建议我修复命名空间声明的解决方案。但是,我不控制我需要使用的 XML,因此修改输入文件不是一个可行的选择。

搜索命名空间解析通常会导致我提出许多关于以与命名空间无关的方式进行搜索的问题,这不是我所需要的。

我正在寻找一些方法来自动解析这些文件,即使命名空间声明被破坏。我曾想过做以下事情:

事先告诉 ElementTree 需要哪些名称空间,因为我知道哪些名称空间会出现。我找到了register_namespace,但这似乎不起作用。 在解析之前读入完整的 DTD,看看是否能解决问题。我找不到使用 ElementTree 的方法。 告诉 ElementTree 根本不用考虑命名空间。它不应该导致我的数据出现问题,但我发现没有办法这样做 使用其他一些可以处理这个问题的解析库——尽管我不希望安装额外的库。我很难从文档中看出是否有其他人能够解决我的问题。 我目前看不到的其他路线?

更新: 在har07让我走上lxml的道路后,我试着看看这是否能让我执行我想到的不同的解决方案,结果会是什么:

预先告诉解析器期望什么命名空间:我仍然找不到任何“官方”方法来执行此操作,但在我的搜索中,我发现了以编程方式简单地将必要的声明添加到数据的建议。 (对于不同的编程情况 - 不幸的是我再也找不到链接了)这对我来说似乎非常糟糕,但我还是尝试了。它涉及将数据作为字符串加载,将封闭元素更改为具有正确的xmlns 声明,然后将其交给lxml.etreefromstring 方法。不幸的是,这还需要从字符串中删除对编码声明的所有引用。不过,它确实有效。 在解析前读入 DTD:lxml 可以(通过attribute_defaultsdtd_validationload_dtd),但遗憾的是不能解决命名空间问题。 告诉lxml 不要打扰命名空间:可以通过recover 选项。不幸的是,这也忽略了可能破坏 XML 的其他方式(有关详细信息,请参阅 Har07 的答案)

【问题讨论】:

【参考方案1】:

一种可能的方法是使用ElementTree 兼容库lxml。例如:

from lxml import etree as ElementTree

xml = """<?xml version="1.0" encoding="UTF-8"?>
<item subtype="bla">
    <thing>Word</thing>
    <abc:thing2>Another Word</abc:thing2>
</item>"""
parser = ElementTree.XMLParser(recover=True)
tree = ElementTree.fromstring(xml, parser)

thing = tree.xpath("//thing")[0]
print(ElementTree.tostring(thing))

使用lxml 解析非格式良好的XML 所需要做的就是将参数recover=True 传递给XMLParser 的构造函数。 lxml 还完全支持 xpath 1.0,这在您需要使用更复杂的标准获取部分 XML 文档时非常有用。

更新:

我不知道recover=True 选项可以容忍的所有类型的 XML 错误。但是,除了未绑定的命名空间前缀之外,我还知道另一种类型的错误:未闭合标记。 lxml 将通过自动添加相应的关闭标签来修复 - 而不是忽略 - 未关闭标签。例如,给定以下损坏的 XML:

xml = """<item subtype="bla">
    <thing>Word</thing>
    <bad>
    <abc:thing2>Another Word</abc:thing2>
</item>"""
parser = ElementTree.XMLParser(recover=True)
tree = ElementTree.fromstring(xml, parser)

print(ElementTree.tostring(tree))

lxml解析后的最终输出XML如下:

<item subtype="bla">
    <thing>Word</thing>
    <bad>
    <abc:thing2>Another Word</abc:thing2>
</bad></item>

【讨论】:

谢谢,这确实有效。但是,从lxml 的文档来看,这似乎会忽略所有解析错误:“recover - try hard to parse through broken XML”你知道什么程度的破坏会漏掉,什么仍然会导致解析器抛出一个错误?出于我的目的,我确实想让这个命名空间问题通过,但仍应标记严重损坏的 XML。 @Anique lxml 也容忍未封闭的标签,这就是我所知道的。请参阅上面的更新

以上是关于在 Python 中使用未声明的前缀解析 XML的主要内容,如果未能解决你的问题,请参考以下文章

带有未声明前缀的 SQL Server Xml 查询

如何修复解析 XML:android 中的未绑定前缀

接收解析 XML:未绑定前缀错误和属性缺少 Android 命名空间前缀

XML:一个命名空间前缀被声明为未声明,而事实上它是

XML 和 XSL 中的前缀命名空间处理由 XML 解析器返回错误

使用非utf-8编码在Python中解析XML