【问题标题】:How To remove an Subement in xml file using python如何使用python删除xml文件中的子元素
【发布时间】:2021-01-11 22:57:12
【问题描述】:

我想删除一个包含所有属于他的子元素的特定元素。 要找到我要删除的元素,我想使用标签的 id 或标签的名称。

例如,给定这个 etree 对象

<?xml version="1.0" ?>
<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
    <tag id="29">
      <name>Moon</name>
    </tag>
</tags>
</root>

例如,我想删除 id 为“29”的 Moon

我想要的输出:

<?xml version="1.0" ?>
<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
 </tags>

</root>

这是我的代码:

def remove_tag(root, tag_id_r):
    i = 0
    for tag in root.iter('tag'):
        tag_id = tag.get('id')
        if (tag_id == tag_id_r):
            #root.clear(tag)
            #root.remove(tag)
            #root[1][i].remove(tag)
        # print(i, tag_id, tag_id_r, root[1][i])
        i += 1

def main():
    with open("lib.xml", 'a') as f:
        tree = etree.parse('lib.xml')
        root = tree.getroot()

        remove_tag(root, input("What is the id of the tag you want to remove?"))

        f.seek(0)
        f.truncate()

        dom = minidom.parseString(etree.tostring(tree, encoding="utf-8"))
        print('\n'.join([line for line in dom.toprettyxml(indent=' '*2).split('\n') if line.strip()]), file=f)
main()

我在 cmets 中尝试了所有方法,但都不起作用

【问题讨论】:

  • 如果您可以使用lxml 包,XSLT 将使这非常简单,无需单个循环。

标签: python xml elementtree


【解决方案1】:

试试这样的:

elems = """<?xml version="1.0" ?>
<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
    <tag id="29">
      <name>Moon</name>
    </tag>
   </tags>
</root>
""" #note that the xml has been fixed

from lxml import etree
doc = etree.XML(elems)
to_del = doc.xpath('//name["Moon"]/parent::tag[@id="29"]')
for td in to_del:
    td.getparent().remove(td)    
print(etree.tostring(doc, pretty_print=True, xml_declaration=True).decode())

输出:

<?xml version='1.0' encoding='ASCII'?>
<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
    </tags>
</root>

【讨论】:

  • 是否有另一种方法来替换“elem”,因为文件夹的内容并不总是相同的。 (对不起,如果我的问题很愚蠢,但这是一个新项目,我在 ElementTree 之前不知道)。
  • @Souheil,但您的示例使用ElementTree(即xml.etree.ElementTree)。也许你的意思是lxml What are the differences between lxml and ElementTree?
【解决方案2】:

要删除 ElementTree 中的元素(这是问题的标记,但没有显示导入),您必须首先获取父元素(在本例中为 tags)。 (lxml 具有 Jack Fleeting 的答案中显示的 .getparent() 方法。)

另外,如果你真的想覆盖它,你不应该打开文件并截断​​它;只需使用 ElementTree 对象的 .write() 方法即可。

示例...

XML 输入(lib.xml;添加“&lt;/tags&gt;”以使其格式正确)

<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
    <tag id="29">
      <name>Moon</name>
    </tag>
  </tags>
</root>

Python

import xml.etree.ElementTree as etree


def remove_tag(root, tag_id_r):
    tags_elem = root.find("tags")
    target_tag = tags_elem.find(f"tag[@id='{tag_id_r}']")
    if target_tag:
        tags_elem.remove(target_tag)
    else:
        print(f"A tag with the id \"{tag_id_r}\" cannot be found.")


def main():
    tree = etree.parse("lib.xml")
    root = tree.getroot()

    remove_tag(root, input("What is the id of the tag you want to remove? "))

    # Overwriting the input file. Are you sure that's a good idea?
    tree.write("lib.xml", encoding="utf-8")


main()

XML 输出(更新 lib.xml)

<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
    </tags>
</root>

【讨论】:

  • 我有几个功能,一个是添加标签,一个是添加文件夹...有时xml文件不存在,所以我必须创建它并尝试它:python with open("lib.xml", 'w') as f: 但它没有不适合我,我不知道为什么。
  • @Souheil - 即使 xml 文件不存在,您也不应该那样创建它。如果它不存在,则创建一个新树而不是解析现有文件。树的序列化(写入文件)不会改变。
【解决方案3】:

另一种方法。

from simplified_scrapy import SimplifiedDoc
html = """
<?xml version="1.0" ?>
<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
    <tag id="29">
      <name>Moon</name>
    </tag>
</root>
"""
doc = SimplifiedDoc(html)
tag29 = doc.select('tag#29')
# Or
# tag29 = doc.getElementByText('Moon',tag='tag')
tag29.remove()
print (doc.html)

结果:

<?xml version="1.0" ?>
<root>
  <tag_folders>
    <folder id="1">Stars</folder>
    <folder id="2">Planet</folder>
    <folder id="3">Satellite</folder>
  </tag_folders>
  <tags>
    <tag>
      <name>Earth</name>
    </tag>
    <tag id="2">
      <name>Sun</name>
    </tag>
</root>

【讨论】:

    猜你喜欢
    • 2022-11-23
    • 1970-01-01
    • 1970-01-01
    • 2022-12-13
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多