目次


魅力的なPython: XMLとPythonのあれこれ

Python用のXMLツールの紹介

Comments

Python は、多くの点で XML 文書を処理するための理想的な言語です。Perl、REBOL、REXX、および TCL のように、柔軟なスクリプト言語であり、強力なテキスト操作機能を備えています。さらに、たいていのテキスト・ファイル (またはストリーム) のタイプよりも、XML 文書は通常、制御が多く複雑なデータ構造でエンコードされますので、"行を読んでそれらを正規表現と比較する" というおなじみのスタイルのテキスト処理では、XML を適切に構文解析して処理するには適しません。Python は、うれしいことに (他の多くの言語と比べるとさらにそういえるのですが)、複雑なデータ構造 (通常はクラスや属性がある) を処理する分かりやすい方法と、XML の構文解析、処理、および生成に役立つ一連の XML 関連モジュールの両方を備えています。

XML について覚えておくべき 1 つの一般的な概念は、XML 文書は、妥当性検査を行う処理または妥当性検査なしの処理どちらでも行えるということです。前者の処理のタイプでは、まず "文書タイプ定義" (DTD: Document Type Definition) を前もって読み取って、それからそれが適用される XML 文書を読み取ることが必要です。この場合の処理では、XML 文書一般の簡単な構文規則を評価するだけでなく、DTD の固有の文法制約も評価します。多くの場合、妥当性検査をしない処理が適切です (また、一般に実行が速く、プログラミングが簡単です) -- この処理では、文書作成者がその文書を扱うアプリケーションの規則に従っていることを前提とします。以下に説明している大半のモジュールでは、妥当性検査は実行しません。妥当性検査オプションがある場合は、説明でそれを示しています。

Vaults of Parnassus (参考資料を参照) は、最近の Python リソースを見つける標準的な手段となっています。以下に説明するすべてのモジュールは、そのサイトから見つけることができます (それぞれのモジュール所有者のサイトへのリンクがあります)。特に Vaults では、PyXML 配布については、tarball と Win32 インストーラーの両方があります。

Python の XML-SIG (XML special interest group)

Python 用の一連の XML ツールを保守する努力の多く -- というより大半 -- は、XML-SIG のメンバーによって行われています。他の Python Special Interest Group と同じく、XML-SIG は、メーリング・リスト、リスト・アーカイブ、有用な参照先、資料、標準パッケージ、および他のリソースを保守しています。おそらく、この記事で要旨を読んだ後に、XML-SIG の Web ページから始めることが最善でしょう。

この記事で特に関心の対象となるのは、XML-SIG が PyXML 配布を保守していることです。このパッケージには、この記事で説明している多くのモジュール、いくつかの "入門" 資料、例示用コード、そして XML-SIG が配布に含めることに決めたものが何でも入っています。既定のパッケージには、必ずしもそれぞれのモジュールまたはツールの "最新の" バージョンが含まれているとは限りませんが、それでも PyXML 配布は手始めにダウンロードするのに適しています。組み込まれていないモジュールや、組み込み済みのモジュールの新規バージョンも、後からいつでも追加することができます (さらにそれ自体は組み込まれていないモジュールの多くについても、PyXML 配布が提供するサービスをあてにすることができます)。

モジュール: XMLLIB モジュール (標準)

この "out of the box" 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)\n'
              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 Parser Toolkit の 'expat' のラッパーです。'expat' は、C で作成されたライブラリーで、それを使いたいどの言語からでも使用可能にすることを意図して作られています。'expat' は、妥当性検査を行わないので、Python によって書かれたパーサーよりもかなり高速になるはずです。[sgmlop] は、目的の点で [pyexpat] と似ています。これも妥当性検査をせず、C で作成されています。[pyexpat] は MacOS バイナリーとして、[sgmlop] は Win32 バイナリーとして使用できます。これ以外のプラットフォームを使用する必要がある場合は、C コンパイラーを使用して、ご使用のプラットフォーム用のモジュールを作成する必要があります。

[xmlproc] は Python によって書かれたパーサーで、ほぼ完全な妥当性検査を実行します。妥当性検査をするパーサーを必要とする場合は、現在のところ [xmlproc] が Python における唯一の選択肢です。加えて、[xmlproc] は、他のパーサーが提供していない、様々な高水準の検査用インターフェースを提供しています。

Simple API for XML (SAX) を使用することにした場合、-- 多くの他のツールがその上で作成されている実績からすれば、洗練されたアプリケーションを作りたければこれを使用すべきですが -- パーサーから得られるデータを分類する作業の多くを実行させることができます。PyXML の配布物では、[xml.sax.drivers] には、ここで説明したものすべてを含め、多数のパーサーのシン・ラッパー (thin wrapper) が、'drv_*.py' という形式の名前で含まれています。ただし一般には、実行されるシステム上で使用可能な「ベスト」のパーサーを自動的に選択する、高水準の SAX 機能によってドライバーにアクセスします。

パーサーの選択
     #------------- selecting the best parser ---------------#
      from xml.sax.saxext import *
      parser = XMLParserFactory.make_parser()

パッケージ: SAX

SAX は、使用するパーサーを自動的に選択できると先に述べましたが、そもそも SAX とは何なのでしょうか ? 適切な答えは以下のとおりです。

"SAX (Simple API for XML) は、XML パーサーの共通パーサー・インターフェースです。これによってアプリケーション作成者は、どのパーサーを実際に使用するかに関係なく、XML パーサーを使用するアプリケーションを作成することができます。(XML 用の JDBC と考えてください。)"
-- Lars Marius Garshol 氏の 'SAX for Python' より (参考資料を参照)

