如何在Python中通过XPath以命名空间无关的方式查找XML元素?

如何在Python中通过XPath以命名空间无关的方式查找XML元素?,python,xml,xpath,lxml,elementtree,Python,Xml,Xpath,Lxml,Elementtree,因为我第二次遇到了这个恼人的问题,我想问一下会有帮助的 有时我必须从XML文档中获取元素,但这样做的方法很笨拙 我想知道一个可以实现我想要的功能的python库,一种制定XPath的优雅方法,一种自动在前缀中注册名称空间的方法,或者在内置XML实现中或在lxml中隐藏首选项以完全剥离名称空间的方法。除非你已经知道我想要什么,否则说明如下:) 示例文档: 我能做什么 据我所知,ElementTreeAPI是唯一一个提供XPath查询的内置API。但它要求我使用“UNames”。看起来是这样的:

因为我第二次遇到了这个恼人的问题,我想问一下会有帮助的

有时我必须从XML文档中获取元素,但这样做的方法很笨拙

我想知道一个可以实现我想要的功能的python库,一种制定XPath的优雅方法,一种自动在前缀中注册名称空间的方法,或者在内置XML实现中或在lxml中隐藏首选项以完全剥离名称空间的方法。除非你已经知道我想要什么,否则说明如下:)

示例文档:


我能做什么 据我所知,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)