如何获取python中XML命名空间的前缀部分?

How to get the prefix part of XML namespace in python?

我有以下 XML(简而言之):

<?xml version="1.0" encoding="iso-8859-1"?>
<SOAP-ENV:Envelope 
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <SOAP-ENV:Body>
        <Proposal 
            xmlns="http://www.opengis.net/AAA"  
            xmlns:apd="http://www.opengis.net/BBB" 
            xmlns:common="http://www.opengis.net/DDD"  
            xmlns:core="http://www.opengis.net/EEE" 
            xmlns:pdt="http://www.opengis.net/CCC" 
            xmlns:xlink="http://www.opengis.net/FFF" 
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            
            <SchemaVersion>1.3</SchemaVersion>
            <ApplicationHeader>
                <ApplicationTo>W1234                         </ApplicationTo>
                <DateSubmitted>2021-04-26</DateSubmitted>
# ...
            <Agent>
                <common:PersonName>
                    <pdt:PersonNameTitle>Mr </pdt:PersonNameTitle>
                    <pdt:PersonGivenName>Holmes</pdt:PersonGivenName>
                    <pdt:PersonFamilyName>Sherlock</pdt:PersonFamilyName>
                </common:PersonName>
                <common:OrgName>Bad Company LTD</common:OrgName>
        </Proposal>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我正在尝试提取 XML 标签 命名空间 prefix 部分。 (基本上它在 XML 中的样子。)使用 Python 3.10.3,我尝试了以下的许多变体。


from lxml import html, etree
...

def list_xml_tags(xml_blob):
    print(' [INFO] Printing all XML tags:')
    
    xml = etree.fromstring(bytes(xml_blob, encoding='utf-8'))  
    root = etree.Element("root")
    
    print('Root TAG: {}'.format(root))
    print('nsmap : {}'.format(root.nsmap))
    print('\nDescendants:')
    
    for el in xml.iter():
        el.tag = el.xpath('local-name()')
        #ns = el.xpath('namespace-uri()')
        #ns = etree.QName(el).namespace
        #ns = root.nsmap
        ns = etree.QName(el).namespace
        if el.attrib == None: el.attrib =''
        print('{} : {}  : {}'.format(ns, el.tag, el.attrib))

但是,这不起作用。我根本无法使用它来获取命名空间。 唯一出来的就是None。 (也不确定为什么根标签显示为地址。)

 [INFO] Printing all XML tags:
 ------------------------------------------------------------
Root TAG: <Element root at 0x16a85fc2340>
nsmap : {}

Descendants:
None : Envelope  : {}
None : Body  : {}
None : Proposal  : {}
None : SchemaVersion  : {}
...

问:如何得到下面的输出?

SOAP-ENV : Envelope
pdt      : PersonGivenName
common   : OrgName
...

和python3

from lxml import etree                                  
doc = etree.parse('tmp.xml')
# namespace reverse lookup dict
ns = { value:(key if key is not None else 'default') for (key,value) in set(doc.xpath('//*/namespace::*'))}
for ele in doc.iter():
    qn = etree.QName(ele)
    print(f"{ns[qn.namespace]:>30} : {qn.localname}")

结果:
default前缀的属于默认不带前缀xmlns="http://www.opengis.net/AAA"

的命名空间
       SOAP-ENV : Envelope
       SOAP-ENV : Body
        default : Proposal
        default : SchemaVersion
        default : ApplicationHeader
        default : ApplicationTo
        default : DateSubmitted
        default : Agent
         common : PersonName
            pdt : PersonNameTitle
            pdt : PersonGivenName
            pdt : PersonFamilyName
         common : OrgName