python模块 xml--操作文件xml
1、xml包中的四个子包
- Python的核心XML支持。
- xml包的说明文档:https://docs.python.org/zh-cn/3.7/library/xml.html
1、dom包
- Document Object Model。适合用于处理DOM API。它能够将XML数据在内存中解析成一个树,然后通过对树的操作来操作XML。但是这种方式由于将XML数据映射到内存中的树,导致比较慢,且消耗更多内存。
- DOM的解析器在解析一个XML文档时,一次性读取整个文档,如果要处理的XML文本比较大的话,就会很耗内存,所以DOM一般偏向于处理一些小的XML,(如配置文件)比较快。
- dom中的模块
- xml.dom:DOM API定义
- xml.dom.minidom:最小的DOM实现
- xml.dom.pulldom:支持构建部分DOM树
2、SAX(simple API for XML)
- Python标准库中包含SAX解析器,SAX是用的是事件驱动模型,通过在解析XML过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。
- 解析的基本过程:读到一个XML开始标签,就会开始一个事件,然后事件就会调用一系列的函数去处理一些事情,当读到一个结束标签时,就会触发另一个事件。所以,我们写XML文档入如果有格式错误的话,解析就会出错。
- 由于SAX以流式读取XML文件,一边读一边解析,速度较快,占用内存少,但是在操作上稍微复杂,需要用户实现回调函数。
- 适用场景如下:
- 1、对大型xml文件进行处理;
- 2、只需要文件的部分内容,或者只需从文件中得到特定信息。
- 3、想建立自己的对象模型的时候。
3、parsers包
- 用于XML解析器的Python包装器(目前只支持Expat)。
- xml.parsers.expat:Expat解析器绑定
4、etree包
- ElementTree XML库。这是完整ElementTree XML版本的一个子集。
- xml.etree.ElementTree: ElementTree API,一个简单而轻量级的XML处理器
示例:xml文件
<?xml version="1.0"?> <data> <country> <rank>1</rank> <year>2008</year> <gdppc>141100</gdppc> <h1> <year1h>20081h</year1h> <gdppc1h>1411001h</gdppc1h> </h1> <neighbor name="Austria" direction="E"/> <neighbor name="Switzerland" direction="W"/> </country> <country name="Singapore"> <rank>4</rank> <year>2011</year> <gdppc>59900</gdppc> <neighbor name="Malaysia" direction="N"/> </country> <country name="Panama"> <rank>68</rank> <year>2011</year> <gdppc>13600</gdppc> <h2> <year2h>20082h</year2h> <gdppc2h>1411002h</gdppc2h> </h2> <neighbor name="Costa Rica" direction="W"/> <neighbor name="Colombia" direction="E"/> </country> </data>
2、etree包解析xml
- xml.etree.ElementTree模块实现了一个简单高效的API,用于解析和创建XML数据。
- xml.etree.ElementTree模块对于恶意构建的数据是不安全的。如果需要解析不可信或未经身份验证的数据,请参见XML漏洞。
1、XML树和元素
- XML是一种固有的分层数据格式,最自然的表示方法是使用树。为此,ET有两个类:ElementTree将整个XML文档表示为一个树,Element表示该树中的单个节点。与整个文档的交互(读写文件)通常在ElementTree级别完成。与单个XML元素及其子元素的交互是在Element级别完成的。
2、元素对象(元素--标签)
1、class xml.etree.ElementTree.Element(tag, attrib={}, **extra)
- Element类定义了Element接口,并提供了该接口的参考实现。
- 元素名、属性名和属性值可以是字节串,也可以是Unicode字符串。Tag是元素名。attrib是一个可选的字典,包含元素属性。extra包含作为关键字参数的额外属性。
2、tag:元素名
- 一个标识此元素意味着何种数据的字符串(换句话说,元素类型)。
3、text:元素值
4、tail
- 这些属性可用于保存与元素关联的其他数据。它们的值通常是字符串,但也可以是任何特定于应用程序的对象。如果元素是从XML文件创建的,则text属性保存元素的开始标记和它的第一个子标记或结束标记之间的文本,或者None, tail属性保存元素的结束标记和下一个标记之间的文本,或者None。
5、attrib:元素属性
- 包含元素属性的字典。请注意,虽然attrib值总是一个真正的可变Python字典,但ElementTree实现可能选择使用另一种内部表示,并仅在有人要求时创建字典。要利用这些实现,请尽可能使用下面的字典方法。
- clear()
- 重置一个元素。此函数删除所有子元素,清除所有属性,并将文本和尾部属性设置为None。
- get(key, default=None)
- 获取名为key的元素属性。返回属性值,如果未找到该属性,则返回默认值。
- items()
- 以(名称、值)对序列的形式返回元素属性。属性以任意顺序返回。
- keys()
- 以列表形式返回元素属性名称。名称以任意顺序返回。
- set(key, value)
- 将元素上的属性key设置为value。
6、以下方法处理元素的子元素。
- append(subelement)
- 将元素子元素添加到此元素内部子元素列表的末尾。如果subelement不是Element则引发TypeError。
- extend(subelements)
- 从序列对象中添加零个或多个元素的子元素。如果子元素不是Element则引发TypeError。3.2 新版功能.
- find(match, namespaces=None)
- 查找第一个匹配的子元素。match可以是标记(tag)名或路径。返回一个元素实例或None。namespaces(名称空间)是从名称空间前缀到全名的可选映射。传递''作为前缀将表达式中所有不带前缀的标记名称移动到给定的命名空间中。
- findall(match, namespaces=None)
- 根据标记(tag)名称或路径查找所有匹配的子元素。返回一个按文档顺序包含所有匹配元素的列表。名称空间是从名称空间前缀到全名的可选映射。传递''作为前缀将表达式中所有不带前缀的标记名称移动到给定的命名空间中。
- findtext(match, default=None, namespaces=None)
- 查找第一个匹配子元素的文本。match可以是标记(tag)名或路径。返回第一个匹配元素的文本内容,如果没有找到元素则返回默认值。注意,如果匹配元素没有文本内容,则返回一个空字符串。namespaces(名称空间)是从名称空间前缀到全名的可选映射。传递''作为前缀将表达式中所有不带前缀的标记名称移动到给定的命名空间中。
- getchildren()
- 3.2版后已移除,将在3.9版中移除:使用list(elem)或iteration。
- getiterator(tag=None)
- 3.2版后已移除,将在3.9版中移除:请使用Element.iter()方法代替..
- insert(index, subelement)
- 在元素的给定位置插入子元素。如果subelement不是Element则引发TypeError
- iter(tag=None)
- 创建一个以当前元素为根的树迭代器。迭代器按照文档顺序(深度优先)遍历该元素及其下面的所有元素。如果tag不是None或'*',则迭代器只返回tag等于tag的元素。如果在迭代过程中修改了树结构,那么结果是未定义的。3.2 新版功能.
- iterfind(match, namespaces=None)
- 根据标记名称或路径查找所有匹配的子元素。返回一个可迭代对象,按文档顺序生成所有匹配的元素。namespaces是从名称空间前缀到全名的可选映射。3.2 新版功能.
- itertext()
- 创建一个文本迭代器。迭代器按文档顺序遍历该元素和所有子元素,并返回所有内部文本。3.2 新版功能.
- makeelement(tag, attrib)
- 创建与此元素相同类型的新元素对象。不要调用此方法,而是使用SubElement()工厂函数。
- remove(subelement)
- 从元素中移除子元素。与find方法不同,该方法是根据实例标识来比较元素,而不是根据标记值或内容。
- 元素对象还支持以下用于处理子元素的序列类型方法:__delitem__(), __getitem__(), __setitem__(), __len__()。
3、ElementTree对象
1、class xml.etree.ElementTree.ElementTree(element=None, file=None)
- ElementTree类表示整个元素层次结构,并为与标准XML之间的序列化添加了一些额外的支持。
- element(元素)是根元素。如果给定,则用XML文件的内容初始化树。
2、_setroot(element)
- 这将丢弃树的当前内容,并用给定的元素替换它。小心使用。element(元素)是一个元素实例。
3、parse(source, parser=None)
- 将外部XML文件加载到元素树中。source是一个文件名或文件对象。Parser是一个可选的解析器实例。如果没有给出,则使用标准XMLParser解析器。返回一个ElementTree实例。
4、getroot()
- 返回此树的根元素。
5、find(match, namespaces=None)
- 与Element.find()相同,从树的根开始。
6、findall(match, namespaces=None)
- 与Element.findall()相同,从树的根开始。
7、findtext(match, default=None, namespaces=None)
- 与Element.findtext()相同,从树的根开始。
8、getiterator(tag=None)
- 3.2版后已移除,将在3.9版中移除:请使用ElementTree.iter()方法代替。
9、iter(tag=None)
- 为根元素创建并返回一个树迭代器。迭代器按照节顺序遍历树中的所有元素。tag(标签)是要查找的标签(默认是返回所有元素)。
10、iterfind(match, namespaces=None)
- 与Element.iterfind()相同,从树的根开始。
11、write(file, encoding="us-ascii", xml_declaration=None, default_namespace=None, method="xml", *, short_empty_elements=True)
- 将元素树作为XML写入文件。
- file是一个文件名,或一个为写入而打开的文件对象。
- encoding是输出编码(默认为US-ASCII)。
- xml_declaration控制是否应该将XML声明添加到文件中。如果不是US-ASCII或UTF-8或Unicode,则使用False表示从不,True表示总是,None仅表示None(默认为None)。
- default_namespace设置默认的XML名称空间(对于“xmlns”)。
- method是“xml”,“html”或“text”(默认是“xml”)。
- 仅限关键字的short_empty_elements参数控制不包含内容的元素的格式。
- 如果为True(默认值),则它们作为单个自闭标记发出,否则它们作为一对开始/结束标记发出。
4、遍历xml文件
1、使用递归方式遍历xml文件
import xml.etree.ElementTree as ET def xmlread(root_node, level): note = [level, root_node.tag, root_node.attrib, root_node.text] nodelst.append(note) child_node = list(root_node) #返回元素的子元素的列表,例如下面的就是data元素的子元素的列表 #print(child_node) #结果是:[<Element 'country' at 0x000001B53D1C61D0>, <Element 'country' at 0x000001B53D1E9360>, <Element 'country' at 0x000001B53D1E94F0>] if len(child_node) == 0: return for child in child_node: xmlread(child, level + 1) tree = ET.parse("xmltest.xml") #返回ElementTree实例 root = tree.getroot() #返回xml的根元素,通过根节点向下开始读取各个元素(element对象)。 #print(root) #结果是:<Element 'data' at 0x0000024CD0FF70E0> nodelst = [] #存放所有的元素的列表node level = 1 #层数 xmlread(root, level) for i in nodelst: print(i) <<< [1, 'data', {}, '\n '] [2, 'country', {}, '\n '] [3, 'rank', {}, '1'] [3, 'year', {}, '2008'] [3, 'gdppc', {}, '141100'] [3, 'h1', {}, '\n '] [4, 'year1h', {}, '20081h'] [4, 'gdppc1h', {}, '1411001h'] [3, 'neighbor', {'name': 'Austria', 'direction': 'E'}, None] [3, 'neighbor', {'name': 'Switzerland', 'direction': 'W'}, None] [2, 'country', {'name': 'Singapore'}, '\n '] [3, 'rank', {}, '4'] [3, 'year', {}, '2011'] [3, 'gdppc', {}, '59900'] [3, 'neighbor', {'name': 'Malaysia', 'direction': 'N'}, None] [2, 'country', {'name': 'Panama'}, '\n '] [3, 'rank', {}, '68'] [3, 'year', {}, '2011'] [3, 'gdppc', {}, '13600'] [3, 'h2', {}, '\n '] [4, 'year2h', {}, '20082h'] [4, 'gdppc2h', {}, '1411002h'] [3, 'neighbor', {'name': 'Costa Rica', 'direction': 'W'}, None] [3, 'neighbor', {'name': 'Colombia', 'direction': 'E'}, None]
2、通过索引访问特定的子级节点
- 层数:从零开始计数的
- 深度:每加一层,就加一个索引。
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() print(root) #根元素 print(root[1].tag, root[1].attrib) #第二层的元素名、元素属性 print(root[0][0].tag, root[0][0].text) #第一层的第一个元素名、元素值 print(root[0][3][1].tag, root[0][3][1].text) #第一层中的第四层的第二个元素名、元素值 <<< <Element 'data' at 0x00000215968E6130> country {'name': 'Singapore'} rank 1 gdppc1h 1411001h
3、使用iter根据元素名进行深度优先遍历节点
1、使用iter根据元素名进行深度优先遍历特点节点。
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() for node in root.iter('year'): print([node.tag, node.attrib, node.text]) <<< ['year', {}, '2008'] ['year', {}, '2011'] ['year', {}, '2011']
2、使用iter进行深度优先遍历所有节点
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() for node in root.iter(): print([node.tag, node.attrib, node.text]) <<< ['data', {}, '\n '] ['country', {}, '\n '] ['rank', {}, '1'] ['year', {}, '2008'] ['gdppc', {}, '141100'] ['h1', {}, '\n '] ['year1h', {}, '20081h'] ['gdppc1h', {}, '1411001h'] ['neighbor', {'name': 'Austria', 'direction': 'E'}, None] ['neighbor', {'name': 'Switzerland', 'direction': 'W'}, None] ['country', {'name': 'Singapore'}, '\n '] ['rank', {}, '4'] ['year', {}, '2011'] ['gdppc', {}, '59900'] ['neighbor', {'name': 'Malaysia', 'direction': 'N'}, None] ['country', {'name': 'Panama'}, '\n '] ['rank', {}, '68'] ['year', {}, '2011'] ['gdppc', {}, '13600'] ['h2', {}, '\n '] ['year2h', {}, '20082h'] ['gdppc2h', {}, '1411002h'] ['neighbor', {'name': 'Costa Rica', 'direction': 'W'}, None] ['neighbor', {'name': 'Colombia', 'direction': 'E'}, None]
5、编辑xml(增删改查)
1、查找感兴趣的元素
1、iter()
- 根据元素名进行深度优先遍历特点节点。
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() for node in root.iter('year'): print([node.tag, node.attrib, node.text]) <<< ['year', {}, '2008'] ['year', {}, '2011'] ['year', {}, '2011']
2、findall()
- findall()仅查找当前元素的直接子元素中带有指定标签的元素。
- find() 查找当前元素带有特定标签的第一个 子级,并返回子级中的元素。
- 可以用 Element.text 访问元素的文本内容。 Element.get 访问元素的属性
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() print(list(root.findall('country'))) print(list(root.find('country'))) <<< [<Element 'country' at 0x000002D41EA66180>, <Element 'country' at 0x000002D41EA88310>, <Element 'country' at 0x000002D41EA884A0>] [<Element 'rank' at 0x000002D41EA88090>, <Element 'year' at 0x000002D41EA880E0>, <Element 'gdppc' at 0x000002D41EA88130>, <Element 'h1' at 0x000002D41EA88180>, <Element 'neighbor' at 0x000002D41EA88270>, <Element 'neighbor' at 0x000002D41EA882C0>]
2、删除节点
- del删除当前元素索引指定的子级。
import xml.etree.ElementTree as ET tree = ET.parse('xmltest.xml') root = tree.getroot() del root[0] #这仅仅是在内存中删除了,xml文件中的内容没有改变 for i in root: print([i.tag, i.attrib, i.text]) tree.write('xmltest.xml') #将内存中的数据写入xml文件中,此时xml文件中的内容才发生改变 <<< #第一个country节点全部删除 ['country', {'name': 'Singapore'}, '\n '] ['country', {'name': 'Panama'}, '\n ']
- 使用remove删除节点,只能删除当前节点的子节点
示例1:
import xml.etree.ElementTree as ET tree = ET.parse('xmltest.xml') root = tree.getroot() for i in root.findall('country'): #找到所用的country节点 if i.find('year').text == '2008': #country节点中有year子节点,且year元素的值是‘2008’,就删除此country节点 root.remove(i) #当前节点是root,子节点是i tree.write('xmltest.xml')
示例2:
import xml.etree.ElementTree as ET tree = ET.parse('xmltest.xml') root = tree.getroot() for i in root.findall('country'): print(i) for y in i.iter('year'): print(y) if i.find('year').text == '2008': i.remove(y) #只能删除当前节点的子节点。当前节点是i,子节点是y tree.write('xmltest.xml')
3、修改XML文件
- 使用 Element.text 修改文本字段。
- 使用 Element.set() 方法添加和修改属性。
- 使用 Element.append() 添加新的子元素。将元素子元素添加到此元素内部子元素列表的末尾。如果subelement不是Element则引发TypeError。
1、修改现有的节点
import xml.etree.ElementTree as ET tree = ET.parse('xmltest.xml') root = tree.getroot() for node in root.iter('year'): node.text = '2012' #将元素名位year的值修改为2012 node.set('末日','到了') #给year元素添加/修改属性,<year 末日="到了">2012</year> tree.write('xmltest.xml',encoding='UTF-8')
2、使用append添加节点
import xml.etree.ElementTree as ET tree = ET.parse('xmltest.xml') root = tree.getroot() sub = ET.fromstring('<myyear/>') #准备要添加的元素名 for item in root.iter('country'): #要加在哪个字段下,这里是将myyear加在country下面 item.append(sub) for item in root.iter('myyear'): item.text = '永恒,当前' #给myyear元素添加元素值 item.set('末日', '到了') #给myyear元素添加/修改属性,<year 末日="到了">2012</year> tree.write('xmltest.xml', encoding='UTF-8')
3、使用ET.SubElement添加节点
import xml.etree.ElementTree as ET tree = ET.parse('xmltest.xml') root = tree.getroot() for item in root.iter('country'): #要加在哪个字段下,这里是将myyear加在country下面 ET.SubElement(item,'myyear') #前面是要添加在哪个字段下;后面是要添加的元素名 for item in root.iter('myyear'): item.text = '永恒,当前' #给myyear元素添加元素值 item.set('末日', '到了') #给myyear元素添加/修改属性,<year 末日="到了">2012</year> tree.write('xmltest.xml', encoding='UTF-8')