ElementTree 查找特定 XML 元素

76 阅读1分钟

我想使用 Python 的 ElementTree 来处理一个 XML 文件。我需要在其中找到所有 Instance 元素,条件是其 UserValue 的 title 属性值为 THIRD 且 value 属性值不为空。例如,在这个 XML 文件中,结果应该是 marble 和 mouse。

huake_00257_.jpg

<?xml version="1.0" encoding="utf-8"?>
<Data>
    <Instance id="61" name="atom">
        <UserData id="30">
            <UserValue value="" title="FIRST"></UserValue>
            <UserValue value="" title="SECOND"></UserValue>
            <UserValue value="" title="THIRD"></UserValue>
            <UserValue value="watch" title="FOURTH"></UserValue>
        </UserData>
    </Instance>
    <Instance id="64" name="marble" ref="33">
        <UserData id="34">
            <UserValue value="" title="FIRST"></UserValue>
            <UserValue value="stuff" title="SECOND"></UserValue>
            <UserValue value="airplane" title="THIRD"></UserValue>
            <UserValue value="" title="FOURTH"></UserValue>
        </UserData>
    </Instance>
    <Instance id="65" name="rock">
        <UserData id="36">
            <UserValue value="" title="FIRST"></UserValue>
            <UserValue value="" title="SECOND"></UserValue>
            <UserValue value="" title="THIRD"></UserValue>
            <UserValue value="" title="FOURTH"></UserValue>
        </UserData>     
    </Instance>
    <Instance id="66" name="mouse">
        <UserData id="38">
            <UserValue value="" title="FIRST"></UserValue>
            <UserValue value="" title="SECOND"></UserValue>
            <UserValue value="rocket" title="THIRD"></UserValue>
            <UserValue value="" title="FOURTH"></UserValue>
        </UserData>     
    </Instance>
</Data>

我使用 ElementTree 写了一段代码来查找这些元素,如下所示:

import xml.etree.ElementTree as xml

tree = xml.parse("example.xml")

for node in tree.iter('Instance'):

    name = node.get('name')

    for col in node.iter('UserValue'):
        title = col.attrib.get('title')
        value = col.attrib.get('value')

        if (title == "THIRD" and value != ""):
            print("     name =", name)

代码可以正常运行,并打印出 marble 和 mouse。

解决方案

上面提到的代码可以工作,但是它并不是最优的解决方案。ElementTree 在处理大文件时可能会遇到内存问题,因为它是将整个 XML 文件加载到内存中来处理的。

为了解决这个问题,我推荐使用 lxml。lxml 是一个更强大的 XML 库,它可以逐行处理 XML 文件,而不需要将整个文件加载到内存中。

可以使用 lxml 的 xpath 表达式来查找 Instance 元素,如下所示:

import lxml.etree

root = lxml.etree.parse("example.xml")
for instance in root.xpath('//Instance[descendant::UserValue[@title = "THIRD"][@value != ""]]'):
    print(instance.get('name'))

这段代码会打印出 marble 和 mouse。

如果这段代码仍然占用太多内存,可以尝试使用 lxml 的事件解析器,如下所示:

import lxml.etree

class InstanceNamePrinter(object):
    def start(self, tag, attrib):
        if tag == 'Instance':
            self.name = attrib['name']
        elif tag == 'UserValue':
            if attrib['title'] == 'THIRD' and attrib['value'] != '':
                print(self.name)
    def close(self):
        pass

with open('example.xml') as xml:
    parser = lxml.etree.XMLParser(target=InstanceNamePrinter())
    lxml.etree.parse(xml, parser)

这段代码会逐行解析 XML 文件,并打印出 marble 和 mouse。