歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Python:使用基於事件驅動的SAX解析XML

SAX的特點:

  • 是基於事件的 API
  • 在一個比 DOM 低的級別上操作
  • 為您提供比 DOM 更多的控制
  • 幾乎總是比 DOM 更有效率
  • 但不幸的是,需要比 DOM 更多的工作

基於對象和基於事件的接口

您可能已經知道語法分析器有兩類接口 - 基於對象的(如:DOM)和基於事件(如:SAX)的接口。

DOM是基於對象的語法分析器的標准 API。

作為基於對象的接口,DOM 通過在內存中顯示地構建對象樹來與應用程序通信。對象樹是 XML 文件中元素樹的精確映射。

DOM 易於學習和使用,因為它與基本 XML 文檔緊密匹配。然而,對於大多數應用程序,處理 XML 文檔只是其眾多任務中的一種。例如,記帳軟件包可能導入 XML 發票,但這不是其主要活動。計算帳戶余額、跟蹤支出以及使付款與發票匹配才是主要活動。記帳軟件包可能已經具有一個數據結構(最有可能是數據庫)。DOM 模型不太適合記帳應用程序,因為在那種情況下,應用程序必須在內存中維護數據的兩份副本(一個是 DOM 樹,另一個是應用程序自己的結構)。至少,在內存維護兩次數據會使效率下降。對於桌面應用程序來說,這可能不是主要問題,但是它可能導致服務器癱瘓。

對於不以 XML 為中心的應用程序,SAX 是明智的選擇。實際上,SAX 並不在內存中顯式地構建文檔樹。它使應用程序能用最有效率的方法存儲數據。

基於事件驅動

基於事件的語法分析器將事件發送給應用程序。這些事件類似於用戶界面事件,例如,浏覽器中的 ONCLICK 事件或者 Java 中的 AWT/Swing 事件。

事件通知應用程序發生了某件事並需要應用程序作出反應。在浏覽器中,通常為響應用戶操作而生成事件:當用戶單擊按鈕時,按鈕產生一個 ONCLICK 事件。

在 XML 語法分析器中,事件與用戶操作無關,而與正在讀取的 XML 文檔中的元素有關。有對於以下方面的事件:

  • 元素開始和結束標記
  • 元素內容
  • 實體
  • 語法分析錯誤

圖 3 顯示語法分析器在讀取文檔時如何生成事件。

清單 1 顯示了 XML 格式的清單。它詳細列出了不同公司對 XML 培訓的收費。圖 4 顯示了價目表文檔的結構。

清單 1. pricelist.xml

<?xml version="1.0"?>
<xbe:price-list xmlns:xbe="http://www.psol.com/xbe2/listing8.1">
  <xbe:product>XML Training</xbe:product>
  <xbe:price-quote price="999.00"  vendor="Playfield Training"/>
  <xbe:price-quote price="699.00"  vendor="XMLi"/>
  <xbe:price-quote price="799.00"  vendor="WriteIT"/>
  <xbe:price-quote price="1999.00" vendor="Emailaholic"/>
</xbe:price-list> 

圖 4. 價目表的結構

XML 語法分析器讀取並解釋該文檔。每當它識別出文檔中的某些內容,就會生成一個事件。

讀取 清單 1 時,語法分析器首先讀取 XML 聲明並生成文檔開始事件。當它遇到第一個開始標記 <xbe:price-list> 時,語法分析器生成它的第二個事件來通知應用程序已經遇到了 price-list 元素。

接下來,語法分析器看到 product 元素的開始標記(為簡單起見,在本文其余部分,我將忽略名稱空格和縮進空格)並生成它的第三個事件。

在開始標記後,語法分析器看到 product 元素的內容: XML Training ,它產生另一個事件。

下一個事件指出 product 元素的結束標記。語法分析器已經完成了對 product 元素的語法分析。到目前為止,它已經激發了 5 個事件: product 元素的 3 個事件,一個文檔開始事件和一個 price-list 開始標記事件。

語法分析器現在移動到第一個 price-quote 元素。它為每個 price-quote 元素生成兩個事件:一個開始標記事件和一個結束標記事件。

是的,即使將結束標記簡化為開始標記中的 / 字符,語法分析器仍然生成一個結束事件。

