【问题标题】:Parsing XML with namespace使用命名空间解析 XML
【发布时间】:2014-01-10 19:19:33
【问题描述】:

有了这个 XML

<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
    <subject>Reference rates</subject>
    <Sender>
        <name>European Central Bank</name>
    </Sender>
    <Cube>
        <Cube time='2013-12-20'>
            <Cube currency='USD' rate='1.3655'/>
            <Cube currency='JPY' rate='142.66'/>
        </Cube>
    </Cube>
</Envelope>

我可以像这样得到内部的Cube标签

from xml.etree.ElementTree import ElementTree

t = ElementTree()
t.parse('eurofxref-daily.xml')
day = t.find('Cube/Cube')
print 'Day:', day.attrib['time']
for currency in day:
    print currency.items()

Day: 2013-12-20
[('currency', 'USD'), ('rate', '1.3655')]
[('currency', 'JPY'), ('rate', '142.66')]

问题在于上述 XML 是已定义命名空间的原始文件的清理版本

<?xml version="1.0" encoding="UTF-8"?>
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
    <gesmes:subject>Reference rates</gesmes:subject>
    <gesmes:Sender>
        <gesmes:name>European Central Bank</gesmes:name>
    </gesmes:Sender>
    <Cube>
        <Cube time='2013-12-20'>
            <Cube currency='USD' rate='1.3655'/>
            <Cube currency='JPY' rate='142.66'/>
        </Cube>
    </Cube>
</gesmes:Envelope>

当我尝试获取第一个 Cube 标签时,我得到一个 None

t = ElementTree()
t.parse('eurofxref-daily.xml')
print t.find('Cube')

None

根标签包含命名空间

root = t.getroot()
print 'root.tag:', root.tag

root.tag: {http://www.gesmes.org/xml/2002-08-01}Envelope

它的孩子也是

for e in root.getchildren():
    print 'e.tag:', e.tag

e.tag: {http://www.gesmes.org/xml/2002-08-01}subject
e.tag: {http://www.gesmes.org/xml/2002-08-01}Sender
e.tag: {http://www.ecb.int/vocabulary/2002-08-01/eurofxref}Cube

如果我在标签中包含命名空间,我可以获得Cube 标签

day = t.find('{http://www.ecb.int/vocabulary/2002-08-01/eurofxref}Cube/{http://www.ecb.int/vocabulary/2002-08-01/eurofxref}Cube')
print 'Day: ', day.attrib['time']

Day:  2013-12-20

但这真的很难看。除了在处理或进行字符串操作之前清理文件之外,还有一种优雅的方式来处理它吗?

【问题讨论】:

  • “定义的不存在的命名空间”是什么意思,URI 不必存在于网络上
  • @Mark 我的意思是那些 URI 会返回 404 not found。如果这不是问题,那么问题是另一个问题。
  • 这对于命名空间来说不是问题,并且包含命名空间是按照 lxml(它是 ElementTree 的超集)tutorial 执行此操作的正确方法 - 这种使用命名空间的方式是比我在其他 XML API 中看到的要好得多

标签: python xml python-2.6 elementtree


【解决方案1】:

有一种比在查询文本中包含整个命名空间 URI 更优雅的方法。对于不支持ElementTree.find 上的namespaces 参数的python 版本,lxml 提供了缺少的功能并且与xml.etree“大部分兼容”:

from lxml.etree import ElementTree

t = ElementTree()
t.parse('eurofxref-daily.xml')
namespaces = { "exr": "http://www.ecb.int/vocabulary/2002-08-01/eurofxref" }
day = t.find('exr:Cube', namespaces)
print day

使用namespaces 对象,您可以一劳永逸地设置它,然后在查询中使用前缀。

这是输出:

$ python test.py
<Element '{http://www.ecb.int/vocabulary/2002-08-01/eurofxref}Cube' at 0x7fe0f95e3290>

如果您发现前缀不优雅,那么您必须处理没有名称空间的文件。或者,即使命名空间有效但我不使用它们,也可能有其他工具会“欺骗”并匹配 local-name()。

在 python 2.7 或 python 3.3 或更高版本中,您可以使用与上述相同的代码,但使用 xml.etree 而不是 lxml,因为它们已经为这些版本添加了对命名空间的支持。

【讨论】:

  • 您使用的 XML 版本是什么?请注意,问题发生在第二个未修改的版本上,即带有名称空间的版本。你得到了没有属性的根Cube。试试find('Cube/Cube')
  • find(match, namespaces) 是 Python 3.3 中的新功能
  • 实际上,namespaces 参数在 python 2.7 中也可用,这是我在发布答案之前用来测试答案的。但是您在问题中使用了 2.6 标签,所以我更新了我的答案以考虑到这一点。
  • lxml 不包含在 2.6 中,但您的回答已经足够好了,所以我接受了。
猜你喜欢
  • 2021-05-03
  • 2010-11-08
  • 2018-07-15
  • 2012-06-11
  • 2015-09-08
  • 2011-09-20
  • 2012-10-14
相关资源
最近更新 更多