Python - XML 处理


XML 是一种可移植的开源语言,它允许程序员开发可以被其他应用程序读取的应用程序,而不管操作系统和/或开发语言如何。

什么是XML?

可扩展标记语言 (XML) 是一种非常类似于 HTML 或 SGML 的标记语言。这是由万维网联盟推荐并作为开放标准提供的。

XML 对于跟踪中小型数据非常有用,无需基于 SQL 的主干。

XML 解析器架构和 API。

Python 标准库提供了一组最少但有用的接口来处理 XML。xml 包中提供了用于 XML 处理的所有子模块。

  • xml.etree.ElementTree - ElementTree API,一个简单且轻量级的 XML 处理器

  • xml.dom - DOM API 定义。

  • xml.dom.minidom - 一个最小的 DOM 实现。

  • xml.dom.pulldom - 支持构建部分 DOM 树。

  • xml.sax - SAX2 基类和便利函数。

  • xml.parsers.expat - Expat 解析器绑定。

XML 数据的两个最基本且使用最广泛的 API 是 SAX 和 DOM 接口。

  • Simple API for XML (SAX) - 在这里,您注册感兴趣事件的回调,然后让解析器继续处理文档。当您的文档很大或有内存限制时,这非常有用,它会在从磁盘读取文件时解析文件,并且整个文件永远不会存储在内存中。

  • 文档对象模型 (DOM) - 这是万维网联盟的建议,其中整个文件被读入内存并以分层(基于树)的形式存储以表示 XML 文档的所有功能。

当处理大文件时,SAX 显然无法像 DOM 那样快速地处理信息。另一方面,专门使用 DOM 确实会浪费你的资源,尤其是在许多小文件上使用时。

SAX 是只读的,而 DOM 允许更改 XML 文件。由于这两个不同的 API 实际上是相辅相成的,因此您没有理由不能将它们同时用于大型项目。

对于我们所有的 XML 代码示例,让我们使用一个简单的 XML 文件movie.xml作为输入 -

<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>

使用 SAX API 解析 XML

SAX 是事件驱动的 XML 解析的标准接口。使用 SAX 解析 XML 通常需要您通过子类化 xml.sax.ContentHandler 来创建自己的 ContentHandler。

您的 ContentHandler 处理您的 XML 风格的特定标签和属性。ContentHandler 对象提供了处理各种解析事件的方法。它所属的解析器在解析 XML 文件时调用 ContentHandler 方法。

startDocument 和 endDocument 方法在 XML 文件的开头和结尾处调用。方法characters(text)通过参数text传递XML文件的字符数据。

ContentHandler 在每个元素的开始和结束处被调用。如果解析器不处于命名空间模式,则调用方法startElement(tag,attributes)和endElement(tag);否则,调用相应的方法startElementNS和endElementNS。这里,tag是元素标签,attributes是一个Attributes对象。

在继续之前,以下是需要了解的其他重要方法 -

make_parser 方法

以下方法创建一个新的解析器对象并返回它。系统发现,创建的解析器对象将属于第一个解析器类型。

xml.sax.make_parser( [parser_list] )

这是参数的详细信息 -

  • parser_list - 可选参数,由要使用的解析器列表组成,必须全部实现 make_parser 方法。

解析方法

以下方法创建一个 SAX 解析器并使用它来解析文档。

xml.sax.parse( xmlfile, contenthandler[, errorhandler])

以下是参数的详细信息 -

  • xmlfile - 这是要读取的 XML 文件的名称。

  • contenthandler - 这必须是一个 ContentHandler 对象。

  • errorhandler - 如果指定,errorhandler 必须是 SAX ErrorHandler 对象。

parseString 方法

还有另一种方法可以创建 SAX 解析器并解析指定的 XML 字符串。

xml.sax.parseString(xmlstring, contenthandler[, errorhandler])

以下是参数的详细信息 -

  • xmlstring - 这是要读取的 XML 字符串的名称。

  • contenthandler - 这必须是一个 ContentHandler 对象。

  • errorhandler - 如果指定,errorhandler 必须是 SAX ErrorHandler 对象。

例子

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 = ""

   # Call when an element starts
   def startElement(self, tag, attributes):
      self.CurrentData = tag
      if tag == "movie":
         print ("*****Movie*****")
         title = attributes["title"]
         print ("Title:", title)

   # Call when an elements ends
   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 = ""

   # Call when a character is read
   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__"):

   # create an XMLReader
   parser = xml.sax.make_parser()
   
   # turn off namepsaces
   parser.setFeature(xml.sax.handler.feature_namespaces, 0)
   
   # override the default ContextHandler
   Handler = MovieHandler()
   parser.setContentHandler( Handler )
   
   parser.parse("movies.xml")

这将产生以下结果 -

*****Movie*****
Title: Enemy Behind
Type: War, Thriller
Format: DVD
Year: 2003
Rating: PG
Stars: 10
Description: Talk about a US-Japan war
*****Movie*****
Title: Transformers
Type: Anime, Science Fiction
Format: DVD
Year: 1989
Rating: R
Stars: 8
Description: A schientific fiction
*****Movie*****
Title: Trigun
Type: Anime, Action
Format: DVD
Rating: PG
Stars: 10
Description: Vash the Stampede!
*****Movie*****
Title: Ishtar
Type: Comedy
Format: VHS
Rating: PG
Stars: 2
Description: Viewable boredom

有关 SAX API 文档的完整详细信息,请参阅标准Python SAX API

使用 DOM API 解析 XML

文档对象模型(“DOM”)是来自万维网联盟(W3C)的跨语言 API,用于访问和修改 XML 文档。

