在許多情況下,Python 是使用 XML 文檔的理想語言。像 Perl、REBOL、REXX 和 TCL 一樣,它是一種靈活的腳本語言,並且有強大的文本操作能力。而且,XML 文檔除了編碼大多數類型的文本文件(或流文件),通常還編碼大量復雜的數據結構。文本處理中常見的“讀取幾行,並將它們與一些規則表達式比較”樣式通常不能很好地適合對 XML 進行徹底語法分析和處理。幸好,Python(與大多數其它語言相比)不僅有直接處理復雜數據結構的方法(通常使用類和屬性),還有許多 XML 相關的模塊可以幫助語法分析、處理和生成 XML。
關於 XML,要記住一個總體概念:可以驗證或非驗證方式處理 XML 文檔。在以前的處理類型中,讀取 XML 文檔之前,必須先讀取“文檔類型定義”(DTD)。這種情況下,處理將總體計算 XML 文檔的簡單句型規則,還將計算 DTD 的特定語法約束。大多數情況下,使用非驗證處理就可以了(通常運行更快,更適合程序) -- 我們相信文檔創建者遵循文檔范圍的規則。在下面討論的大多數模塊都是非驗證型;如果存在驗證選項,則描述將指出。
中心資源庫 (Vaults of Parnassus)最近已成為查找 Python 資源的標准方法。可以在那個站點上找到所有以下討論的模塊。特別地,可以在資源庫中找到 PyXML 發行版,它是 tar 文件和 Win32 形式的安裝程序。
Python 的 XML 特殊興趣組 (XML-SIG)
XML-SIG 的成員執行了許多 -- 或大部分 -- 維護 Python 一部分 XML 工具的任務。與其它 Python SIG 一樣,XML-SIG 要維護郵件發送列表、列表檔案、有用的參考大權、文檔、標准包和其它資源。閱讀了本文中的概述後,最好從 XML-SIG Web 頁面入手。
根據本文中講述的特定重點,XML-SIG 維護了 PyXML 發行版。這個包包含了許多本文中討論的模塊,一些“入門”文檔,一些演示代碼和其它一些 XML-SIG 決定放入該發行版的東西。給定的包也許不會總是包含每個獨立模塊或工具的最新版本,但下載 PyXML 發行版是個好主意。以後,可以隨時添加任何未包含的模塊,或者已包含模塊的新版本(以及許多 PyXML 發行版提供的服務所未包含的模塊)。
模塊:XMLLIB 模塊(標准)
“不包括在標准發行版中”,Python 1.5.* 帶有模塊 [xmllib]。Python 1.6 也許結合了更多 XML-SIG 的成就,但它仍是測試版。[xmllib] 是一個非驗證的低級語法分析器。[xmllib] 的工作方式是用應用程序覆蓋 XMLParser 類,並提供處理文檔元素(如特定或類屬標記,或字符實體)的方法。
作為正在使用的 [xmllib] 示例,PyXML 發行版包括一個叫做 'quotations.dtd' 的 DTD,以及這個 DTD 的文檔 'sample.xml'。以下的代碼顯示了 'sample.XML' 中每段引言的前幾行,並生成了非常簡單的未知標記和實體的 ASCII 指示符。經過分析的文本作為連續流來處理,所使用的任何累加器都由程序員負責(如標記中的字符串 (#PCDATA),或所遇到的標記的列表/詞典)。
嘗試 XMLlib 的代碼
#-------------------- try_xmllib.py --------------------# import xmllib, string class QuotationParser(xmllib.XMLParser): """Crude xmllib extractor for quotations.dtd document""" def __init__(self): xmllib.XMLParser.__init__(self) self.thisquote = '' # quotation accumulator def handle_data(self, data): self.thisquote = self.thisquote + data def syntax_error(self, message): pass def start_quotations(self, attrs): # top level tag print '--- Begin Document ---' def start_quotation(self, attrs): print 'QUOTATION:' def end_quotation(self): print string.join(string.split(self.thisquote[:230]))+'...', print '('+str(len(self.thisquote))+' bytes)' self.thisquote = '' def unknown_starttag(self, tag, attrs): self.thisquote = self.thisquote + '{' def unknown_endtag(self, tag): self.thisquote = self.thisquote + '}' def unknown_charref(self, ref): self.thisquote = self.thisquote + '?' def unknown_entityref(self, ref): self.thisquote = self.thisquote + '#' if __name__ == '__main__': parser = QuotationParser() for c in open("sample.XML").read(): parser.feed(c) parser.close()
其它語法分析模塊
PyXML 發行版包含了幾個具有各種功能的附加語法分析模塊。提供這些模塊是為了對基本 [XMLlib] 模塊做一些改進。
[pyexpat] 是 GPL 方式的 XML 語法分析器工具箱 'expat' 的封裝程序。'expat' 是用 C 語言寫的庫,這就意味著任何想要利用它的語言都可以使用它。'expat' 是非驗證型,因此它比原來的 Python 語法分析器快很多。[sgmlop] 的目的與 [pyexpat] 相同。它也是非驗證型,而且也用 C 語言編寫。[pyexpat] 可以作為 MacOS 二進制使用,[sgmlop] 可以當作 Win32 二進制使用;但如果您需要使用不同的平台,那麼就要用 C 編譯器為您自己的平台構建模塊。
[xmlproc] 是 python 原有的語法分析器,它執行幾乎完整的驗證。如果需要驗證型語法分析器, [xmlproc] 是 Python 當前唯一的選擇。同樣,[XMLproc] 提供其它語法分析器所不具備的各種高級和測試接口。
如果決定使用 XML 的簡單 API (SAX) -- 它應該用於復雜的事物,因為其它大部分工具都構建在它之上 -- 將為您完成許多語法分析器的分類工作。在 PyXML 發行版中,[XML.sax.drivers] 包含許多語法分析器的瘦封裝程序,包括所有那些已討論過的、名稱形式為 'drv_*.py' 的語法分析器。但是,一般使用高級 SAX 設施訪問驅動器,該設施自動選擇系統上“最佳”的可用語法分析器:
選擇語法分析器
#------------- selecting the best parser ---------------# from xml.sax.saxext import * parser = XMLParserFactory.make_parser()包:SAX
以上,我們已提到 SAX 會自動選擇要使用的語法分析器;但 SAX 是什麼?一個較好的答案是:
“SAX(XML 的簡單 API)是 XML 語法分析器的公用語法分析器接口。它允許應用程序作者編寫使用 XML 語法分析器的應用程序,但是它卻獨立於所使用的語法分析器。(將它看作 XML 的 JDBC。)”
-- Lars Marius Garshol, SAX for Python
SAX -- 如同它提供的語法分析器模塊的 API -- 基本上是一個 XML 文檔的順序處理器。使用它的方法與 [xmllib] 示例極其相似,但更加抽象。定義語法分析器類,應用程序員將定義一個 'handler' 類,該類將注冊所使用的語法分析器。必須定義四個 SAX 接口(每個接口都有幾個方法):DocumentHandler、DTDHandler、EntityResolver 和 ErrorHandler。已提供了所有這些接口的基類,但大多數情況下,最簡單的方法是繼承 'HandlerBase',因為這個類繼承了所有四個接口。可以不用考慮想要做什麼。某些代碼將幫助解釋這一點;該樣本執行與 [XMLlib] 示例相同的任務。
嘗試 SAX 的樣本代碼
#--------------------- try_sax.py ----------------------# import string from xml.sax import saxlib, saxexts class QuotationHandler(saxlib.HandlerBase): """Crude sax extractor for quotations.dtd document""" def __init__(self): self.in_quote = 0 self.thisquote = '' def startDocument(self): print '--- Begin Document ---' def startElement(self, name, attrs): if name == 'quotation': print 'QUOTATION:' self.in_quote = 1 else: self.thisquote = self.thisquote + '{' def endElement(self, name): if name == 'quotation': print string.join(string.split(self.thisquote[:230]))+'...', print '('+str(len(self.thisquote))+' bytes)' self.thisquote = '' self.in_quote = 0 else: self.thisquote = self.thisquote + '}' def characters(self, ch, start, length): if self.in_quote: self.thisquote = self.thisquote + ch[start:start+length] if __name__ == '__main__': parser = saxexts.XMLParserFactory.make_parser() handler = QuotationHandler() parser.setDocumentHandler(handler) parser.parseFile(open("sample.XML")) parser.close()
與 [XMLlib] 相比,關於示例要注意兩件小事:'parseFile()'/'parse()' 方法處理整個流/字符串,所以不必為語法分析器創建循環;向 'characters()' 提供了大量數據,自變量會指出數據的大小和位置以及傳遞的字符串。不要假設變量 'ch' 將以什麼形式傳送給 'characters()'。
包:DOM
DOM 是一種 XML 文檔的高級樹型表示。該模型並非特定於 Python,而是一種普通 XML 模型。Python 的 DOM 包是針對 SAX 構建的,並且包括在 PyXML 發行版中。由於篇幅關系,沒有將代碼樣本加到本文中,但在 XML-SIG 的 "Python/XML HOWTO" 中給出了一個極好的總體描述。
“文檔對象模型”(DOM) 為 XML 文檔指定了樹型表示。頂級文檔實例是樹的根,它只有一個子代,即頂級元素實例;這個元素有表示內容和子元素的子節點,他們也可以有子代。定義的函數允許隨意遍歷結果樹,訪問元素和屬性值,插入和刪除節點,以及將樹轉換回 XML。
DOM 可以用於修改 XML 文檔,因為可以創建一棵 DOM 樹,通過添加新節點和來回移動子樹來修改這棵樹,然後生成一個新的 XML 文檔作為輸出。您也可以自己構造一棵 DOM 樹,然後將它轉換成 XML;用這種方法生成 XML 輸出比僅將
包:PyxIE
[pyxie] 模塊從 XML-SIG 構建到 PyXML 發行版之上,它為 XML 文檔提供了附加的高級接口。[pyxie] 將完成兩項基本操作:它將 XML 文檔轉換成一種更易於進行語法分析的基於行的格式;並且它提供了將 XML 文檔當作可操作樹處理的方法。[pyxIE] 所使用的基於行的 PYX 格式是獨立於語言的,其工具適用於幾種語言。總之,文檔的 PYX 表示與其 XML 表示相比,更易於使用常見的基於行的文本處理工具進行處理,如 grep、sed、awk、bash、perl,或標准 Python 模塊,如 [string] 和 [re]。根據結果,從 XML 轉換到 PYX 可能節省許多工作。
[pyxIE] 將 XML 文檔當作樹處理的概念與 DOM 中的思路相似。由於 DOM 標准得到許多編程語言的廣泛支持,那麼如果 XML 文檔的樹型表示是必需的,大多數程序員會使用 DOM 標准而非 [pyxIE]。
模塊:XML 語法分析器
“XML 語法分析器”這個叫法太籠統,也許還不太確切,實際上它是一種比較舊的工具,用於檢查 XML 文檔是否符合句法以及其結構是否完好(但對於 DTD 無效)。一個附加的實用程序類在進行檢查時會產生一些小麻煩,它會讓 Html 文檔通過檢查(即使那些文檔沒有 XML 必需的結束標記)。這個模塊的適用范圍並不能覆蓋 PyXML 發行版中的所有模塊。但如果只想驗證一些 XML 文檔,那麼設置和運行 XML 語法分析器還是很容易的。如果從命令行運行,則該模塊將在 STDIN 上檢查 XML 文檔,甚至不用將它導入程序。這是最簡單的做法。
XML_OBJECTS 0.1
如同其它高級工具,xml_objects 構建在 SAX 之上。構建 xml_objects 的目的是將 XML 文檔轉換成一個兩維網格表示,從而更易於在關系數據庫中存儲。
下一步
在下一個“可愛的 Python”專欄中,我們將進一步研究 xml.dom 模塊,它可能是 Python 程序員用來處理 XML 文檔的功能最強大的工具。