如何使用Python从XML中移除元素

16 投票
3 回答
60498 浏览
提问于 2025-04-16 03:26

我在处理XML和Python的时候遇到了困难。这个任务其实很简单,但我到现在还没解决,花了很长时间。我来这里寻求一些建议,希望能用几行代码解决这个问题。

感谢任何关于遍历树的帮助。我总是得到太多或太少的元素。元素可以无限嵌套。给出的例子只是一个示例。我对解决方案没有特别的要求,不管是用dom、minidom、sax,随便什么都可以。

我有一个类似这样的XML文件:

<root>
    <elm>
        <elm>Common content</elm>

        <elm xmlns="http://example.org/ns">
            <elm lang="en">Content EN</elm>
            <elm lang="cs">žluťoučký koníček</elm>
        </elm>

        <elm xml:id="abc123">Common content</elm>

        <elm lang="en">Content EN</elm>
        <elm lang="cs">Content CS</elm>

        <elm lang="en">
            <elm>Content EN</elm>
            <elm>Content EN</elm>
        </elm>

        <elm lang="cs">
            <elm>Content CS</elm>
            <elm>Content CS</elm>
        </elm>
    </elm>
</root>

我需要做的是解析这个XML并写一个新文件。新文件应该包含给定语言的所有元素,以及没有lang属性的元素。

对于“cs”语言,输出文件应该包含这些内容:

<root>
    <elm>
        <elm>Common content</elm>

        <elm xmlns="http://example.org/ns">
            <elm lang="cs">žluťoučký koníček</elm>
        </elm>

        <elm xml:id="abc123">Common content</elm>

        <elm lang="cs">Content CS</elm>

        <elm lang="cs">
            <elm>Content CS</elm>
            <elm>Content CS</elm>
        </elm>
    </elm>
</root>

如果你能让新文件省略lang属性,那就更好了。但这不是特别重要。

更新1:添加了unicode字符和命名空间属性。

更新2:使用Python 2.5,优先考虑标准库。

3 个回答

3

更新了@Alex Martelli的代码,修复了一个错误:在原地更新元素列表。如果输入稍微复杂一点,上面的解决方案会给出错误的答案。

import sys
from xml.etree import cElementTree as et

def picklang(path, lang='en'):
    tr = et.parse(path)
    for element in tr.iter():
        for subelement in element[:]:
            la = subelement.get('lang')

            if la is not None and la != lang:
                element.remove(subelement)
    return tr

if __name__ == '__main__':
    tr = picklang('la.xml')
    tr.write(sys.stdout)
    print

第7行的代码 for subelement in element: 被改成了 for subelement in element[:]:,因为在遍历列表时直接更新列表是不正确的。

这段代码是遍历元素列表的一个副本,并在原始元素列表中删除那些语言不是“en”的元素。

6

我不太确定怎么最好地去掉 lang 属性,不过这里有一些代码可以做其他的修改(这是针对 Python 2.7 的;如果你用的是 2.5 或 2.6,就用 getIterator 代替 iter)。假设你在删除一个元素的时候,也总是想把这个元素里面的所有东西都删掉。

这段代码只是把结果打印到标准输出(当然,你可以根据需要重定向输出,或者直接写入一个新文件等等):

import sys
from xml.etree import cElementTree as et

def picklang(path, lang='en'):
    tr = et.parse(path)
    for element in tr.iter():
        for subelement in element:
            la = subelement.get('lang')
            if la is not None and la != lang:
                element.remove(subelement)
    return tr

if __name__ == '__main__':
    tr = picklang('la.xml')
    tr.write(sys.stdout)
    print

假设 la.xml 是你的示例,这段代码会输出:

<root>
    <elm>Common content</elm>

    <elm>
        <elm lang="en">Content EN</elm>
        </elm>

    <elm>Common content</elm>

    <elm lang="en">Content EN</elm>
    <elm lang="en">
        <elm>Content EN</elm>
        <elm>Content EN</elm>
    </elm>

    </root>
15

使用 lxml 库:

import lxml.etree as le

with open('doc.xml','r') as f:
    doc=le.parse(f)
    for elem in doc.xpath('//*[attribute::lang]'):
        if elem.attrib['lang']=='en':
            elem.attrib.pop('lang')
        else:
            parent=elem.getparent()
            parent.remove(elem)
    print(le.tostring(doc))

会得到

<root>
    <elm>Common content</elm>

    <elm>
        <elm>Content EN</elm>
        </elm>

    <elm>Common content</elm>

    <elm>Content EN</elm>
    <elm>
        <elm>Content EN</elm>
        <elm>Content EN</elm>
    </elm>

    </root>

撰写回答