使用Element Tree的findall方法解析XML命名空间

4

在以下xml中,我如何使用查询元素树findall('Email')

<DocuSignEnvelopeInformation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.docusign.net/API/3.0">
    <EnvelopeStatus>
        <RecipientStatus>
                <Type>Signer</Type>
                <Email>joe@gmail.com</Email>
                <UserName>Joe Shmoe</UserName>
                <RoutingOrder>1</RoutingOrder>
                <Sent>2015-05-04T09:58:01.947</Sent>
                <Delivered>2015-05-04T09:58:14.403</Delivered>
                <Signed>2015-05-04T09:58:29.473</Signed>
        </RecipientStatus>
    </EnvelopeStatus>
</DocuSignEnvelopeInformation>

我有一种感觉与命名空间有关,但我不确定。我查看了文档,但没有找到答案。

tree = <xml.etree.ElementTree.ElementTree object at 0x7f27a47c4fd0>
root = tree.getroot()
root
<Element '{http://www.docusign.net/API/3.0}DocuSignEnvelopeInformation' at 0x7f27a47b8a48>

root.findall('Email')
[]
2个回答

6
你应该仔细阅读文档,特别是使用命名空间解析XML部分,其中包含一个几乎与你所需相同的示例。
但即使没有文档,答案实际上也包含在你的示例输出中。当你打印文档的根元素时...
>>> tree = etree.parse(open('data.xml'))
>>> root = tree.getroot()
>>> root
<Element {http://www.docusign.net/API/3.0}DocuSignEnvelopeInformation at 0x7f972cd079e0>

...你可以看到它打印了根元素的名称 (DocuSignEnvelopeInformation),并带有一个命名空间前缀 ({http://www.docusign.net/API/3.0})。 你可以在findall的参数中使用相同的前缀:

>>> root.findall('{http://www.docusign.net/API/3.0}Email')

但这样本身是行不通的,因为这只会找到根元素的直接子级中的Email元素。你需要提供一个ElementPath表达式,以使findall搜索整个文档。下面的方法可行:
>>> root.findall('.//{http://www.docusign.net/API/3.0}Email')
[<Element {http://www.docusign.net/API/3.0}Email at 0x7f972949a6c8>]

您也可以使用XPath和命名空间前缀执行类似的搜索,如下所示:
>>> root.xpath('//docusign:Email',
... namespaces={'docusign': 'http://www.docusign.net/API/3.0'})
[<Element {http://www.docusign.net/API/3.0}Email at 0x7f972949a6c8>]

这使您可以使用类似于 XML 的 命名空间 前缀,而不是 LXML 命名空间语法。

谢谢,非常有帮助。我想使用您的最后一个root.xpath示例,但似乎ElementTree元素不支持.xpath,会出现AttributeError: 'xml.etree.ElementTree.Element' object has no attribute 'xpath'错误。 - user2954587
1
此答案假定您正在使用lxml.etree。 - larsks

1

我成功地使用了find命令中的namespaces选项:


s='<DocuSignEnvelopeInformation xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.docusign.net/API/3.0">     <EnvelopeStatus>         <RecipientStatus>                 <Type>Signer</Type>                 <Email>joe@gmail.com</Email>                 <UserName>Joe Shmoe</UserName>                 <RoutingOrder>1</RoutingOrder>                 <Sent>2015-05-04T09:58:01.947</Sent>                 <Delivered>2015-05-04T09:58:14.403</Delivered>                 <Signed>2015-05-04T09:58:29.473</Signed>         </RecipientStatus>     </EnvelopeStatus> </DocuSignEnvelopeInformation>'

ET.register_namespace('xsd', 'http://www.w3.org/2001/XMLSchema')
ET.register_namespace('def', 'http://www.docusign.net/API/3.0')
ET.register_namespace('xsi', 'http://www.w3.org/2001/XMLSchema-instance')
nsmap = {'xsd':'http://www.w3.org/2001/XMLSchema','def': 'http://www.docusign.net/API/3.0','xsi':'http://www.w3.org/2001/XMLSchema-instance'}
e=ET.fromstring(s)
print (ET.tostring(e,'utf-8'))

print (nsmap)
print (e.findall('.//def:Email', namespaces = nsmap))
print (e.findall('.//{http://www.docusign.net/API/3.0}Email'))

给出如下结果:

b'<def:DocuSignEnvelopeInformation xmlns:def="http://www.docusign.net/API/3.0">     <def:EnvelopeStatus>         <def:RecipientStatus>                 <def:Type>Signer</def:Type>                 <def:Email>joe@gmail.com</def:Email>                 <def:UserName>Joe Shmoe</def:UserName>                 <def:RoutingOrder>1</def:RoutingOrder>                 <def:Sent>2015-05-04T09:58:01.947</def:Sent>                 <def:Delivered>2015-05-04T09:58:14.403</def:Delivered>                 <def:Signed>2015-05-04T09:58:29.473</def:Signed>         </def:RecipientStatus>     </def:EnvelopeStatus> </def:DocuSignEnvelopeInformation>'
{'xsd': 'http://www.w3.org/2001/XMLSchema', 'def': 'http://www.docusign.net/API/3.0', 'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
[<Element '{http://www.docusign.net/API/3.0}Email' at 0x7f828077a590>]
[<Element '{http://www.docusign.net/API/3.0}Email' at 0x7f828077a590>]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接