Python 读写 xml 文件

王 茂南 2020年11月2日07:20:00
评论
3 6116字阅读20分23秒
摘要这一篇简单介绍一下使用python来处理xml文件. 主要会通过SAX (simple API for XML)和ElementTree这两种方式, 来完成对xml的读写.

简介

这里我们介绍 Python 来处理 xml 文件的方法。目前,有三种主要的读写 xml 文件的方式,分别如下所示:

  • SAX (simple API for XML), python 标准库包含 SAX 解析器,SAX用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理 XML 文件。
  • DOM(Document Object Model), 将 XML 数据在内存中解析成一个树,通过对树的操作来操作XML。
  • ElementTree(元素树), ElementTree 就像一个轻量级的 DOM,具有方便友好的 API。代码可用性好,速度快,消耗内存少。
这一篇文章, 我们会介绍 SAX ElementTree 的使用。

参考资料

 

测试文件

下面是本文会使用的 xml 的测试文件,后面的实验都会基于这个文件进行:

  1. <?xml version='1.0' encoding='UTF-8'?>
  2. <books>
  3.   <book>
  4.     <name>Python黑帽子</name>
  5.     <date>2015</date>
  6.     <price bookName="Python黑帽子">37</price>
  7.     <description>用python写一些程序</description>
  8.   </book>
  9.   <book>
  10.     <name>Web安全深度剖析</name>
  11.     <date>2014</date>
  12.     <price bookName="Web安全深度剖析">39</price>
  13.     <description>讲述web渗透的基础知识</description>
  14.   </book>
  15.   <book>
  16.     <name>白帽子讲web安全</name>
  17.     <date>2013</date>
  18.     <price bookName="白帽子讲web安全">44</price>
  19.     <description>道哥力作</description>
  20.   </book>
  21. </books>

 

使用SAX解析XML文件

SAX会在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件. 我们常会用到:

  • startElement(name, attrs)方法, 他在遇到XML开始标签时调用, name是标签的名字, attrs是标签的属性值字典.
  • characters(content)方法, 他会提取每一个xml标签内的值. 每一次都会触发.

在有了ContentHandler的对象之后, 会使用parser方法来解析xml文档. 下面是完整的sax.parse的参数说明:

  1. xml.sax.parse( xmlfile, contenthandler[, errorhandler])
  2. - # 参数说明:
  3.     - # xmlfile - xml文件名
  4.     - # contenthandler - 必须是一个ContentHandler的对象
  5.     - # errorhandler - 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象

 

计算图书的价格

简单介绍了关于SAX的内容之后, 我们使用上面的例子简单做一下测试. 我们希望计算出上面这三本书的总的价格.

完整的代码如下所示. 因为我们希望计算每一本书的价格, 故我们在startElement中, 如果标签是price, 那么我们将is_element=True, 这样在characters的时候, 就可以将价格相加.

  1. from xml.sax import parse, handler
  2. class test_parse(handler.ContentHandler):
  3.     """解析xml文件
  4.     """
  5.     def __init__(self):
  6.         self.total_price = 0
  7.         self.is_element = False
  8.     def startElement(self, name, attrs):
  9.         if name == 'price':
  10.             print(attrs['bookName'])
  11.             self.is_element = True
  12.     def characters(self, content):
  13.         if self.is_element:
  14.             self.total_price = self.total_price + float(content)
  15.             self.is_element = False
  16.     def endDocument(self):
  17.         """读取完整个xml文件后会触发
  18.         """
  19.         print(self.total_price)
  20. if __name__ == "__main__":
  21.     testParse = test_parse()
  22.     parse('./test.xml', testParse)

需要注意的是, 我们可以在startElement中的attrs获得标签内部的属性, 例如在上面例子中我们在price中定义的bookName这个属性.

上面运行的结果如下所示:

Python 读写 xml 文件

 

ElementTree解析XML文件

上面介绍的SAX其实在使用的时候不是很方便, 特别是当我们要对xml文件进行修改的时候. 所以这里我们介绍使用ElementTree来解析XML文件.

首先介绍一下每一个Element对象的属性:

  • tag:string对象,表示数据代表的种类
  • attrib:dictionary对象,表示附有的属性
  • text:string对象,表示element的内容
  • tail:string对象,表示element闭合之后的尾迹
  • 若干子元素(child elements)

 

遍历 xml 文件-getroot

我们首先对xml文件进行简单的遍历.

  1. from xml.etree import ElementTree as ET
  2. if __name__ == "__main__":
  3.     tree = ET.parse("./test.xml")
  4.     root = tree.getroot()
  5.     for children in root:
  6.         for child in children:
  7.             print(children.tag, child.tag, child.text)

遍历的结果如下所示, 上面的xml文件一共是三本书, 这里会逐个打印出每一个element的内容.

Python 读写 xml 文件

 

遍历 xml 文件-tree.getiterator

除了上面的遍历xml文件的方法之外, 我们还可以使用tree.getiterator来进行xml文件的遍历. 下面也是一个简单的例子说明:

  1. from xml.etree import ElementTree as ET
  2. if __name__ == "__main__":
  3.     tree = ET.parse("./test.xml")
  4.     for children in tree.iter():
  5.         print(children.tag, children.text)

 

关于 find 的使用

