首页 > Python基础教程 >
-
Python3标准库:xml.etree.ElementTree XML操纵API
1. xml.etree.ElementTree XML操纵API
ElementTree库提供了一些工具,可以使用基于事件和基于文档的API来解析XML,可以用XPath表达式搜索已解析的文档,还可以创建新文档或修改现有文档。
1.1 解析XML文档
已解析的XML文档在内存中由ElementTree和Element对象表示,这些对象基于XML文档中节点嵌套的方式按树结构互相连接。
用parse()解析一个完整的文档时,会返回一个ElementTree实例。这个树了解输入文档中的所有数据,另外可以原地搜索或操纵树中的节点。基于这种灵活性,可以更方便的处理已解析的文档,不过,与基于事件的解析方法相比,这种方法往往需要更多的内存,因为必须一次加载整个文档。
对于简单的小文档(如下面的播客列表,被表示为一个OPML大纲),内存需求不大。
podcasts.opml:
- <?xml version="1.0" encoding="UTF-8"?>
- <opml version="1.0">
- <head>
- <title>My Podcasts</title>
- <dateCreated>Sat, 06 Aug 2016 15:53:26 GMT</dateCreated>
- <dateModified>Sat, 06 Aug 2016 15:53:26 GMT</dateModified>
- </head>
- <body>
- <outline text="Non-tech">
- <outline
- text="99% Invisible" type="rss"
- xmlUrl="http://feeds.99percentinvisible.org/99percentinvisible"
- htmlUrl="http://99percentinvisible.org" />
- </outline>
- <outline text="Python">
- <outline
- text="Talk Python to Me" type="rss"
- xmlUrl="https://talkpython.fm/episodes/rss"
- htmlUrl="https://talkpython.fm" />
- <outline
- text="Podcast.__init__" type="rss"
- xmlUrl="http://podcastinit.podbean.com/feed/"
- htmlUrl="http://podcastinit.com" />
- </outline>
- </body>
- </opml>
要解析这个文档,需要向parse()传递一个打开的文件句柄。
- from xml.etree import ElementTree
- with open('podcasts.opml', 'rt') as f:
- tree = ElementTree.parse(f)
- print(tree)
这个方法会读取数据、解析XML,并返回一个ElementTree对象。
1.2 遍历解析树
要按顺序访问所有子节点,可以使用iter()创建一个生成器,该生成器迭代处理这个ElementTree实例。
- from xml.etree import ElementTree
- with open('podcasts.opml', 'rt') as f:
- tree = ElementTree.parse(f)
- for node in tree.iter():
- print(node.tag)
这个例子会打印整个树,一次打印一个标记。
如果只是打印播客的名字组和提要URL,则可以只迭代处理outline节点(而不考虑首部中的所有数据),并且通过查找attrib字典中的值来打印text和xmlUrl属性。
- from xml.etree import ElementTree
- with open('podcasts.opml', 'rt') as f:
- tree = ElementTree.parse(f)
- for node in tree.iter('outline'):
- name = node.attrib.get('text')
- url = node.attrib.get('xmlUrl')
- if name and url:
- print(' %s' % name)
- print(' %s' % url)
- else:
- print(name)
iter()的'outline'参数意味着只处理标记为'outline'的节点。
1.3 查找文档中的节点
查看整个树并搜索有关的节点可能很容易出错。前面的例子必须查看每一个outline节点,来确定这是一个组(只有一个text属性的节点)还是一个播客(包含text和xmlUrl的节点)。要生成一个简单的播客提要URL列表而不包含名字或组,可以简化逻辑,使用findall()来查找有更多描述性搜索特性的节点。
对以上第一个版本做出第一次修改,用一个XPath参数来查找所有outline节点。
- from xml.etree import ElementTree
- with open('podcasts.opml', 'rt') as f:
- tree = ElementTree.parse(f)
- for node in tree.findall('.//outline'):
- url = node.attrib.get('xmlUrl')
- if url:
- print(url)
这个版本中的逻辑与使用getiterator()的版本并没有显著区别。这里仍然必须检查是否存在URL,只不过如果没有发现URL,它不会打印组名。
outline节点只有两层嵌套,可以利用这一点,把搜索路径修改为.//outline/outline,这意味着循环只处理outline节点的第二层。