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))
廣告