XML 树和元素
XML 是一种继承性的分层数据格式,最自然的表示方法是使用树。
为此, ET
有两个类
ElementTree
将整个XML文档表示为一个树Element
表示该树中的单个节点
与整个文档的交互(读写文件)通常在 ElementTree
级别完成。 与单个 XML 元素及其子元素的交互是在 Element
级别完成的。
解析XML
从文件中读取来导入数据
1
2
3import xml.etree.ElementTree as ET
tree = ET.parse('country_data.xml')
root = tree.getroot()parse()
方法用来解析xml文档直接从字符串中解析
fromstring()
将 XML 从字符串直接解析为Element
,该元素是已解析树的根元素1
root = ET.fromstring(country_data_as_string)
作为
Element
,root
具有标签和属性字典:1
2
3
4root.tag
'data'
root.attrib
{}还有可以迭代的子节点:
1
2
3
4
5
6for child in root:
print(child.tag, child.attrib)
country {'name': 'Liechtenstein'}
country {'name': 'Singapore'}
country {'name': 'Panama'}子级是可以嵌套的,我们可以通过索引访问特定的子级节点:
1
20][1].text root[
'2008'
用于非阻塞解析的拉取 API
大多数解析函数都要求在返回任何结果之前一次性读取整个文档。
XMLParser
:以增量方式添加数据,但这是在回调目标上调用方法的推送式 API。 有时用户真正想要的是能够以增量方式解析 XML 而无需阻塞操作,同时享受完整的已构造 Element
对象。
针对此需求的最强大工具是 XMLPullParser
。 它不要求通过阻塞式读取来获得 XML 数据,而是通过执行 XMLPullParser.feed()
调用来增量式地添加数据。 要获得已解析的 XML 元素,应调用 XMLPullParser.read_events()
。
1 | parser = ET.XMLPullParser(['start', 'end']) |
应用:针对以非阻塞方式进行的应用程序,其中 XML 是从套接字接收或从某些存储设备增量式读取的。 在这些用例中,阻塞式读取是不可接受的。
缺点:因为其非常灵活,XMLPullParser
在更简单的用例中使用起来可能并不方便。
其他方法:
iterparse()
:不介意你的应用程序在读取 XML 数据时造成阻塞但仍希望具有增量解析能力。 它在你读取大型 XML 文档并且不希望将它完全放去内存时会很适用。XMLPullParser.flush()
:有助于减少延迟,适用于需要通过事件获得即时反馈的场合
查找元素
Element.iter()
:帮助递归遍历其下的所有子树(包括子级,子级的子级,等等)1
2
3
4
5
6
7
8for neighbor in root.iter('neighbor'):
print(neighbor.attrib)
{'name': 'Austria', 'direction': 'E'}
{'name': 'Switzerland', 'direction': 'W'}
{'name': 'Malaysia', 'direction': 'N'}
{'name': 'Costa Rica', 'direction': 'W'}
{'name': 'Colombia', 'direction': 'E'}Element.findall()
:仅查找当前元素的直接子元素中带有指定标签的元素Element.find()
:找带有特定标签的 第一个 子级
然后可以用 Element.text
访问元素的文本内容。 Element.get
访问元素的属性:
1 | for country in root.findall('country'): |
ps:通过使用 XPath ,可以更精确地指定要查找的元素
修改XML文件
ElementTree
提供了一种构建XML文档并将其写入文件的简单方法。调用 ElementTree.write()
方法就可以实现。
创建后可以直接操作 Element
对象。例如:
- 使用
Element.text
修改文本字段 - 使用
Element.set()
方法添加和修改属性 - 使用
Element.append()
添加新的子元素。
假设我们要为每个国家/地区的中添加一个排名,并在排名元素中添加一个 updated
属性:
1 | for rank in root.iter('rank'): |
Element.remove()
删除元素假设我们要删除排名高于50的所有国家/地区:
1
2
3
4
5
6
7for country in root.findall('country'):
# using root.findall() to avoid removal during traversal
rank = int(country.find('rank').text)
if rank > 50:
root.remove(country)
tree.write('output.xml')
请注意在迭代时进行并发修改可能会导致问题,就像在迭代并修改 Python 列表或字典时那样。 因此,可以先通过
root.findall()
收集所有匹配的元素,在此之后再对匹配项列表进行迭代。
构建XML文档
SubElement()
函数还提供了一种便捷方法来为给定元素创建新的子元素
1 | 'a') a = ET.Element( |