Python XML 解析
什么是 XML?
XML 指可扩展标记语言(eXtensible Markup Language)。
XML 被设计用来传输和存储数据。
XML 是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识。
它也是元标记语言,即定义了用于定义其他与特定领域有关的、语义的、结构化的标记语言的句法语言。
Python 对 XML 的解析
常见的 XML 编程接口有 DOM 和 SAX,这两种接口处理 XML 文件的方式不同,当然使用场合也不同。
Python 有三种方法解析 XML,SAX,DOM,以及 ElementTree:
1.SAX (simple API for XML )
Python 标准库包含 SAX 解析器,SAX 用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。
2.DOM(Document Object Model)
将 XML 数据在内存中解析成一个树,通过对树的操作来操作XML。
3.ElementTree(元素树)
ElementTree就像一个轻量级的DOM,具有方便友好的API。代码可用性好,速度快,消耗内存少。
注:因DOM需要将XML数据映射到内存中的树,一是比较慢,二是比较耗内存,而SAX流式读取XML文件,比较快,占用内存少,但需要用户实现回调函数(handler)。推荐使用ET来处理XML,除非你有什么非常特别的需要。
ElementTree - 一个 API ,两种实现
ElementTree 生来就是为了处理 XML ,它在 Python 标准库中有两种实现。一种是纯 Python 实现例如 xml.etree.ElementTree
,另外一种是速度快一点的 xml.etree.cElementTree
。你要记住: 尽量使用 C 语言实现的那种,因为它速度更快,而且消耗的内存更少。如果你的电脑上没有 _elementtree
那么你需要这样做:
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
这是一个让 Python 不同的库使用相同 API 的一个比较常用的办法。还是那句话,你的编译环境和别人的很可能不一样,所以这样做可以防止一些莫名其妙的小问题。注意:从 Python 3.3 开始,你没有必要这么做了,因为 ElementTree
模块会自动寻找可用的 C 库来加快速度。所以只需要 import xml.etree.ElementTree
就可以了。但是在 3.3 正式推出之前,你最好还是使用我上面提供的那段代码。
将 XML 解析为树的形式
我们来讲点基础的。XML 是一种分级的数据形式,所以最自然的表示方法是将它表示为一棵树。ET 有两个对象来实现这个目的 - ElementTree
将整个 XML 解析为一棵树, Element
将单个结点解析为树。如果是整个文档级别的操作(比如说读,写,找到一些有趣的元素)通常用 ElementTree
。单个 XML 元素和它的子元素通常用 Element
。下面的例子能说明我刚才啰嗦的一大堆。
我们用这个 XML 文件来做例子:
<?xml version="1.0"?>
<doc>
<branch name="testing" hash="1cdf045c">
text,source
</branch>
<branch name="release01" hash="f200013e">
<sub-branch name="subrelease01">
xml,sgml
</sub-branch>
</branch>
<branch name="invalid">
</branch>
</doc>
让我们加载并且解析这个 XML :
>>> import xml.etree.cElementTree as ET
>>> tree = ET.ElementTree(file='doc1.xml')
然后抓根结点元素:
>>> tree.getroot()
<Element 'doc' at 0x11eb780>
和预期一样,root 是一个 Element
元素。我们可以来看看:
>>> root = tree.getroot()
>>> root.tag, root.attrib
('doc', {})
看吧,根元素没有任何状态。就像任何 Element
一样,它可以找到自己的子结点:
>>> for child_of_root in root:
... print(child_of_root.tag, child_of_root.attrib)
...
('branch', {'hash': '1cdf045c', 'name': 'testing'})
('branch', {'hash': 'f200013e', 'name': 'release01'})
('branch', {'name': 'invalid'})
我们也可以进入一个指定的子结点:
>>> root[0].tag, root[0].text
('branch', '\n text,source\n ')
找到我们感兴趣的元素
从上面的例子我们可以轻而易举的看到,我们可以用一个简单的递归获取 XML 中的任何元素。然而,因为这个操作比较普遍,ET 提供了一些有用的工具来简化操作.
Element 对象有一个 iter
方法可以对子结点进行深度优先遍历。(好像自己手敲哦一个dfs...) ElementTree
对象也有 iter
方法来提供便利。
>>> for elem in tree.iter():
... print(elem.tag, elem.attrib)
...
('doc', {})
('branch', {'hash': '1cdf045c', 'name': 'testing'})
('branch', {'hash': 'f200013e', 'name': 'release01'})
('sub-branch', {'name': 'subrelease01'})
('branch', {'name': 'invalid'})
遍历所有的元素,然后检验有没有你想要的。ET 可以让这个过程更便捷。 iter
方法接受一个标签名字,然后只遍历那些有指定标签的元素:
>>> for elem in tree.iter(tag='branch'):
... print elem.tag, elem.attrib
...
branch {'hash': '1cdf045c', 'name': 'testing'}
branch {'hash': 'f200013e', 'name': 'release01'}
branch {'name': 'invalid'}
来自 XPath 的帮助
为了寻找我们感兴趣的元素,一个更加有效的办法是使用 XPath 支持。 Element
有一些关于寻找的方法可以接受 XPath 作为参数。 find
返回第一个匹配的子元素, findall
以列表的形式返回所有匹配的子元素, iterfind
为所有匹配项提供迭代器。这些方法在 ElementTree
里面也有。
给出一个例子:
>>> for elem in tree.iterfind('branch/sub-branch'):
... print(elem.tag, elem.attrib)
...
('sub-branch', {'name': 'subrelease01'})
这个例子在 branch
下面找到所有标签为 sub-branch
的元素。然后给出如何找到所有的 branch
元素,用一个指定 name
的状态即可:
>>> for elem in tree.iterfind('branch[@name="release01"]'):
... print(elem.tag, elem.attrib)
...
('branch', {'hash': 'f200013e', 'name': 'release01'})
想要深入学习 XPath 的话,请看 这里 。
建立 XML 文档
ET 提供了建立 XML 文档和写入文件的便捷方式。 ElementTree
对象提供了 write
方法。
现在,这儿有两个常用的写 XML 文档的脚本。
修改文档可以使用 Element
对象的方法:
>>> for subelem in root:
... print(subelem.tag, subelem.attrib)
...
branch {'foo': 'bar', 'hash': '1cdf045c', 'name': 'testing'}
branch {'hash': 'f200013e', 'name': 'release01'}
我们在这里删除了根元素的第三个子结点,然后为第一个子结点增加新状态。然后这个树可以写回到文件中。
>>> import sys
>>> tree.write(sys.stdout) # ET.dump can also serve this purpose, ET.dump(tree)
<doc>
<branch foo="bar" hash="1cdf045c" name="testing">
text,source
</branch>
<branch hash="f200013e" name="release01">
<sub-branch name="subrelease01">
xml,sgml
</sub-branch>
</branch>
</doc>
注意状态的顺序和原文档的顺序不太一样。这是因为 ET 讲状态保存在无序的字典中。语义上来说,XML 并不关心顺序。
建立一个全新的元素也很容易。ET 模块提供了 SubElement
函数来简化过程:
>>> a = ET.Element('elem')
>>> c = ET.SubElement(a, 'child1')
>>> c.text = "some text"
>>> d = ET.SubElement(a, 'child2')
>>> b = ET.Element('elem_b')
>>> root = ET.Element('root')
>>> root.extend((a, b))
>>> tree = ET.ElementTree(root)
>>> tree.write(sys.stdout)
<root><elem><child1>some text</child1><child2 /></elem><elem_b /></root>
如果要修改好的保存到指定文件
tree.write(new_data.xml)
使用 iterparse 来处理 XML 流
就像我在文章一开头提到的那样,XML 文档通常比较大,所以将它们全部读入内存的库可能会有点儿小问题。这也是为什么我建议使用 SAX API 来替代 DOM 。
我们刚讲过如何使用 ET 来将 XML 读入内存并且处理。但它就不会碰到和 DOM 一样的内存问题么?当然会。这也是为什么这个包提供一个特殊的工具,用来处理大型文档,并且解决了内存问题,这个工具叫 iterparse
。
我给大家演示一个 iterparse
如何使用的例子。我用 自动生成 拿到了一个 XML 文档来进行说明。这只是开头的一小部分:
<?xml version="1.0" standalone="yes"?>
<site>
<regions>
<africa>
<item id="item0">
<location>United States</location> <!-- Counting locations -->
<quantity>1</quantity>
<name>duteous nine eighteen </name>
<payment>Creditcard</payment>
<description>
<parlist>
[...]
我已经用注释标出了我要处理的元素,我们用一个简单的脚本来计数有多少 location
元素并且文本内容为“Zimbabwe”。这是用 ET.parse
的一个标准的写法:
tree = ET.parse(sys.argv[2]) count = 0
for elem in tree.iter(tag='location'):
if elem.text == 'Zimbabwe':
count += 1
print count
所有 XML 树中的元素都会被检验。当处理一个大约 100MB 的 XML 文件时,占用的内存大约是 560MB ,耗时 2.9 秒。
注意:我们并不需要在内存中加载整颗树。它检测我们需要的带特定值的 location
元素。其他元素被丢弃。这是 iterparse
的来源:
count = 0
for event, elem in ET.iterparse(sys.argv[2]):
if event == 'end':
if elem.tag == 'location' and elem.text == 'Zimbabwe':
count += 1
elem.clear() # discard the element print(count)
这个循环遍历 iterparse
事件,检测“闭合的”(end)事件并且寻找 location
标签和指定的值。在这里 elem.clear()
是关键 - iterparse
仍然建立一棵树,只不过不需要全部加载进内存,这样做可以有效的利用内存空间。
处理同样的文件,这个脚本占用内存只需要仅仅的 7MB ,耗时 2.5 秒。速度的提升归功于生成树的时候只遍历一次。相比较来说, parse
方法首先建立了整个树,然后再次遍历来寻找我们需要的元素(所以慢了一点)。
结论
在 Python 众多处理 XML 的模块中, ElementTree
真是屌爆了。它将轻量,符合 Python 哲学的 API ,出色的性能完美的结合在了一起。所以说如果要处理 XML ,果断地使用它吧!
参考资料:
1. 用 ElementTree 在 Python 中解析 XML
Python XML 解析的更多相关文章
- Python XML解析(转载)
Python XML解析 什么是XML? XML 指可扩展标记语言(eXtensible Markup Language). 你可以通过本站学习XML教程 XML 被设计用来传输和存储数据. XML是 ...
- Python XML解析之ElementTree
参考网址: http://www.runoob.com/python/python-xml.html https://docs.python.org/2/library/xml.etree.eleme ...
- python大法好——Python XML解析
Python XML解析 什么是XML? XML 被设计用来传输和存储数据. XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识. 它也是元标记语言,即定义了用于定义其他与 ...
- Python XML解析之DOM
DOM说明: DOM:Document Object Model API DOM是一种跨语言的XML解析机制,DOM把整个XML文件或字符串在内存中解析为树型结构方便访问. https://docs. ...
- Python XML解析
什么是XML? XML 指可扩展标记语言(eXtensible Markup Language). 你可以通过本站学习XML教程 XML 被设计用来传输和存储数据. XML是一套定义语义标记的规则,这 ...
- Python XML 解析Ⅱ
make_parser方法 以下方法创建一个新的解析器对象并返回. 参数说明: parser_list - 可选参数,解析器列表 parser方法 以下方法创建一个 SAX 解析器并解析xml文档: ...
- Python XML解析和处理
movies.xml <collection shelf = "New Arrivals"> <movie title = "Enemy Behind& ...
- Python xml 解析百度糯米信息
先利用爬虫利用百度糯米提供的api来采集北京当天的团购信息,保存为numi.html import xml.etree.ElementTree as ET import os class Nuomi( ...
- 面试官问我:如何在 Python 中解析和修改 XML
摘要:我们经常需要解析用不同语言编写的数据.Python提供了许多库来解析或拆分用其他语言编写的数据.在此 Python XML 解析器教程中,您将学习如何使用 Python 解析 XML. 本文分享 ...
随机推荐
- day01-HTML(1)
一. 常用快捷键 Ctrl+c 复制 Ctrl+v 粘贴 Ctrl+x 剪切 Ctrl+a 全选 Ctrl+s 保存 Ctrl+z 撤销一步 Windows+d 返回桌面 Windows+e 我的电脑 ...
- 文档公式编辑神器-Snip
最近在重新学习统计学的一些基础知识,整理笔记的时候需要输入一些数学公式.从学校毕业之后,就没有在文档中插入过公式了.按照以前的经验,我把输入公式的方式分成两类. 所见即所得的方式,常见的就是微软wor ...
- 【读后感1】SQL2008技术内幕- SQL逻辑查询处理
引言观点 1. 编程语言日新月异,但是从没有人否定sql 在现代编程中的巨大作用和 持续的可利用性.SQL以对人类友好的阅读体验提供数据查询能力( 相比其他编程语言 ), 同时在各种数据库平台中,基础 ...
- MYSQL中coalesce函数的用法
coalesce():返回参数中的第一个非空表达式(从左向右依次类推): 例如: select coalesce(null,4,5); // 返回4 select coalesce(null,null ...
- JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String
在使用Postman测试Spring Boot项目接口时,接口返回JSON parse error: Cannot deserialize value of type `java.time.Local ...
- Unity5.5 Lighting Scene
参考:https://docs.unity3d.com/Manual/GlobalIllumination.html Environment Lighting(环境光) Skybox: 天空盒材质,这 ...
- 洛谷P3455 [POI2007]ZAP-Queries(莫比乌斯反演)
传送门 设$$f(k)=\sum_{i=1}^{a}\sum_{j=1}^{b}[gcd(i,j)=k]$$ $$g(n)=\sum_{n|k}f(k)=\lfloor\frac{a}{n}\rflo ...
- [Xcode 实际操作]五、使用表格-(8)自定义UITableView单元格Accessory样式(附件图标)
目录:[Swift]Xcode实际操作 本文将演示如何自定义单元格的附件图标. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] import UIKit //首先添 ...
- 数组常用API(1)
数组常用API: 1. push 作用:数组尾部添加: 特点:可以添加一个或多个值: 返回值是数组最新的长度:会改变原数组: 示例: var arr = [10,20,30,40]; ...
- 剑指Offer的学习笔记(C#篇)-- 构建乘积数组
题目描述 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1].不 ...