在Python中解析带有未声明前缀的XML

Ani*_*que 8 python xml parsing namespaces xml-namespaces

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

<?xml version="1.0" encoding="UTF-8"?>
<item subtype="bla">
    <thing>Word</thing>
    <abc:thing2>Another Word</abc:thing2>
</item>
Run Code Online (Sandbox Code Playgroud)

我一直在使用xml.etree.ElementTree解析这些文件,但是只要未正确声明前缀,ElementTree就会引发解析错误。(unbound prefix,位于的开头<abc:thing2>)。搜索此错误将导致我提出解决方案,建议我修复名称空间声明。但是,我无法控制需要使用的XML,因此修改输入文件不是一个可行的选择。

通常,搜索名称空间解析会使我想到许多与以名称空间无关的方式进行搜索的问题,这不是我所需要的。

我正在寻找一种自动解析这些文件的方法,即使名称空间声明已损坏。我已经考虑过要执行以下操作:

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

更新:在Har07使我走上的道路之后lxml,我试图查看这是否可以执行我曾想到的不同解决方案,其结果将是:

  • 告诉解析器事先要有什么名称空间:我仍然找不到任何“官方”方式来做到这一点,但是在搜索之前,我发现只是简单地以编程方式向数据添加必需声明的建议。(对于另一种编程情况-不幸的是我找不到链接了)对我来说似乎很骇客,但我还是尝试了一下。它涉及将数据作为字符串加载,更改包围元素以具有正确的xmlns声明,然后将其传递给lxml.etreefromstring方法。不幸的是,这还需要从字符串中删除对编码声明的所有引用。虽然可以。
  • 阅读在DTD解析之前:这是可能的lxml(通过attribute_defaultsdtd_validationload_dtd),但遗憾的是没有解决的命名空间的问题。
  • 告诉您lxml不要打扰名称空间:可以通过该recover选项进行操作。不幸的是,这也忽略了破坏XML的其他方式(有关详细信息,请参见Har07的答案)。

har*_*r07 5

一种可能的方法是使用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))
Run Code Online (Sandbox Code Playgroud)

使用解析格式不正确的XML所需要做的lxml就是将参数传递recover=True给的构造函数XMLParserlxml还完全支持xpath 1.0,当您需要使用更复杂的条件获取XML文档的一部分时,xpath 1.0非常有用。

更新:

我不知道该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))
Run Code Online (Sandbox Code Playgroud)

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

<item subtype="bla">
    <thing>Word</thing>
    <bad>
    <abc:thing2>Another Word</abc:thing2>
</bad></item>
Run Code Online (Sandbox Code Playgroud)