人生苦短,我用python。

一、XML

XML 指可扩展标记语言(eXtensible Markup Language),标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。

常见的XML编程接口有DOM和SAX,这两种接口处理XML文件的方式不同,使用场合也不同。

python有三种方法解析XML:SAX(simple API for XML ),DOM(Document Object Model),ElementTree。

后面用到的XML实例文件movies.xml内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<collection shelf="New Arrivals">
<movie title="Enemy Behind">
<type>War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>PG</rating>
<stars>10</stars>
<description>Talk about a US-Japan war</description>
</movie>
<movie title="Transformers">
<type>Anime, Science Fiction</type>
<format>DVD</format>
<year>1989</year>
<rating>R</rating>
<stars>8</stars>
<description>A schientific fiction</description>
</movie>
<movie title="Trigun">
<type>Anime, Action</type>
<format>DVD</format>
<episodes>4</episodes>
<rating>PG</rating>
<stars>10</stars>
<description>Vash the Stampede!</description>
</movie>
<movie title="Ishtar">
<type>Comedy</type>
<format>VHS</format>
<rating>PG</rating>
<stars>2</stars>
<description>Viewable boredom</description>
</movie>
</collection>

1、SAX

SAX是流模式,边读边解析,占用内存小,解析快,缺点是我们需要自己处理事件。

SAX解析XML文档牵涉到两个部分:解析器和事件处理器。

  • 解析器:负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件;
  • 事件处理器:则负责对事件作出相应,对传递的XML数据进行处理。

(1)ContentHandler类方法介绍
1)characters(content)方法调用时机:

  • 从行开始,遇到标签之前,存在字符,content的值为这些字符串。
  • 从一个标签,遇到下一个标签之前, 存在字符,content的值为这些字符串。
  • 从一个标签,遇到行结束符之前,存在字符,content的值为这些字符串。

2)startDocument()方法文档启动的时候调用。

3)endDocument()方法解析器到达文档结尾时调用。

4)startElement(name, attrs)方法遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。

5)endElement(name)方法遇到XML结束标签时调用。

(2)make_parser方法创建一个新的解析器对象并返回。
xml.sax.make_parser( [parser_list] )

  • parser_list : 可选参数,解析器列表

(3)parser方法创建一个SAX解析器并解析xml文档:
xml.sax.parse( xmlfile, contenthandler[, errorhandler])

  • xmlfile : xml文件名
  • contenthandler : 必须是一个ContentHandler的对象
  • errorhandler : 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象

(4)parseString方法创建一个XML解析器并解析xml字符串:
xml.sax.parseString(xmlstring, contenthandler[, errorhandler])

  • xmlstring : xml字符串
  • contenthandler : 必须是一个ContentHandler的对象
  • errorhandler : 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import xml.sax

class MovieHandler( xml.sax.ContentHandler ):
def __init__(self):
self.CurrentData = ""
self.type = ""
self.format = ""
self.year = ""
self.rating = ""
self.stars = ""
self.description = ""

# 元素开始调用
def startElement(self, tag, attributes):
self.CurrentData = tag
if tag == "movie":
print ("*****Movie*****")
title = attributes["title"]
print ("Title:", title)

# 元素结束调用
def endElement(self, tag):
if self.CurrentData == "type":
print ("Type:", self.type)
elif self.CurrentData == "format":
print ("Format:", self.format)
elif self.CurrentData == "year":
print ("Year:", self.year)
elif self.CurrentData == "rating":
print ("Rating:", self.rating)
elif self.CurrentData == "stars":
print ("Stars:", self.stars)
elif self.CurrentData == "description":
print ("Description:", self.description)
self.CurrentData = ""

# 读取字符时调用
def characters(self, content):
if self.CurrentData == "type":
self.type = content
elif self.CurrentData == "format":
self.format = content
elif self.CurrentData == "year":
self.year = content
elif self.CurrentData == "rating":
self.rating = content
elif self.CurrentData == "stars":
self.stars = content
elif self.CurrentData == "description":
self.description = content

if ( __name__ == "__main__"):

# 创建一个 XMLReader
parser = xml.sax.make_parser()
# turn off namepsaces
parser.setFeature(xml.sax.handler.feature_namespaces, 0)

# 重写 ContextHandler
Handler = MovieHandler()
parser.setContentHandler( Handler )

parser.parse("movies.xml")

2、DOM

DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,优点是可以任意遍历树的节点。

一个 DOM 的解析器在解析一个 XML 文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后你可以利用DOM 提供的不同的函数来读取或修改文档的内容和结构,也可以把修改过的内容写入xml文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from xml.dom.minidom import parse
import xml.dom.minidom

# 使用minidom解析器打开 XML 文档
DOMTree = xml.dom.minidom.parse("movies.xml")
collection = DOMTree.documentElement
if collection.hasAttribute("shelf"):
print ("Root element : %s" % collection.getAttribute("shelf"))

# 在集合中获取所有电影
movies = collection.getElementsByTagName("movie")

# 打印每部电影的详细信息
for movie in movies:
print ("*****Movie*****")
if movie.hasAttribute("title"):
print ("Title: %s" % movie.getAttribute("title"))

