python xml 查询获取父节点

1 投票
3 回答
15882 浏览
提问于 2025-04-16 14:06

我有一个很大的xml文档,长得像这样:

<Node name="foo">
    <Node name="16764764625">
        <Val name="type"><s>3</s></Val>
        <Val name="owner"><s>1</s></Val>
        <Val name="location"><s>4</s></Val>
        <Val name="brb"><n/></Val>
        <Val name="number"><f>24856</f></Val>
        <Val name="number2"><f>97000.0</f></Val>
    </Node>
    <Node name="1764466544">
        <Val name="type"><s>1</s></Val>
        <Val name="owner"><s>2</s></Val>
        <Val name="location"><s>6</s></Val>
        <Val name="brb"><n/></Val>
        <Val name="number"><f>265456</f></Val>
        <Val name="number2"><f>99000.0</f></Val>
    </Node>
    ...
</Node>

我的任务是通过搜索,找到父节点的值:1764466544(第二个节点中name的值),这个父节点的子元素Val的name属性是"number",并且它的值包含265456。

我读了很多关于XPath和ElementTree的资料,但我还是不知道从哪里开始进行查询。我在找例子……但是找不到任何提到父节点作为结果的例子。

我对python还很陌生……任何建议都很受欢迎。

谢谢

3 个回答

0

下面这个函数在类似的情况下对我很有帮助。正如文档说明的那样,它并不适用于所有情况,但如果你的节点是唯一的,它应该能帮上忙。

def get_element_ancestry(root, element):
'''Return a list of ancestor Elements for the given element.

If both root and element are of type xml.etree.ElementTree.Element, and if
the given root contains the given element as a descendent, then return a
list of direct xml.etree.ElementTree.Element ancestors, starting with root
and ending with element. Otherwise, return an empty list.

The xml.etree.ElementTree module offers no function to return the parent of
a given Element, presumably because an Element may be in more than one tree,
or even multiple times within a given tree, so its parent depends on the
context. This function provides a solution in the specific cases where the
caller either knows that the given element appears just once within the
tree or is satisfied with the first branch to reference the given element.
'''
result = []
xet = xml.etree.ElementTree
if not xet.iselement(root) or not xet.iselement(element):
    return result
xpath = './/' + element.tag \
    + ''.join(["[@%s='%s']" % a for a in element.items()])
parent = root
while parent != None:
    result.append(parent)
    for child in parent.findall('*'):
        if child == element:
            result.append(element)
            return result
        if child.findall(xpath).count(element):
            parent = child
            break
    else:
        return []
return result
4

很遗憾,在使用ElementTree这个工具时,每个Element对象并没有指向它父级的链接,所以你不能从一个已知的点向上查找父节点。相反,你需要找到可能的父节点,然后筛选出你想要的。

通常,这个过程是通过XPath表达式来完成的。不过,ElementTree只支持一部分XPath的功能(查看文档),而且最有用的部分是在ElementTree 1.3版本中才添加的,这个版本只在Python 2.7以上或3.2以上的版本中提供。

而且,即使是ElementTree的XPath也不能直接处理你的文件——你无法根据节点的文本来选择,只能根据它的属性(或者属性值)来选择。

我实验后发现,使用ElementTree有两种方法。如果你在使用Python 2.7以上的版本(或者能够下载并安装更新版本的ElementTree来兼容旧版Python),并且你可以修改XML文件的格式,把数字放成属性,像这样:

<Val name="number"><f val="265456" /></Val>

那么接下来的Python代码就能提取出你感兴趣的节点:

import xml.etree.ElementTree as ETree
tree = ETree.ElementTree(file='sample.xml')
nodes = tree.findall(".//Node/Val[@name='number']/f[@val='265456']....")

对于旧版Python,或者如果你不能修改XML格式,你就得手动筛选无效的节点。以下的方法对我有效:

import xml.etree.ElementTree as ETree
tree = ETree.ElementTree(file='sample.xml')
all = tree.findall(".//Node")
nodes = []

# Filter matching nodes and put them in the nodes variable.
for node in all:
    for val in node.getchildren():
        if val.attrib['name'] == 'number' and val.getchildren()[0].text =='265456':
            nodes.append(node)

这两种解决方案都不是我认为理想的,但这是我能在ElementTree库中找到的唯一可行的方法(因为你提到过要使用这个库)。你可能会发现使用第三方库比使用内置的库更好;可以查看Python关于XML的维基页面,上面有一些选项的列表。lxml是一个流行的libxml2库的Python绑定,我建议你优先考虑这个。它支持XPath,所以你应该能够使用其他答案中的查询。

3

这个XPath:

/Node/Node[Val[@name='number']/f='265456']/@name

输出结果是:

1764466544

撰写回答