有 4 個 price-quote 元素,所以語法分析器在分析它們時生成 8 個事件。最後,語法分析器遇到 price-list 的結束標記並生成它的最後兩個事件:結束 price-list 和文檔結束。

如圖 5 所示,這些事件共同向應用程序描述了文檔樹。開始標記事件意味著“轉到樹的下一層”,而結束標記元素意味著“轉到樹的上一層”。

圖 5. 語法分析器如何隱含地構建樹

請注意,語法分析器傳遞了足夠信息以構建 XML 文檔的文檔樹,但是與 DOM 語法分析器不同,它並不顯式地構建該樹。

為何選擇SAE而不是DOM

現在,我敢肯定你已經糊塗了。應該使用哪一種類型的 API,應該何時使用它 - SAX 還是 DOM?不幸的是,這個問題沒有明確的答案。這兩種 API 中沒有一種在本質上更好;他們適用於不同的需求。

經驗法則是在需要更多控制時使用 SAX;要增加方便性時,則使用 DOM。

例如,DOM 在腳本語言中很流行。

采用 SAX 的主要原因是效率。SAX 比 DOM 做的事要少,但提供了對語法分析器的更多控制。當然,如果語法分析器的工作減少,則意味著您(開發者)有更多的工作要做。

而且,正如我們已討論的,SAX 比 DOM 消耗的資源要少,這只是因為它不需要構建文檔樹。

在 XML 早期,DOM 得益於 W3C 批准的官方 API 這一身份。逐漸地,開發者選擇了功能性而放棄了方便性,並轉向了 SAX。

SAX 的主要限制是它無法向後浏覽文檔。實際上,激發一個事件後,語法分析器就將其忘記。如您將看到的,應用程序必須顯式地緩沖其感興趣的事件。 

使用Python解析XML的時候,需要 import xml.sax 和 xml.sax.handler

xml.sax

xml.sax提供了3個函數以及 SAX 異常類

make_parser方法

創建並返回一個SAX XMLReader對象

xml.sax.make_parser([parser_list])

  • parser_list - 可選參數,解析器列表
parse方法

創建一個 SAX 解析器並解析xml文檔

xml.sax.parse(filename_or_stream, handler[, error_handler])  

  • file_or_stream:xml文件名
  • handler:必須是一個ContentHandler的對象
  • error_handler:如果指定該參數,errorhandler必須是一個SAX ErrorHandler對象
parseString方法

創建一個XML解析器並解析xml字符串

xml.sax.parseString(string, handler[, error_handler])

  • string: xml字符串
  • handler:必須是一個ContentHandler的對象
  • error_handler:如果指定該參數,errorhandler必須是一個SAX ErrorHandler對象

 

ContentHandler類方法介紹

  • characters(content)方法
    • 調用時機
    • 從行開始,遇到標簽之前,存在字符,content的值為這些字符串。
    • 從一個標簽,遇到下一個標簽之前, 存在字符,content的值為這些字符串。
    • 從一個標簽,遇到行結束符之前,存在字符,content的值為這些字符串。
    • 標簽可以是開始標簽,也可以是結束標簽。
  • startDocument()方法:文檔啟動的時候調用。
  • endDocument()方法:解析器到達文檔結尾時調用。
  • startElement(name, attrs)方法:遇到XML開始標簽時調用,name是標簽的名字,attrs是標簽的屬性值字典。
  • endElement(name)方法:遇到XML結束標簽時調用。

Python 解析XML實例

要解析的XML

<?xml version="1.0"?>
<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解析XML的Python源代碼

#!/usr/bin/python

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

執行結果:

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

《Python核心編程 第二版》.(Wesley J. Chun ).[高清PDF中文版] http://www.linuxidc.com/Linux/2013-06/85425.htm

《Python開發技術詳解》.( 周偉,宗傑).[高清PDF掃描版+隨書視頻+代碼] http://www.linuxidc.com/Linux/2013-11/92693.htm

Python腳本獲取Linux系統信息 http://www.linuxidc.com/Linux/2013-08/88531.htm

在Ubuntu下用Python搭建桌面算法交易研究環境 http://www.linuxidc.com/Linux/2013-11/92534.htm

Python 語言的發展簡史 http://www.linuxidc.com/Linux/2014-09/107206.htm

Copyright © Linux教程網 All Rights Reserved