如何在Python中通过XPath以命名空间无关的方式查找XML元素?
因为我第二次遇到了这个恼人的问题,我想问一下会有帮助的 有时我必须从XML文档中获取元素,但这样做的方法很笨拙 我想知道一个可以实现我想要的功能的python库,一种制定XPath的优雅方法,一种自动在前缀中注册名称空间的方法,或者在内置XML实现中或在lxml中隐藏首选项以完全剥离名称空间的方法。除非你已经知道我想要什么,否则说明如下:) 示例文档:如何在Python中通过XPath以命名空间无关的方式查找XML元素?,python,xml,xpath,lxml,elementtree,Python,Xml,Xpath,Lxml,Elementtree,因为我第二次遇到了这个恼人的问题,我想问一下会有帮助的 有时我必须从XML文档中获取元素,但这样做的方法很笨拙 我想知道一个可以实现我想要的功能的python库,一种制定XPath的优雅方法,一种自动在前缀中注册名称空间的方法,或者在内置XML实现中或在lxml中隐藏首选项以完全剥离名称空间的方法。除非你已经知道我想要什么,否则说明如下:) 示例文档: 我能做什么 据我所知,ElementTreeAPI是唯一一个提供XPath查询的内置API。但它要求我使用“UNames”。看起来是这样的:
我能做什么
据我所知,ElementTreeAPI是唯一一个提供XPath查询的内置API。但它要求我使用“UNames”。看起来是这样的:/{http://really-long-namespace.uri}根/{http://with-ambivalent.end/#}元素
正如你所看到的,这些都非常冗长。我可以通过执行以下操作缩短它们:
default\u ns=”http://really-long-namespace.uri"
其他"http://with-ambivalent.end/#"
查找(“/{{{0}}}root/{{{{1}}}}elem.”格式(默认值,其他)
但这是{{{{{{{{{}}}和脆弱的,因为http…end/#
≃ <代码>http…结束#≃ <代码>http…end/≃ <代码>http…结束,我是谁知道将使用哪种变体
此外,lxml支持名称空间前缀,但它既不使用文档中的前缀,也不提供自动处理默认名称空间的方法。我仍然需要为每个名称空间获取一个元素才能从文档中检索它。名称空间属性不会被保留,因此也无法从这些属性中自动检索它们
XPath查询也有一种与名称空间无关的方式,但它既冗长又难看,而且在内置实现中不可用:/*[local-name()='root']/*[local-name()='elem']
我想做什么
我想找到一个库、选项或通用XPath变形函数,通过键入以下内容来实现上述示例
/root/elem
/root/other:elem
解决了的 用户samplebias将我的注意力引导到:;正是我想要的。我的实际代码如下所示:
#将文档解析为DOM树
rdf_tree=xml.dom.minidom.parse(“install.rdf”)
#从根节点读取默认命名空间和前缀
context=xpath.XPathContext(rdf_树)
name=context.findvalue(“//em:id”,rdf_树)
version=context.findvalue(“//em:version”,rdf_树)
#继承默认的RDF命名空间
resource\u nodes=context.find(“//说明/以下同级::*”,rdf\u树)
与文档一致、简单、名称空间感知;完美。语法应该可以工作,*[local-name()
我在Ubuntu10.04上使用的是PythonLXML2.2.4,下面的脚本适合我。您需要根据希望为每个元素指定默认名称空间的方式,以及处理要折叠到表达式中的任何其他XPath语法的方式,自定义行为:
import lxml.etree
def xpath_ns(tree, expr):
"Parse a simple expression and prepend namespace wildcards where unspecified."
qual = lambda n: n if not n or ':' in n else '*[local-name() = "%s"]' % n
expr = '/'.join(qual(n) for n in expr.split('/'))
nsmap = dict((k, v) for k, v in tree.nsmap.items() if k)
return tree.xpath(expr, namespaces=nsmap)
doc = '''<root xmlns="http://really-long-namespace.uri"
xmlns:other="http://with-ambivalent.end/#">
<other:elem/>
</root>'''
tree = lxml.etree.fromstring(doc)
print xpath_ns(tree, '/root')
print xpath_ns(tree, '/root/elem')
print xpath_ns(tree, '/root/other:elem')
导入lxml.etree
定义xpath(树,表达式):
“解析简单表达式,并在未指定的位置前置命名空间通配符。”
qual=lambda n:n如果不是n或n-else中的“:”*[local-name()=“%s”]'%n
expr='/'.join(expr.split('/')中n的相等(n)
nsmap=dict((k,v)表示树中的k,v.nsmap.items(),如果k)
return tree.xpath(expr,namespaces=nsmap)
doc='''
'''
tree=lxml.etree.fromstring(doc)
打印xpath(树“/root”)
打印xpath(树“/root/elem”)
打印xpath(树“/root/other:elem”)
输出:
[<Element {http://really-long-namespace.uri}root at 23099f0>]
[<Element {http://with-ambivalent.end/#}elem at 2309a48>]
[<Element {http://with-ambivalent.end/#}elem at 2309a48>]
[]
[]
[]
更新:如果您发现确实需要解析XPath,您可以查看类似于XPath1.0的纯Python实现的项目。至少,这会让您了解解析XPath的复杂性。首先,关于“您想做什么”:
未命名:/root/elem
->我想这里没问题
文档中的名称空间前缀:/root/other:elem
->这有点问题,您不能只使用“文档中的名称空间前缀”。即使在一份文件中:
- 命名空间元素甚至不一定有前缀
- 相同的前缀不一定总是映射到相同的命名空间uri
- 同一命名空间uri不一定总是具有相同的前缀
仅供参考:如果您想获得特定元素范围内的前缀映射,请尝试elem.nsmap
以lxml表示。此外,可以使用lxml.etree中的方法“通知”名称空间声明。我认为您应该阅读,我认为您应该阅读我的问题。您是对的。我仔细阅读了它,似乎您想定义其他语言,而不是更多的XPath,将任何只是NCName测试的名称测试(即/root
)转换为本地名称测试(即/*[local-name()='root']
)和任何QName测试(即/other:elem
)转换为源名称测试(即/*[local-name()='elem'][name>)(名称空间::*[.=名称空间uri(..)='other']
)。但再次强调:这不是XPath。经过一些研究,我发现它不是XPath 1.0,而是XPath 2.0。请参阅和下面的定义。Niether XPath 1.0也不是XPath 2.0。对非限定XPath 2.0表达式使用默认命名空间并不意味着不需要声明此类命名空间URI(包括空命名空间U)