有的时候我们需要访问特定的 tag 标签,这个时候可以使用 find 函数。这里用到Element类的几个函数,分别是

  • Element.findall(),只查找直接的孩子,返回所有符合要求的Tag的Element;
  • Element.find(),只返回符合要求的第一个Element;

还是使用上面的 xml 文件,我们找出每一本书的名字:

  1. from xml.etree import ElementTree as ET
  2. from xml.dom import minidom
  3. if __name__ == "__main__":
  4.     tree = ET.parse("./test.xml")
  5.     root = tree.getroot()
  6.     for children in root.findall('book'):
  7.         name_tag = children.find('name')
  8.         print(name_tag.text)
  9. """
  10. Python黑帽子
  11. Web安全深度剖析
  12. 白帽子讲web安全
  13. """

当然上面 for 循环,不使用 findall 也是可以的,可以直接使用 for children in root:

 

计算图书的总的价格

有了上面的遍历之后, 我们就可以很方便的计算这里图书的总的价格了.

  1. from xml.etree import ElementTree as ET
  2. if __name__ == "__main__":
  3.     tree = ET.parse("./test.xml")
  4.     total_price = 0
  5.     for children in tree.iter():
  6.         if children.tag == 'price':
  7.             print(children.attrib['bookName'])
  8.             total_price = total_price + float(children.text)
  9.     print(total_price)

最后计算出来的结果也是120, 和上面的计算结果是一样的.

Python 读写 xml 文件

完成对 XML 文件的修改

有的时候我们需要对 xml 文件进行修改, 这个例子我们将这里书的价格都修改为20. 整体的思路是, 将 xml 文件读取, 然后修改 price, 最后再进行保存即可.

  1. from xml.etree import ElementTree as ET
  2. if __name__ == "__main__":
  3.     tree = ET.parse("./test.xml")
  4.     for children in tree.iter():
  5.         if children.tag == 'price':
  6.             children.text = str(20)
  7.     tree.write('./test.xml', encoding='UTF-8')

这里我们将修改后的tree, 直接write到原始的文件, 这样所有的价格都会变为20了.

Python 读写 xml 文件

 

增加 xml 的内容并进行保存

有的时候我们希望可以在原始的 xml 文件中增加一行,并将新的 xml 文件进行保存。例如下面的 xml 文件,我们希望可以在第 2 个 book 的位置,在 description 的位置下面加一个 tag,这个时候可以使用 append 来进行添加。

Python 读写 xml 文件

我们可以使用下面的代码来完成新的 xml 文件的生成:

  1. from xml.etree import ElementTree as ET
  2. from xml.dom import minidom
  3. if __name__ == "__main__":
  4.     tree = ET.parse("./test.xml")
  5.     root = tree.getroot()
  6.     for children in root:
  7.         for child in children:
  8.             if child.text == 'Web安全深度剖析':
  9.                 children.append(ET.fromstring("<tag value='你好'>书籍</tag>"))
  10.                 break
  11.     # 美化 xml 文件, 并进行保存
  12.     xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="   ")
  13.     xmlstr = '\n'.join([line for line in xmlstr.split('\n') if line.strip()]) # 去掉换行
  14.     with open('./test1.xml', "w", encoding='utf-8') as f:
  15.         f.write(xmlstr)

最终生成的新的 xml 文件中已经添加了新的一行,如下所示:

Python 读写 xml 文件

 

构造一个新的 xml 文件

上面我们都是基于一个 xml 文件来进行修改,现在我们想要从头来生成一个 xml 文件。我们的目标是生成下面的文件:

  1. <routes>
  2.    <vehicle depart="1" id="2" departLane="random">
  3.       <route edges="1 2 3"/>
  4.    </vehicle>
  5. </routes>

首先我们定义根节点,也就是上面的 routes

  1. from xml.etree import ElementTree
  2. from xml.dom import minidom
  3. route_root = ElementTree.Element('routes') # 根节点

接着定义 routes 里面的 vehicle,同时设置属性。可以像下面这样,使用 set 来设置属性:

  1. vehicle_element = ElementTree.SubElement(route_root, 'vehicle')
  2. vehicle_element.set('depart', '1')
  3. vehicle_element.set('id', '2')
  4. vehicle_element.set('departLane', 'random')

接着我们定义 vehicle 里面的 route,使用另外一种方式定义属性:

  1. route_element = ElementTree.SubElement(vehicle_element, 'route', {'edges': '1 2 3'})

到这里就完成了 xml 的生成。我们使用 ElementTree.dump 查看此时的 route_root 的结果:

Python 读写 xml 文件

如果我们想要将上面的内容写在文件中,可以再加上下面的代码:

  1. xmlstr = minidom.parseString(ElementTree.tostring(route_root)).toprettyxml(indent="   ")
  2. xmlstr = '\n'.join([line for line in xmlstr.split('\n') if line.strip()]) # 去掉换行
  3. with open('/mnt/d/test.xml', "w", encoding='utf-8') as f:
  4.     f.write(xmlstr)

 

  • 微信公众号
  • 关注微信公众号
  • weinxin
  • QQ群
  • 我们的QQ群号
  • weinxin
王 茂南
  • 本文由 发表于 2020年11月2日07:20:00
  • 转载请务必保留本文链接:https://mathpretty.com/12924.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: