解析XML的Python包 Python的标准库中,提供了6种可以用于处理XML的包。
xml.dom xml.dom实现的是W3C制定的DOM API。如果你习惯于使用DOM API或者有人要求这这样做,可以使用这个包。不过要注意,在这个包中,还提供了几个不同的模块,各自的性能有所区别。
DOM解析器在任何处理开始之前,必须把基于XML文件生成的树状数据放在内存,所以DOM解析器的内存使用量完全根据输入资料的大小。
xml.dom.minidom xml.dom.minidom是DOM API的极简化实现,比完整版的DOM要简单的多,而且这个包也小的多。那些不熟悉DOM的朋友,应该考虑使用xml.etree.ElementTree模块。据lxml的作者评价 ,这个模块使用起来并不方便,效率也不高,而且还容易出现问题。
xml.dom.pulldom 与其他模块不同,xml.dom.pulldom模块提供的是一个“pull解析器”,其背后的基本概念指的是从XML流中pull事件,然后进行处理。虽然与SAX一样采用事件驱动模型(event-driven processing model),但是不同的是,使用pull解析器时,使用者需要明确地从XML流中pull事件,并对这些事件遍历处理,直到处理完成或者出现错误。
pull解析(pull parsing)是近来兴起的一种XML处理趋势。此前诸如SAX和DOM这些流行的XML解析框架,都是push-based,也就是说对解析工作的控制权,掌握在解析器的手中。
xml.sax xml.sax模块实现的是SAX API,这个模块牺牲了便捷性来换取速度和内存占用。SAX是Simple API for XML的缩写,它并不是由W3C官方所提出的标准。它是事件驱动的,并不需要一次性读入整个文档,而文档的读入过程也就是SAX的解析过程。所谓事件驱动,是指一种基于回调(callback)机制的程序运行方法。
xml.etree.ElementTree(以下简称ET) xml.etree.ElementTree模块提供了一个轻量级、Pythonic的API,同时还有一个高效的C语言实现,即xml.etree.cElementTree。与DOM相比,ET的速度更快,API使用更直接、方便。与SAX相比,ET.iterparse函数同样提供了按需解析的功能,不会一次性在内存中读入整个文档。ET的性能与SAX模块大致相仿,但是它的API更加高层次,用户使用起来更加便捷。
详解 xml.etree.ElementTree 简介 Element类型是一种灵活的容器对象,用于在内存中存储结构化数据。
注意:xml.etree.ElementTree模块在应对恶意结构数据时显得并不安全。
每个element对象都具有以下属性:
tag:string对象,表示数据代表的种类。 attrib:dictionary对象,表示附有的属性。 text:string对象,表示element的内容。 tail:string对象,表示element闭合之后的尾迹。 若干子元素(child elements)。 1 2 <tag attrib1=1 >text</tag>tail 1 2 3 4
导入 Python标准库中,提供了ET的两种实现。一个是纯Python实现的xml.etree.ElementTree,另一个是速度更快的C语言实现xml.etree.cElementTree。
Python 3.3版本之前优先导入C语言编译的API:
1 2 3 4 try : import xml.etree.cElementTree as ETexcept ImportError: import xml.etree.ElementTree as ET
Python 3.3之后,ElemenTree模块会自动优先使用C加速器,如果不存在C实现,则会使用Python实现。
Element对象 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 class xml .etree.ElementTree.Element(tag, attrib={}, **extra) tag:string,元素代表的数据种类。 text:string,元素的内容。 tail:string,元素的尾形。 attrib:dictionary,元素的属性字典。 #针对属性的操作 clear():清空元素的后代、属性、text和tail也设置为None 。 get(key, default=None ):获取key对应的属性值,如该属性不存在则返回default值。 items():根据属性字典返回一个列表,列表元素为(key, value)。 keys():返回包含所有元素属性键的列表。 set (key, value):设置新的属性键与值。 #针对后代的操作 append(subelement):添加直系子元素。 extend(subelements):增加一串元素对象作为子元素。#python2.7 新特性 find(match ):寻找第一个匹配子元素,匹配对象可以为tag或path。 findall(match ):寻找所有匹配子元素,匹配对象可以为tag或path。 findtext(match ):寻找第一个匹配子元素,返回其text值。匹配对象可以为tag或path。 insert(index, element):在指定位置插入子元素。 iter (tag=None ):生成遍历当前元素所有后代或者给定tag的后代的迭代器。#python2.7 新特性 iterfind(match ):根据tag或path查找所有的后代。 itertext():遍历所有后代并返回text值。 remove(subelement):删除子元素。
ElementTree对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class xml .etree.ElementTree.ElementTree(element=None , file=None ) element如果给定,则为新的ElementTree的根节点。 _setroot(element):用给定的element替换当前的根节点。慎用。 # 以下方法与Element类中同名方法近似,区别在于它们指定以根节点作为操作对象。 find(match ) findall(match ) findtext(match , default=None ) getroot():获取根节点. iter (tag=None ) iterfind(match ) parse(source, parser=None ):装载xml对象,source可以为文件名或文件类型对象. write(file, encoding="us-ascii" , xml_declaration=None , default_namespace=None ,method="xml" )
快速入门 将XML文档解析为树(tree) XML是一种结构化、层级化的数据格式,最适合体现XML的数据结构就是树。ET提供了两个对象:ElementTree将整个XML文档转化为树,Element则代表着树上的单个节点。对整个XML文档的交互(读取,写入,查找需要的元素),一般是在ElementTree层面进行的。对单个XML元素及其子元素,则是在Element层面进行的。
使用下面的XML文档,作为演示数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8" ?> <ProductMetaData > <SatelliteID > HY-1C</SatelliteID > <SensorID > CZI</SensorID > <TimeType > MONTHLY</TimeType > <ProductUnit > NSOAS</ProductUnit > <ProductInfo > <ProductLevel > L1B</ProductLevel > <ProductFormat > HDF5.0</ProductFormat > <ProductDescription > HY-1C LEVELL1B Product</ProductDescription > <ProcessingType > OPER</ProcessingType > <ProduceTime > 2022-04-25T03:18:00.225817</ProduceTime > <SpatialResolution > </SpatialResolution > </ProductInfo > </ProductMetaData >
加载xml文档,获取ElementTree对象
1 2 3 4 5 6 >>> import xml.etree.ElementTree as ET>>> tree = ET.ElementTree(file='D:\PythonFiles\SatelliteDataParsing\H1C_OPER_CZI_L1B_20220425T025003_20220425T025059_19011_10.meta.xml' ) <xml.etree.ElementTree.ElementTree object at 0x000001DA59EB5940 >
获取根元素(root element)
1 2 3 >>> root = tree获取根元素(root element).getroot()>>> root.tag, root.attrib, root.text, root.tail ('ProductMetaData' , {}, '\n ' , None )
遍历直接子元素
1 2 3 4 5 6 7 8 >>> for child in root:... print (child.tag, child.attrib, child.text)... SatelliteID {} HY-1C SensorID {} CZI TimeType {} MONTHLY ProductUnit {} NSOAS ProductInfo {}
通过索引值来访问特定的子元素
1 2 3 4 >>> root[0 ].tag, root[0 ].text ('SatelliteID' , 'HY-1C' )>>> root[4 ][0 ].tag, root[4 ][0 ].text ('ProductLevel' , 'L1B' )
访问元素 Element对象有一个iter方法,可以对某个元素对象之下所有的子元素进行深度优先遍历(DFS)。ElementTree对象同样也有这个方法。下面是查找XML文档中所有元素的最简单方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> for e in tree.iter ():... print (e.tag, e.attrib, e.text)... ProductMetaData {} SatelliteID {} HY-1C SensorID {} CZI TimeType {} MONTHLY ProductUnit {} NSOAS ProductInfo {} ProductLevel {} L1B ProductFormat {} HDF5.0 ProductDescription {} HY-1C LEVELL1B Product ProcessingType {} OPER ProduceTime {} 2022 -04-25T03:18 :00.225817 SpatialResolution {} None
遍历所有具备所提供tag的元素
1 2 3 4 >>> for e in tree.iter (tag='ProductName' ):... print (e.tag, e.attrib, e.text)... ProductLevel {} L1B
通过XPath查找元素 Element对象中有一些find方法可以接受Xpath路径作为参数,find方法会返回第一个匹配的子元素,findall以列表的形式返回所有匹配的子元素, iterfind则返回一个所有匹配元素的迭代器(iterator)。ElementTree对象也具备这些方法,相应地它的查找是从根节点开始的。
ProductInfo元素之下所有tag为ProductFormat的元素
1 2 3 4 >>> for e in tree.iterfind('ProductInfo/ProductFormat' ):... print (e.tag, e.attrib, e.text)... ProductFormat {} HDF5.0
查找所有具备某个name属性的branch元素
构建XML文档 利用ET,很容易就可以完成XML文档构建,并写入保存为文件。ElementTree对象的write方法就可以实现这个需求。
一般来说,有两种主要使用场景。一是你先读取一个XML文档,进行修改,然后再将修改写入文档,二是从头创建一个新XML文档。
修改文档的话,可以通过调整Element对象来实现。请看下面的例子:
1 2 3 4 5 6 7 8 >>> root = tree.getroot()>>> del root[2 ]>>> root[0 ].set ('foo' , 'bar' )>>> for subelem in root:... print subelem.tag, subelem.attrib ... branch {'foo' : 'bar' , 'hash' : '1cdf045c' , 'name' : 'codingpy.com' } branch {'hash' : 'f200013e' , 'name' : 'release01' }
在上面的代码中,我们删除了root元素的第三个子元素,为第一个子元素增加了新属性。这个树可以重新写入至文件中。最终的XML文档应该是下面这样的:
请注意,文档中元素的属性顺序与原文档不同。这是因为ET是以字典的形式保存属性的,而字典是一个无序的数据结构。当然,XML也不关注属性的顺序。
从头构建一个完整的文档也很容易。ET模块提供了一个SubElement工厂函数,让创建元素的过程变得很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> a = ET.Element('elem' )>>> c = ET.SubElement(a, 'child1' )>>> c.text = "some text" >>> d = ET.SubElement(a, 'child2' )>>> b = ET.Element('elem_b' )>>> root = ET.Element('root' )>>> root.extend((a, b))>>> tree = ET.ElementTree(root)>>> tree.write(sys.stdout)>>> for i in tree.iter ():... print (i.tag, i.attrib, i.text)... root {} None elem {} None child1 {} some text child2 {} None elem_b {} None
利用iterparse解析XML流 ML文档通常都会比较大,如何直接将文档读入内存的话,那么进行解析时就会出现问题。这也就是为什么不建议使用DOM,而是SAX API的理由之一。
我们上面谈到,ET可以将XML文档加载为保存在内存里的树(in-memory tree),然后再进行处理。但是在解析大文件时,这应该也会出现和DOM一样的内存消耗大的问题吧?没错,的确有这个问题。为了解决这个问题,ET提供了一个类似SAX的特殊工具——iterparse,可以循序地解析XML。
接下来,笔者为大家展示如何使用iterparse,并与标准的树解析方式进行对比。我们使用一个自动生成的XML文档 ,下面是该文档的开头部分:
……
模块方法 1 xml.etree.ElementTree.Comment(text=None )
创建一个特别的element,通过标准序列化使其代表了一个comment。comment可以为bytestring或unicode。
dump() 1 xml.etree.ElementTree.dump(elem)
生成一个element tree,通过sys.stdout输出,elem可以是元素树或单个元素。这个方法最好只用于debug。
fromstring() 1 xml.etree.ElementTree.fromstring(text)
text是一个包含XML数据的字符串,与XML()方法类似,返回一个Element实例。
fromstringlist() 1 xml.etree.ElementTree.fromstringlist(sequence, parser=None )
从字符串的序列对象中解析xml文档。缺省parser为XMLParser,返回Element实例。
New in version 2.7.
iselement() 1 xml.etree.ElementTree.iselement(element)
检查是否是一个element对象。
1 2 iterparse() xml.etree.ElementTree.iterparse(source, events=None , parser=None )
将文件或包含xml数据的文件对象递增解析为element tree,并且报告进度。events是一个汇报列表,如果忽略,将只有end事件会汇报出来。
注意,iterparse()只会在看见开始标签的”>”符号时才会抛出start事件,因此届时属性是已经定义了,但是text和tail属性在那时还没有定义,同样子元素也没有定义,因此他们可能不能被显示出来。如果你想要完整的元素,请查找end事件。
parse() 1 xml.etree.ElementTree.parse(source, parser=None )
将一个文件或者字符串解析为element tree。
SubElement() 1 xml.etree.ElementTree.SubElement(parent, tag, attrib={}, **extra)
子元素工厂,创建一个Element实例并追加到已知的节点。
tostring() 1 xml.etree.ElementTree.tostring(element, encoding="us-ascii" , method="xml" )
生成一个字符串来表示表示xml的element,包括所有子元素。element是Element实例,method为”xml”,”html”,”text”。返回包含了xml数据的字符串。
tostringlist() 1 xml.etree.ElementTree.tostringlist(element, encoding="us-ascii" , method="xml" )
生成一个字符串来表示表示xml的element,包括所有子元素。element是Element实例,method为”xml”,”html”,”text”。返回包含了xml数据的字符串列表。
参考:
https://juejin.cn/post/6844903426740994056#heading-8
https://zhuanlan.zhihu.com/p/152207687