type = movie.getElementsByTagName('type')[0]
print ("Type: %s" % type.childNodes[0].data)
format = movie.getElementsByTagName('format')[0]
print ("Format: %s" % format.childNodes[0].data)
rating = movie.getElementsByTagName('rating')[0]
print ("Rating: %s" % rating.childNodes[0].data)
description = movie.getElementsByTagName('description')[0]
print ("Description: %s" % description.childNodes[0].data)

3、ElementTree

与DOM相比,ET的速度更快,API使用更直接、方便。与SAX相比,ET.iterparse函数同样提供了按需解析的功能,不会一次性在内存中读入整个文档。ET的性能与SAX模块大致相仿,但是它的API更加高层次,用户使用起来更加便捷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# xml解析

from xml.etree import ElementTree

# 1、解析XML
et = ElementTree.parse(r'E:\test.xml') # 解析xml树
root = et.getroot() # 获取根节点

for child in root: # 遍历节点的子元素
print(child.tag, child.attrib) # 打印元素的标签名
print(child.attrib) # 打印元素的标签属性
print(child.text) # 打印元素的标签的文本值

print(root[1].tag, root[1].attrib, root[1].text) # 直接通过索引值,访问特定的子元素

for elem in root.iter(): # iter()方法进行深度遍历,包含所有子孙元素
print(elem.tag, elem.attrib, elem.text)
for elem in root.iter(tag='excel'): # 可以深度遍历所有指定标签的元素
print(elem.tag, elem.attrib, elem.text)

excel = root.find('excel') # 支持通过XPath查找元素,例如:查找第一个标签是'excel'的元素
all_excel = root.findall('excel') # 查找所有标签是'excel'的元素,得到的是一个列表
excel_iter = root.iterfind('excel') # 查找所有标签是'excel'的元素,得到的是迭代对象
for elem in excel_iter:
print(elem.tag, elem.attrib, elem.text)

print(root.findall('excel/*')) # 查找某个节点下的所有子节点,注意*的作用
print(root.findall('.//sheet')) # 查找任意层次下的指定节点元素,注意//的作用
print(root.findall('.//sheet/..')) # 查找任意层次下的指定节点元素的父元素,注意..的作用
print(root.findall('.//table[@name]')) # 查找包含指定属性的节点元素
print(root.findall('.//table[@name="new_qiyi_user_vip_copy"]')) # 查找包含'指定属性=指定属性值'的节点元素
print(root.findall('.//sheet[database]')) # 查找包含指定子节点的节点元素

fields = []
tree = ElementTree.iterparse(r'E:\test.xml') # iterparse方法解析树,可以大大提高内存利用效率和时间
for event,elem in tree: # for循环会遍历iterparse事件
if event == 'end': # 首先检查事件是否为end
if elem.tag == 'field': # 然后判断元素
fields.append(elem.text) # 进行运算
elem.clear() # 废除掉不需要的元素,非常关键:因为iterparse仍然会生成一个树,只是循序生成的而已。废弃掉不需要的元素,就相当于废弃了整个树,释放出系统分配的内存。
print(fields)


# 2、生成xml
new_xml = ElementTree.Element('namelist') # 定义生成xml文件的根节点

name1 = ElementTree.SubElement(new_xml, 'name', attrib={'checked':'yes'}) # 在根节点上添加节点,定义tag和标签
age1 = ElementTree.SubElement(name1, 'age', attrib={'checked':'no'}) # 在节点上添加子节点
age1.text = '20' # 给节点设置文本值

name2 = ElementTree.SubElement(new_xml, 'name', attrib={'checked':'no'})
age2 = ElementTree.Element('age', attrib={'checked':'yes'})
age2.text = '19'
name2.append(age2) # 也可以将定义好的节点追加到其他节点下面

et = ElementTree.ElementTree(new_xml) # 生成xml文件,注意模块和方法名一样
et.write(r'E:\new.xml', encoding='utf-8', xml_declaration=True) # 将生成的xml文件写入到操作系统实际的xml文件中
ElementTree.dump(new_xml) # 打印文档

root = ElementTree.Element('page')
root.extend((new_xml,)) # 还可以将定义好的节点添加到其他节点下,注意传入的是元组
tree = ElementTree.ElementTree(root)
tree.write(r'E:\new1.xml', encoding='utf-8', xml_declaration=True)
ElementTree.dump(tree)


# 3、修改和删除
tree = ElementTree.parse(r'E:\new2.xml')
root = tree.getroot()

for elem in root.iter('name'):
elem.set('checked', 'yes') # 修改属性
for elem in root.iter('age'):
elem.text = '20' # 修改内容
for elem in root.findall('namelist'):
if elem.find('name').attrib['checked'] == 'yes':
root.remove(elem) # 删除节点
del root[1] # 也可以del删除节点
tree.write(r'E:\new2.xml')
ElementTree.dump(tree)

持续更新…

最后更新: 2018年12月04日 22:00

原始链接: http://pythonfood.github.io/2017/12/30/pythonXML解析/

× 多少都行~
打赏二维码