在做目标检测的时候,我们获取到图片的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