Python读取、修改、保存xml文件
在做目标检测的时候,我们获取到图片的xml文件,我们想利用xml文件来解析得到我们想要的信息。或者我们想要对xml文件做一些修改,下面我将利用Python中自带的xml包来完成这一系列的操作(其实还有一个用于解析HTML的包lxml也可以解析xml文件,也非常好用的,具体使用方法可以参看这篇博客)。参考网站我放在的底部,里面讲得也很详细。本文利用来演示的xml模板结构如下图:
一、读取并解析xml文件
我们主要使用的模块是xml.etree.ElementTree
1、解析xml——获取xml树
import xml.etree.ElementTree as ET file_xml = '/home/g4/桌面/project/xxxx/99.xml' # xml文件路径
tree = ET.parse(file_xml)
type(tree)
xml.etree.ElementTree.ElementTree
这里的tree的对象是ElementTree,从名字也可以知道这个数据结构类似于多叉树,我们可以通过dir()来查看这个类的属性和方法。
dir(tree)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_root', '_setroot', 'find', 'findall', 'findtext', 'getiterator', 'getroot', 'iter', 'iterfind', 'parse', 'write', 'write_c14n']
我们这里可以看到里面有find方法,findall方法,之后会讲到,使用方式。
我们接下来要获取其根节点,以及其他节点的内容。
2、解析xml——获取子节点及其节点内容
获得一棵树之后我们,我们通过tree的getroot()方法来获得整颗树的根结点
root = tree.getroot()
type(root)
xml.etree.ElementTree.Element
dir(root)
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'attrib', 'clear', 'extend', 'find', 'findall', 'findtext', 'get', 'getchildren', 'getiterator', 'insert', 'items', 'iter', 'iterfind', 'itertext', 'keys', 'makeelement', 'remove', 'set', 'tag', 'tail', 'text']
我们可以看到根结点的数据类型是Element,其实这棵树的所有节点数据类型都是Element,下面介绍这些方法和属性
- root.find('xxx'):返回的是一个Element对象,也就是在该节点下提取出叫做‘xxx’这个字节点(如果有多个叫做xxx的子节点,将会返回首个)
- root.findall('xxx'):返回值是一个列表,列表的每个元素是Element,也就是返回该节点下叫做‘xxx’的所有子节点,用list来储存
- root.attrib : 返回该Element所有的“属性”,是一个字典,该节点的“属性”就是<里面id,name这些东西>,一会可以结合示例xml文件,看到具体的返回值。
- root.text: 返回一个字符串,是这个根节点的所包含的内容(也就是<obj>xxxx<obj>中的xxxx)
接下来我们就根据着文章开头的示例xml,来展示一下以上介绍到的方法和属性
path = root.find('path') # 获取root节点(annotation)下的叫做path的这个节点
type(path)
xml.etree.ElementTree.Element
root.attrib # 获取annotation节点的属性(包含有两个属性一个是name,一个是id)
{'name': 'Panama', 'id': '1234'}
path.text # 获取path节点的内容
'百度'
root.findall('object')
[<Element 'object' at 0x7fd9adcec110>, <Element 'object' at 0x7fd9adcecbf0>]
可以看到第一行中我们获取了root的字节点path,path也是一个Element的类别,因此它也是有上面提到的那些方法和属性的。
我们可以看一下最后一行命令,返回的这个列表,里面储存的元素显示的是节点的内存地址。我们前面提到tree也是有findall方法的,其实如果使用tree的findall('object')得到的结果也是一样的。
tree.findall('object')
[<Element 'object' at 0x7fd9adcec110>, <Element 'object' at 0x7fd9adcecbf0>]
我们可以看到内存地址也是一样的,因此这两种方式来搜索得到子节点是一模一样的。
二、修改xml文件
我们已经可以提取xml中的信息了,接下来我们可以修改获取到的xml文件里的信息。
1、修改节点内容
如果要修改节点的内容我们可以直接使用Element.text = 'xxxx',就就可以完成修改了。
path.text = '修改后' path = root.find('path') path.text
'修改后'
上面的例子显示path节点的text已经从原来的‘百度’变成了‘修改后’,并且在重新从root中获取path节点,也是显示修改之后的结果。因此修改节点内容是非常方简单的。
2、修改节点属性
新增节点属性。Element.set(新属性名,新值)
root.set('sex', '男')
root.attrib
{'name': 'Panama', 'id': '1234', 'sex': '男'}
修改节点的属性,也是使用Element中的set方法。Element.set(待修改的属性名,新值)
root.set('id', '4321')
root.attrib
{'name': 'Panama', 'id': '4321', 'sex': '男'}
可以看到root的id这个属性已经被修改成了4321。
(删除属性值,我还没找到对应的方法。。。。)
3、删除和增加子节点
如果要在一个Element下新一个子节点,我们采用Element.append(childElement)的方式。
path = root.find('path')
path.findall('object')
[]
obj = root.find('object')
path.append(obj)
path.findall('object')
[<Element 'object' at 0x7fd9adcec110>]
可以看到在path下本来是没有object这个子节点的,但是在append之后就有了(要注意的是我们append只有是Element对象)
删除一个子节点采用的是Element.remove(childElement)的方式。
path.remove(obj)
path.findall('object')
[]
也要注意的是参数只能是Element对象并且还得是同一个内存。
obj = root.findall('object')[1]
path.remove(obj)
ValueError: list.remove(x): x not in list
如果我们删除的是另一个obj对象(这里会报错的),原因是path的子节点的并不是我们新创建的这个obj。
三、保存xml文件
对于我们已经修改完成的xml,以上改了属性,增加了子节点,删除了字节点,把操作后的tree保存成新的xml文件。采用
import xml.etree.ElementTree as ET
file_xml = '/home/g4/桌面/project/安全帽100/99.xml'
tree = ET.parse(file_xml) # 读取tree
root = tree.getroot()
path = root.find('path')
obj = root.find('object')
path.append(obj) # 在path子节点下增加一个子节点
root.remove(obj) # 在root节点下删除一个子节点
new_tree = ET.ElementTree(root) # root为修改后的root
new_tree.write("test.xml", encoding='utf-8') # 保存为xml文件
最主要的保存操作是最后两行,这里由于存在中文,因此传入参数encoding=‘utf-8’。
看看最后结果。
参考链接:
https://blog.csdn.net/weixin_42782150/article/details/106219001