带有 xpath 和带有前缀的命名空间的 python etree

Ark*_*ady 5 python prefix elementtree xml-namespaces

我找不到信息,如何使用命名空间解析我的 XML:

我有这个xml:

<par:Request xmlns:par="http://somewhere.net/actual">
  <par:actual>blabla</par:actual>
  <par:documentType>string</par:documentType>
</par:Request>
Run Code Online (Sandbox Code Playgroud)

并试图解析它:

dom = ET.parse(u'C:\\filepath\\1.xml')
rootxml = dom.getroot()
for subtag in rootxml.xpath(u'//par:actual'):
    #do something
    print(subtag)
Run Code Online (Sandbox Code Playgroud)

并且得到了异常,因为它不知道命名空间前缀。有没有最好的方法来解决这个问题,计算那个脚本不会知道它要解析的文件和标签要搜索的文件?

搜索网络和 stackoverflow 我发现,如果我会在那里添加:

namespace = {u'par': u"http://somewhere.net/actual"}
for subtag in rootxml.xpath(u'//par:actual', namespaces=namespace):
    #do something
    print(subtag)
Run Code Online (Sandbox Code Playgroud)

那个有效。完美的。但是我不知道我将解析哪个 XML,并且//par:actual我的脚本也不知道搜索标记(例如)。所以,我需要找到从 XML 中提取命名空间的方法。

找了很多方法,如何提取namespace URI,比如:

print(rootxml.tag)
print(rootxml.xpath('namespace-uri(.)'))
print(rootxml.xpath('namespace-uri(/*)'))
Run Code Online (Sandbox Code Playgroud)

但是我应该如何提取前缀来创建 ElementTree 想要的字典?我不想在 xml 正文上使用正则表达式怪物来提取前缀,我相信必须存在支持的方式,不是吗?

也许必须存在一些方法让我通过 ETree 命名空间从 XML 中提取作为字典(如 ETree 想要的!)而无需手动操作?

Ark*_*ady 5

哦,我找到了。

在我们这样做之后:

dom = ET.parse(u'C:\\filepath\\1.xml')
rootxml = dom.getroot()
Run Code Online (Sandbox Code Playgroud)

对象 rootxml 包含字典 nsmap,其中包含我想要的所有命名空间。

所以,我找到了最简单的解决方案:

dom = ET.parse(u'C:\\filepath\\1.xml')
rootxml = dom.getroot()
nss = rootxml.nsmap
for subtag in rootxml.xpath(u'//par:actual', namespaces=nss):
    #do something
    print(subtag)
Run Code Online (Sandbox Code Playgroud)

那个有效。

UPD:如果用户理解他所使用的 XML 中的“par”是什么意思,那就行了。例如,在任何其他操作之前将假定的命名空间与现有的命名空间进行比较。

尽管如此,我还是喜欢 XPath 的许多变体,它理解 {...}actual,这就是我试图实现的目标。

  • 我未能使 .nsmap 与 py3.6 ` Traceback 一起工作(最近一次调用最后一次):文件“ElementTree_Xpath_xmlns_namespace.py”,第 61 行,在 &lt;module&gt; nss = root.nsmap AttributeError: 'xml.etree.ElementTree.Element ' 对象没有属性 'nsmap' ` (3认同)

Ste*_*ven 5

您不能依赖根元素上的命名空间声明:不能保证声明甚至会在那里,或者文档将始终具有相同命名空间的相同前缀。假设您将有某种方式传递您想要搜索的标签(因为您说您的脚本不知道它),您还应该提供一种传递命名空间映射的方式。或者使用 James Clark 表示法,例如{http://somewhere.net/actual}actualETXPath支持此语法,而“普通”xpath 不支持,但您也可以使用其他方法,例如.findall()如果您不需要完整的 xpath)

如果您根本不关心前缀,您也可以local-name()在 xpath 中使用该函数,例如。//*[local-name()="actual"](但你不会“真的”确定它是正确的“实际”)