在Python

时间:2015-06-02 13:09:39

标签: 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>

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

通常,搜索名称空间解析会引发许多关于以命名空间无关的方式进行搜索的问题,这不是我需要的。

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

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

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

  • 告诉解析器事先想要什么命名空间:我仍然找不到任何'官方'的方法来做到这一点,但在我的搜索之前,我找到了简单地以编程方式向数据添加必要声明的建议。 (对于不同的编程情况 - 不幸的是我再也找不到链接了)对我来说这看起来非常糟糕,但无论如何我都试过了。它涉及将数据作为字符串加载,将封闭元素更改为具有正确的xmlns声明,然后将其移交给lxml.etree的{​​{1}}方法。不幸的是,这还需要从字符串中删除对编码声明的所有引用。但它确实有用。
  • 在解析之前读入DTD:可以使用fromstring(通过lxmlattribute_defaultsdtd_validation),但遗憾的是无法解决命名空间问题。
  • 告诉load_dtd不要打扰命名空间:可以通过lxml选项。不幸的是,这也忽略了XML可能被破坏的其他方式(参见Har07的详细解答)

1 个答案:

答案 0 :(得分: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))

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

更新:

我不知道recover=True选项可以容忍的所有类型的XML错误。但是除了未绑定的名称空间前缀之外,我还知道另一种类型的错误:unclosed标签。 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>