在 Python 中使用未声明的前缀解析 XML
Parsing XML with undeclared prefixes in Python
我正在尝试使用使用前缀的 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
,就在 <abc:thing2>
的开头)
搜索此错误会引导我找到建议我修复命名空间声明的解决方案。但是,我无法控制我需要使用的 XML,因此修改输入文件不是一个可行的选择。
一般搜索命名空间解析让我产生了很多关于以与命名空间无关的方式进行搜索的问题,这不是我需要的。
我正在寻找一些方法来自动解析这些文件,即使名称空间声明被破坏。我考虑过执行以下操作:
- 事先告诉 ElementTree 预期的名称空间是什么,因为我知道哪些名称空间会出现。我找到了
register_namespace
,但这似乎不起作用。
- 在解析之前读入完整的 DTD,看看是否能解决问题。我找不到使用 ElementTree 执行此操作的方法。
- 告诉 ElementTree 根本不用担心名称空间。它不应该导致我的数据出现问题,但我没有办法做到这一点
- 使用一些其他可以处理这个问题的解析库——尽管我不想安装额外的库。我很难从文档中看出是否有其他人能够解决我的问题。
- 我目前没有看到的其他路线?
更新:
Har07让我走上lxml
的道路后,我试着看看这是否会让我执行我想到的不同解决方案,结果会是什么:
- 预先告诉解析器预期的命名空间:我仍然找不到任何 'official' 方法来做到这一点,但在我的搜索中,我发现了以编程方式简单地将必要的声明添加到数据的建议. (对于不同的编程情况 - 不幸的是我再也找不到 link 了)这对我来说似乎非常 hacky,但我还是试过了。它涉及将数据作为字符串加载,更改封闭元素以具有正确的
xmlns
声明,然后将其传递给 lxml.etree
的 fromstring
方法。不幸的是,这还需要从字符串中删除所有对编码声明的引用。不过它确实有效。
- 解析前读入DTD:可以用
lxml
(通过attribute_defaults
、dtd_validation
或load_dtd
),但不幸的是没有解决命名空间问题。
- 告诉
lxml
不要为名称空间操心:可以通过 recover
选项实现。不幸的是,这也忽略了 XML 可能被破坏的其他方式(有关详细信息,请参阅 Har07 的回答)
一种可能的方法是使用 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 错误。但是除了未绑定的命名空间前缀之外,我还知道另一种类型的错误:unclosed tag。 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>
我正在尝试使用使用前缀的 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
,就在 <abc:thing2>
的开头)
搜索此错误会引导我找到建议我修复命名空间声明的解决方案。但是,我无法控制我需要使用的 XML,因此修改输入文件不是一个可行的选择。
一般搜索命名空间解析让我产生了很多关于以与命名空间无关的方式进行搜索的问题,这不是我需要的。
我正在寻找一些方法来自动解析这些文件,即使名称空间声明被破坏。我考虑过执行以下操作:
- 事先告诉 ElementTree 预期的名称空间是什么,因为我知道哪些名称空间会出现。我找到了
register_namespace
,但这似乎不起作用。 - 在解析之前读入完整的 DTD,看看是否能解决问题。我找不到使用 ElementTree 执行此操作的方法。
- 告诉 ElementTree 根本不用担心名称空间。它不应该导致我的数据出现问题,但我没有办法做到这一点
- 使用一些其他可以处理这个问题的解析库——尽管我不想安装额外的库。我很难从文档中看出是否有其他人能够解决我的问题。
- 我目前没有看到的其他路线?
更新:
Har07让我走上lxml
的道路后,我试着看看这是否会让我执行我想到的不同解决方案,结果会是什么:
- 预先告诉解析器预期的命名空间:我仍然找不到任何 'official' 方法来做到这一点,但在我的搜索中,我发现了以编程方式简单地将必要的声明添加到数据的建议. (对于不同的编程情况 - 不幸的是我再也找不到 link 了)这对我来说似乎非常 hacky,但我还是试过了。它涉及将数据作为字符串加载,更改封闭元素以具有正确的
xmlns
声明,然后将其传递给lxml.etree
的fromstring
方法。不幸的是,这还需要从字符串中删除所有对编码声明的引用。不过它确实有效。 - 解析前读入DTD:可以用
lxml
(通过attribute_defaults
、dtd_validation
或load_dtd
),但不幸的是没有解决命名空间问题。 - 告诉
lxml
不要为名称空间操心:可以通过recover
选项实现。不幸的是,这也忽略了 XML 可能被破坏的其他方式(有关详细信息,请参阅 Har07 的回答)
一种可能的方法是使用 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 错误。但是除了未绑定的命名空间前缀之外,我还知道另一种类型的错误:unclosed tag。 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>