DOM 对于随机访问应用程序非常有用。SAX 一次只允许您查看文档的一小部分。如果您正在查看一个 SAX 元素,则无法访问另一个元素。

这是快速加载 XML 文档并使用 xml.dom 模块创建 minidom 对象的最简单方法。minidom 对象提供了一个简单的解析器方法,可以从 XML 文件快速创建 DOM 树。

示例短语调用 minidom 对象的 parse( file [,parser] ) 函数来解析由 file 指定的 XML 文件到 DOM 树对象。

from xml.dom.minidom import parse
import xml.dom.minidom

# Open XML document using minidom parser
DOMTree = xml.dom.minidom.parse("movies.xml")
collection = DOMTree.documentElement
if collection.hasAttribute("shelf"):
   print ("Root element : %s" % collection.getAttribute("shelf"))

# Get all the movies in the collection
movies = collection.getElementsByTagName("movie")

# Print detail of each 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)

这将产生以下输出-

Root element : New Arrivals
*****Movie*****
Title: Enemy Behind
Type: War, Thriller
Format: DVD
Rating: PG
Description: Talk about a US-Japan war
*****Movie*****
Title: Transformers
Type: Anime, Science Fiction
Format: DVD
Rating: R
Description: A schientific fiction
*****Movie*****
Title: Trigun
Type: Anime, Action
Format: DVD
Rating: PG
Description: Vash the Stampede!
*****Movie*****
Title: Ishtar
Type: Comedy
Format: VHS
Rating: PG
Description: Viewable boredom

有关 DOM API 文档的完整详细信息,请参阅标准Python DOM API

元素树 XML API

xml 包有一个 ElementTree 模块。这是一个简单且轻量级的 XML 处理器 API。

XML 是一种树状分层数据格式。该模块中的“ElementTree”将整个 XML 文档视为一棵树。“Element”类表示该树中的单个节点。XML 文件的读写操作是在 ElementTree 级别完成的。与单个 XML 元素及其子元素的交互是在元素级别完成的。

创建 XML 文件

树是元素的分层结构,从根开始,然后是其他元素。每个元素都是使用该模块的 Element() 函数创建的。

import xml.etree.ElementTree as et
e=et.Element('name')

每个元素都有一个 tag 和 attrib 属性,它是一个 dict 对象。对于树的起始元素,attrib 是一个空字典。

>>> root=xml.Element('employees')
>>> root.tag
'employees'
>>> root.attrib
{}

您现在可以设置一个或多个要添加到根元素下的子元素。每个子元素可以有一个或多个子元素。使用 SubElement() 函数添加它们并定义其文本属性。

child=xml.Element("employee")
nm = xml.SubElement(child, "name")
nm.text = student.get('name')
age = xml.SubElement(child, "salary")
age.text = str(student.get('salary'))

每个孩子都通过append()函数添加到根中,如下所示:

root.append(child)

添加所需数量的子元素后,通过 elementTree() 函数构造一个树对象 -

tree = et.ElementTree(root)

整个树结构由树对象的 write() 函数写入二进制文件 -

f=open('employees.xml', "wb")
tree.write(f)

例子

在此示例中,树是由字典项列表构造而成的。每个字典项都包含描述学生数据结构的键值对。如此构建的树被写入“myfile.xml”

import xml.etree.ElementTree as et
employees=[{'name':'aaa','age':21,'sal':5000},{'name':xyz,'age':22,'sal':6000}]
root = et.Element("employees")
for employee in employees:
   child=xml.Element("employee")
   root.append(child)
   nm = xml.SubElement(child, "name")
   nm.text = student.get('name')
   age = xml.SubElement(child, "age")
   age.text = str(student.get('age'))
   sal=xml.SubElement(child, "sal")
   sal.text=str(student.get('sal'))
tree = et.ElementTree(root)
with open('employees.xml', "wb") as fh:
   tree.write(fh)

“myfile.xml”存储在当前工作目录中。

<employees><employee><name>aaa</name><age>21</age><sal>5000</sal></employee><employee><name>xyz</name><age>22</age><sal>60</sal></employee></employee>

解析 XML 文件

现在让我们读回上面示例中创建的“myfile.xml”。为此,将使用 ElementTree 模块中的以下函数 -

ElementTree() - 此函数被重载以将元素的层次结构读取到树对象。

tree = et.ElementTree(file='students.xml')

getroot() - 该函数返回树的根元素。

root = tree.getroot()

您可以获得元素下一级的子元素列表。

children = list(root)

在以下示例中,“myfile.xml”的元素和子元素被解析为字典项列表。

例子

import xml.etree.ElementTree as et
tree = et.ElementTree(file='employees.xml')
root = tree.getroot()
employees=[]
   children = list(root)
for child in children:
   employee={}
   pairs = list(child)
   for pair in pairs:
      employee[pair.tag]=pair.text
   employees.append(employee)
print (employees)

它将产生以下输出-

[{'name': 'aaa', 'age': '21', 'sal': '5000'}, {'name': 'xyz', 'age':'22', 'sal': '6000'}]

修改 XML 文件

我们将使用 Element 的 iter() 函数。它为给定标签创建一个树迭代器,以当前元素作为根。迭代器按文档(深度优先)顺序迭代此元素及其下面的所有元素。

让我们为所有“marks”子元素构建迭代器,并将每个 sal 标签的文本增加 100。

import xml.etree.ElementTree as et
tree = et.ElementTree(file='students.xml')
root = tree.getroot()
for x in root.iter('sal'):
   s=int (x.text)
   s=s+100
   x.text=str(s)
with open("employees.xml", "wb") as fh:
   tree.write(fh)

我们的“employees.xml”现在将进行相应修改。我们还可以使用 set() 来更新某个键的值。

x.set(marks, str(mark))