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) − 在这里,您注册感兴趣事件的回调,然后让解析器处理文档。当您的文档很大或内存有限时,这很有用,它在从磁盘读取文件时解析文件,并且整个文件永远不会存储在内存中。

  • Document Object Model (DOM) − 这是万维网联盟的推荐,其中整个文件被读取到内存中并以分层(基于树)的形式存储,以表示 XML 文档的所有特征。

处理大型文件时,SAX 的处理速度显然不如 DOM。另一方面,仅使用 DOM 会严重消耗您的资源,尤其是在处理许多小型文件时。

SAX 是只读的,而 DOM 允许更改 XML 文件。由于这两个不同的 API 彼此互补,因此您无需理由不能将它们都用于大型项目。

对于我们所有的 XML 代码示例,让我们使用一个简单的 XML 文件 movies.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` 方法。

`parse` 方法

以下方法创建一个 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

ElementTree XML API

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

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

创建 XML 文件

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

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

每个元素都具有一个标签和一个 `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))
广告