SAX は -- それが API を提供するパーサー・モジュールと同じく -- 本質的には XML 文書の順次処理プログラムです。これはおおむね [xmllib] の例と同じような方法で使用しますが、抽象度のレベルはいくぶん高くなります。パーサー・クラスを定義する代わりに、アプリケーション・プログラマーは、使用するすべてのパーサーと共に登録する 'ハンドラー' クラスを定義します。DocumentHandler、DTDHandler、EntityResolver および ErrorHandler の、4 つの SAX インターフェース (それぞれにいくつかのメソッドがある) を定義する必要があります。これらすべての基本クラスが提供されていますが、たいていの場合、これら 4 つのインターフェースすべてから継承される、'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)\n'
                  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] と比較して注目すべき 2 つの小さな点は以下のとおりです: 'parseFile()'/'parse()' メソッドはストリーム / ストリング全体を扱うので、パーサーをフィードするループを作成する必要はありません。'characters()' は読み込まれたデータの一部で、そのサイズおよび位置と渡されたストリングとは、引き数で示されます。'ch' 変数が 'characters()' に渡したそのとおりのものであるとは考えないでください。

パッケージ: DOM

DOM は、XML 文書の非常に高水準なツリー・ベースの表現です。このモデルは Python に固有のものではなく、一般的な XML モデルです (さらに詳細については、参考資料を参照してください)。Python の DOM パッケージは、SAX 上で作成され、PyXML 配布に組み込まれています。長さの制約があるためこの記事にコード例を記載することはできませんが、優れた一般的説明が XML-SIG の "Python/XML HOWTO" にあります。

文書オブジェクト・モデル (Document Object Model) は、XML 文書のツリー・ベースの表現を規定しています。最上位の文書インスタンスがツリーのルートになっています。それには、最上位エレメントのインスタンスである、単一の子エレメントがあります。この子エレメントには、コンテンツおよび他のサブエレメントを表す子ノードがあります。それにはさらに子がある場合があり、これが繰り返されていきます。機能が定義されており、それによって結果のツリーを好みの仕方で検討したり、エレメントや属性値にアクセスしたり、ノードを挿入および削除したり、ツリーを XML に変換し直すことなどができます。

DOM は、XML 文書を変更するために役立ちます。なぜなら、DOM ツリーを作成し、それを新規ノードの追加やサブツリーの移動によって変更し、それから新規 XML 文書を出力として生成することができるからです。DOM ツリーを自分で構築して、それを XML に変換することもできます。これは、ただ <tag1>...</tag1> とファイルに書き込んでいくよりも、たいていの場合 XML 出力を生成するより柔軟な方法となります。

パッケージ: Pyxie

[pyxie] モジュールは、XML-SIG からの PyXML 配布物を基に作成されるものであり、XML 文書に対する追加の高水準インターフェースを提供します。[pyxie] は、次の 2 つの基本的な事柄を行います: まず、XML 文書を、さらに構文解析しやすい行指向の形式に変換します。さらに、XML 文書を、たどることができるツリーとして扱うメソッドを提供します。[pyxie] によって使用される行指向の PYX 形式は、言語には依存せず、いくつかの言語のツールを使用することができます。一般に、grep、sed、awk、bash、perl、または標準 Python モジュールの [string] および [re] などの、おなじみの行指向のテキスト処理ツールを使用して文書の PYX 表現を処理する方が、その XML 表現を処理するよりもずっと簡単です。ダウンストリーム次第では、XML から PYX への変換によって多くの作業を省ける場合があります。

XML 文書をツリーのように扱うという [pyxie] の概念は、DOM での概念と似ています。DOM 標準は、数多くのプログラム言語にわたって広範なサポートを得つつあるので、大半のプログラマーにとっては、XML 文書をツリー表現することが必要な場合に、[pyxie] よりも、DOM 標準に目を向けたほうが良いでしょう。

モジュール: XML Parser

あまりにも一般的な名が付いている -- そしておそらく幾分誤解を招きやすい名である -- 'XML Parser' は、XML 文書の構文および形式が正しいかどうかを検査するにはやや古いツールとなっています (ただし DTD に対する妥当性検査を実行する場合はそうではありません。) 1 つの特別なユーティリティー・クラスでは、検査にいくらかのあいまいさがあって、(XML が要求しているすべての終了タグがないような場合でも) HTML 文書をパスさせます。(このモジュールの適用度の範囲は、PyXML 配布のものほど幅広くありません)。しかし、たんにいくらかの XML 文書を検査することだけが必要な場合には、XML Parser は簡単に実行でき便利です。このモジュールは、コマンド行から実行する場合は、使用しているプログラムにインポートする必要さえなく、STDIN 上の XML 文書を検査することができます。これが最も簡単な XML Perser の使用法でしょう。

XML_OBJECTS 0.1

他の高水準ツールと同様、xml_objects も SAX 上で作成されています。xml_objects の目的は、XML 文書を、さらに容易にリレーショナル・データベースに保管できる、2 次元のグリッド表現に変換することです。

次回予告

次回の "魅力的な Python" のコラムでは、Python プログラマーが XML 文書処理に使用できるおそらく最も強力なツールである、xml.dom モジュールを詳しく考察したいと思います。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, XML
ArticleID=230446
ArticleTitle=魅力的なPython: XMLとPythonのあれこれ
publish-date=06012006