上一篇内容我们通过一个例子详解了Python解析xml文档的方法(参见Web传输和存储数据结构化,xml可轻松实现,用Python如何解析它?)。今天,我们同样使用一个案例来了解下xml文档的读取和写入操作,以加深印象。千万别走开,前面是对之前知识的回顾,如果你对案例更感兴趣,请直接到文末看。
回顾下就当加深印象了
今天,我们同样使用xml.etree.ElementTree模块下的parse()方法来进行xml文件的读写操作。
为什么使用xml.etree模块
我们知道,Python的xml模块下还有很多接口可以用来处理xml文档,比如,xml.dom、xml.sax模块,都提供了用于处理xml文件的接口。我们为什么就使用etree模块呢?这是因为,其它两个模块的处理方式存在一定的缺陷:
另外两个库都不是完美解决方案
首先,xml.dom模块在处理文档之前,会将xml文件构建好的树状数据存储在内存中,这样既浪费了内存空间,又增加了系统开销,而且当数据量大时,程序运行会非常缓慢;
对Python来说慢是很严重的问题
对比xml.sax模块,使用过这个模块的人都知道,虽说该模块实现了SAX API,运行速度和占用内存方面比xml.dom都要好,但是它在处理xml文件时,过程是很复杂的(对比etree模块来说),对于开发人员来讲,它没有etree提供的接口便利。
逛超市和逛菜市场的区别
因此,我们今天仍使用xml.etree模块来处理xml文件。对其它两个模块感兴趣的小伙伴可以私底下好好研究下,说不定你还能发现一些好的算法呢?
etree模块处理机制
我们在上篇解析中提到了xml文件的解析,解析之前肯定要先读取了,但是上篇内容并没有很好的做出说明。今天我们来详细梳理下:
接触过Linux的小伙伴肯定知道,Linux文件系统结构就像一个倒立的树形结构,这种结构的优点是层次清晰,便于检索和操作文件。
更像是一颗倒着的树
xml.etree模块在处理xml文档时也采用类似的结构,它将一个xml文档定义为一个标签树结构,整个标签树我们叫ElementTree,它包含了该xml文档的内容,我们可以使用ElementTree.getroot()方法获取标签树最上层的元素,该元素称之为根节点,本质上来讲它仍是一个节点,即Element,它和ElementTree中的每一个节点的操作都是一样的,区别在于它没有父节点。
理解节点之间的关系是主线
从上面的分析我们很容易理解,对于整个xml文档的读取、写入、修改,我们在ElementTree层面上进行;而对于节点Element的操作,我们是在Element层面上展开的。因此,如果能熟练掌握这两个对象的接口,就基本能够读写xml文件了。千万别走开,下面有案例哦!
读取并解析xml文档的一般步骤
【加载文档】
import xml.etree.ElementTree
et = ElementTree.ElementTree(file="demo.xml")
【获取根节点】
root = et.getroot()
root是一个Element对象,它具有以下属性:
1.tag:返回元素的标签名
2.attrib:以字典形式返回属性名和值
它提供的方法有:
print([i for i in dir(root) if not i.startswith('_')], end=' ')
>>>
['append', 'attrib', 'clear', 'extend', 'find', 'findall', 'findtext', 'get', 'getchildren', 'getiterator', 'insert', 'items', 'iter', 'iterfind', 'itertext', 'keys', 'makeelement', 'remove', 'set', 'tag', 'tail', 'text']
这样一些方法,其实常用的就那么几个,下文会提到,这些方法也不需要记住,但这个处理步骤如果能记住就更好了。
【开始匹配】
1.根节点遍历解析其它节点
这里的root本身是一个可迭代对象,它可以直接遍历子元素。如下
for el in root:
print(el.tag)
也可以使用索引找到需要的元素
root[0].tag # 返回root下的第一个元素标签
2.查找需要的元素
使用find()、findall()、,iterfind()等进行查找,这些函数的查找范围是下一层子节点,它们不会去找孙子节点或更下一层节点
find(tagName):返回第一个匹配的元素
findall(tagName):返回当前元素下一级所有匹配的元素列表
iterfind(tagName):作用和findall一样,但是它返回一个生成器对象
3.查找当前元素下的所有元素
root.iter() # 列出根元素下所有子节点列表,返回一个迭代器对象
root.iter(tagName) # 列出所有标签名为tagName的子节点,是迭代器对象
4.正则表达式匹配
*:表示所有节点,如:root.find("a/*")表示查找路径a下面的所有子节点
.:表示当前元素,如:root.find(./*)表示查找当前元素下的所有子节点
//:表示当前层级,如: root.findall(".//a"):查找当前目录下任意层级的标签名为a的子元素
..:表示上层节点,如:root.findall(".//a/.."):查找当前目录下任意层级的标签名为a的子元素的父元素
[@attrib]:表示根据指定的属性搜索元素
[@attrib='value']:表示根据给定属性名搜索元素,如:root.findall("a[@type='txt']"):找到所有type为txt的a标签
[tag]:表示子元素标签,如root.findall("a[b]"):找到包含子元素为b的a标签
[position]:还可以i根据元素位置找相应的元素,从1开始: root.findall("a[1]") root.findall("a[last()-1]"):找到倒数第二个元素
上面就是基本的解析元素的步骤了。这就完了?No,今天的内容才刚开始,我们通过一个案例来展示如何写入xml文件。
一个例子
例子是这样的,我们有一份Excel文件,里面记录了一些学生的基本信息,我们将Excel文件中的内容读取出来,然后存储到xml文件中去。
举例来说明
读取Excel文件需要使用到xlrd模块(第三方模块需要安装)
Excel文件格式如下
Excel表格结构
废话不说,直接上代码了。如下
Excel文件数据以xml格式返回
pretty()函数是用于格式化输出的,方法可借鉴,这是一个递归函数。
剩余代码
程序运行后,生成的xml文件如下
程序最终实现
好了,今天的内容就到这里了,我们详细梳理了xml文件的读写操作方式,并通过一个案例完美展示了从Excel文件中读取数据转化为xml文档的过程。其实,如果程序有需要,你甚至可以完成诸如json转xml或者是csv文件与xml文档之间的转化,都是可以的,感兴趣的小伙伴们试试看,就当是个练习内容。欢迎大家留言讨论,关注我,后续会有更加精彩的内容哦。
转载请注明出处,百家号:Python高手养成