在本教程中,我们将看到一些使用Python来解析XML或可扩展标记语言的例子。XML有点像HTML的一个更灵活的版本。它是一种标记语言,定义了一套规则,用于将文件编码为人类可读和机器可读的格式。计算机对XML的解析有几种不同的方式。第一种是被称为XML的简单API,也被称为SAX。另一种解析XML的方式是通过使用DOM或文档对象模型。先回到SAX。SAX一次一个字符地读取XML数据,一直到文档的结尾。当 XML 被读取时,解析器会发出与 XML 内容有关的事件。使用 Python,我们可以在这些事件发生时处理它们。
SAX 事件
当分析器遇到我们在下面看到的 XML 时,它会产生一个开始的事件,然后当分析器到达开头标签的这个闭合角括号时,它将发送一个带有标签名称的开始标签事件,以及属性的集合,和它们的值。当解析器到达闭合标签的开口角括号时,它将发送一个结束标签事件,当它到达闭合标签的闭合括号时,也将发送一个事件。

随着这些事件的产生,我们可以使用Python来响应并操作数据。当使用SAX时,XML的内容不能以随机顺序访问。记住,SAX的工作方式是逐个字符地在XML文件中移动,直到到达文件的末端。在这个过程中,你不能 "倒退 "或后退。此外,SAX在处理过程中不能改变XML数据。由于这个原因,在使用XML作为配置文件时,SAX很好。
SAX API
为了在Python中使用SAX API,我们使用xml.sax模块。所以我们将导入该模块来运行一些测试代码。一旦导入,我们就可以访问**xml.sax.parse()函数,它可以与文件或流对象一起工作。我们可以使用的另一个函数是xml.sax.parseString()**函数,如果你已经把XML放在一个字符串变量中,就可以使用这个函数。除了这些函数之外,还有一个名为ContentHandler的基类,可以用于自定义内容处理。ContentHandler类有处理文档的开始和结束、标签的开始和结束以及处理文本数据的函数。你可以创建你自己的类,重写这些函数来处理每种类型的内容。
Python SAX XML 示例
下面我们有一些XML数据的例子。它被存储在一个名为xmldata.xml的文件中。
<?xml version="1.0" encoding="UTF-8"?>
<blogposts title="Blog Posts Collection" date="A date" author="Some dude">
<post type="summary">
<title>Parse XML With SAX</title>
</post>
<post type="detail">
<title>Overview</title>
<entry>
Parsing XML is great
</entry>
<entry />
<entry>
Have fun with XML parsing
</entry>
</post>
</blogposts>
我们正在处理的XML数据代表一个虚构的blogposts元素。有一个blogposts根标签,它有一些属性,在blogposts里面有一些帖子,每个帖子有一些条目。代码从这个XML中提取信息,因为它正在被SAX分析器解析。有一些函数会表明我们开始处理文档和完成处理。为了打印出博文的名称,使用了startElement函数。还有endElement、字符、startDocument和endDocument的方法。为了运行这个程序,我们把它放在Python的main()函数中。一个新的CustomContentHandler实例被分配给处理程序变量。然后我们简单地使用xml.sax.parse()来读取数据并打印出一些结果。
import xml.sax
# define a Custom ContentHandler class that extends ContenHandler
class CustomContentHandler(xml.sax.ContentHandler):
def __init__(self):
self.postCount = 0
self.entryCount = 0
self.isInTitle = False
# Handle startElement
def startElement(self, tagName, attrs):
if tagName == 'blogposts':
print('Blogposts title: ' + attrs['title'])
elif tagName == 'post':
self.postCount += 1
elif tagName == 'entry':
self.entryCount += 1
elif tagName == 'title':
self.isInTitle = True
# Handle endElement
def endElement(self, tagName):
if tagName == 'title':
self.isInTitle = False
# Handle text data
def characters(self, chars):
if self.isInTitle:
print('Title: ' + chars)
# Handle startDocument
def startDocument(self):
print('About to start!')
# Handle endDocument
def endDocument(self):
print('Finishing up!')
def main():
# create a new content handler for the SAX parser
handler = CustomContentHandler()
# call the parse method on an XML file
xml.sax.parse('xmldata.xml', handler)
# when we're done, print out some interesting results
print(f'There were {handler.postCount} post elements')
print(f'There were {handler.entryCount} entry elements')
if __name__ == '__main__':
main()
About to start!
Blogposts title: Blog Posts Collection
Title: Parse XML With SAX
Title: Overview
Finishing up!
There were 2 post elements
There were 3 entry elements
Process finished with exit code 0
XML DOM API
另一种可以操作XML内容的方式是使用文档对象模型API或DOM。DOM API和SAX API的一个很大的区别是,DOM允许你随机访问XML文件的任何部分。这在SAX中是不可能的,因为它从头到尾一次读取一个字符。通过DOM,你还可以修改XML文件的内容。当使用DOM解析XML代码时,XML被完整地读入内存,并被表示为一个树状结构。然后你可以使用各种API来处理产生的文档树。Python标准库在xml.dom.minidom模块中提供了一个DOM API的实现。它旨在成为一个比完整的DOM API更小的实现。下面是一些需要注意的关键点和方法。
- 随机访问XML结构的任何部分
- 修改XML内容
- 将XML表示为一个层次化的树状结构
- xml.dom.minidom是一个轻量级的实现
- domtree = xml.com.minidom.parseString(str)
- elem.getElementById(id)
- elem.getElementsByTagName(tagname)
- elem.getAttribute(attrName)
- elem.setAttribute(attrName, val)
- newElem = document.createElement(tagName)
- newElem = document.createTextNode(strOfText)
- elem.appendChild(newElem)
下面是一个使用xml.dom.minidom来操作我们在SAX例子中使用的同一个xmldata.xml文件的例子。注意这个方法提供了更多的灵活性,我们甚至可以在内存中向文件添加数据。我们中的许多人对DOM相当熟悉,因为它在Web开发中非常普遍,所以在Python中使用DOM来处理XML是相当容易理解的。
import xml.dom.minidom
def main():
domtree = xml.dom.minidom.parse('xmldata.xml')
rootnode = domtree.documentElement
# display some information about the content
print(f'The root element is {rootnode.nodeName}')
print(f'The Title is: {rootnode.getAttribute("title")}')
entries = domtree.getElementsByTagName('entry')
print(f'There are {entries.length} entry tags')
# create a new entry tag in memory
newItem = domtree.createElement('entry')
# add some text to the entry
newItem.appendChild(domtree.createTextNode('Magic Entry!'))
# now add the entry to the first post
firstPost = domtree.getElementsByTagName('post')[0]
firstPost.appendChild(newItem)
# Now count the entry tags again
entries = domtree.getElementsByTagName('entry')
print('Now there are {0} entry tags'.format(entries.length))
# Print out the domtree as xml
print(domtree.toxml())
if __name__ == '__main__':
main()
The root element is blogposts
The Title is: Blog Posts Collection
There are 3 entry tags
Now there are 4 entry tags
<?xml version="1.0" ?><blogposts title="Blog Posts Collection" date="A date" author="Some dude">
<post type="summary">
<title>Parse XML With SAX</title>
<entry>Magic Entry!</entry></post>
<post type="detail">
<title>Overview</title>
<entry>
Parsing XML is great
</entry>
<entry/>
<entry>
Have fun with XML parsing
</entry>
</post>
</blogposts>
Process finished with exit code 0
XML ElementTree API
DOM API非常庞大,为处理XML数据提供了跨语言和跨平台的API。ElementTree API采取了一种不同的方法,它专注于成为一种更简单的处理XML的方式 通过ElementTree API,元素被当作是列表一样处理。这意味着,如果你有一个包含其他元素的XML元素,可以使用标准的迭代方式(如for循环)来迭代这些子元素。ElementTree API像对待字典一样对待属性。因此,如果你有一个元素的引用,那么你可以访问它的attrib属性,这是一个包含所有属性名称和值的字典。ElementTree使搜索XML中的内容变得简单明了。它提供的函数可以使用XPath语法来搜索XML中的特定数据。
在下面的例子中,我们使用ElementTree API来测试这些概念。再一次,我们使用了整个教程中一直在使用的同一个XML数据文件。我们可以看到如何建立一个文档结构并找到树的根元素。我们可以访问一个属性,遍历标签,计算元素的数量,添加新的数据,等等。
from lxml import etree
def main():
postCount = 0
entryCount = 0
# build a doc structure using the ElementTree API
doc = etree.parse('xmldata.xml').getroot()
print(doc.tag)
# Access the value of an attribute
print(doc.attrib['title'])
# Iterate over tags
for elem in doc.findall('post'):
print(elem.tag)
# Count the number of posts
postCount = len(doc.findall('post'))
entryCount = len(doc.findall('.//entry'))
print(f'There are {postCount} post elements')
print(f'There are {entryCount} entry elements')
# Create a new post
newPost = etree.SubElement(doc, 'post')
newPost.text = 'This is a new post'
# Count the number of posts
postCount = len(doc.findall('post'))
entryCount = len(doc.findall('.//entry'))
print(f'There are now {postCount} post elements')
print(f'There are now {entryCount} entry elements')
if __name__ == '__main__':
main()
blogposts
Blog Posts Collection
post
post
There are 2 post elements
There are 3 entry elements
There are now 3 post elements
There are now 3 entry elements
Process finished with exit code 0
Python XML解析总结
使用本教程中提到的任何一个库都可以解决在Python中读、写和操作XML数据的问题。我们看了一下XML的SAX API,XML的DOM API,最后是XML的ElementTree API。它们各有利弊,上面的一些链接将提供更多在Python中处理XML的技巧和窍门。