Skip to main content
 首页 » 编程设计

python之使用 namespace 解析 XML

2025年05月04日11daizhj

使用这个 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 是原始文件的清理版本,其中定义了 namespace

<?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 

它的 child 也是

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 

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

请您参考如下方法:

有一种比在查询文本中包含整个命名空间 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 因为它们已经添加了对这些版本的命名空间。