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
2root[0][1].text
'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 = ET.Element('a') |