PythonでExpatを使用してXML文書の高速な解析を行う
(バージョン2.0以上の)Pythonでは、Expatというライブラリが提供する機能(xml.parsers.expatモジュール)により、XML文書の構文高速にを解析することができる。
解析器の種類としてはストリーム型かつイベント駆動型となり、文書の内容を先頭から読み進めて、要素などを発見したところで内部的な「イベント」が発生し、特定の(ハンドラ)関数が呼び出される仕組みになっている。
なお、Pythonではこの他に、同じタイプのxml.saxと、XMLツリー全体を保持するタイプのxml.dom(の系列)も用意されていて、いずれもバージョン2.0から利用できる(ここでは扱わない)。
- Python ライブラリリファレンス: 13.5 xml.parsers.expat -- Expat を使った高速な XML 解析
- Python ライブラリリファレンス: 13.6 xml.dom -- 文書オブジェクトモデル (DOM) API
- Python ライブラリリファレンス: 13.7 xml.dom.minidom -- 軽量な DOM 実装
- Python ライブラリリファレンス: 13.8 xml.dom.pulldom -- 部分的な DOM ツリー構築のサポート
- Python ライブラリリファレンス: 13.9 xml.sax -- SAX2 パーサのサポート
関連URL:
- http://qune.cside.com/archives/000474.html - C言語でExpatを扱っている日本語の情報
- http://www.atmarkit.co.jp/fxml/rensai/xdk03/xdk03.html - JavaのSAXによるXML文書の操作
その仕組み上、複雑な構造のXMLファイルを処理して色々とたどるのには向かないと思われるが、逆に、高速であることを活かして、構造の単純な設定ファイルなどの処理に使うと便利かもしれない。
下はリファレンスを参考にした使用例。実際には各ハンドラ関数に色々な処理を追加することになる。
[任意]ファイル名: expattest.py
#! /usr/bin/python # -*- encoding: utf-8 -*- from xml.parsers.expat import ParserCreate import sys def start_element(name, attrs): print u"要素の開始: %s".encode("utf-8") % name.encode("utf-8") for (key, val) in attrs.iteritems(): # 辞書の各キーに対する処理を行う print u" 属性: %s = %s".encode("utf-8") % (key.encode("utf-8"), val.encode("utf-8")) def end_element(name): print u"要素の終了: %s".encode("utf-8") % name.encode("utf-8") def char_data(data): if data != "\n": print u" 文字データ: %s".encode("utf-8") % data.encode("utf-8") p = ParserCreate() p.buffer_text = True # 文字データが細切れにならないように # ハンドラ関数の関連付け p.StartElementHandler = start_element # 要素の開始 p.EndElementHandler = end_element # 要素の終了 p.CharacterDataHandler = char_data # 文字データ if len(sys.argv) < 2: print >> sys.stderr, u"エラー: 入力ファイルが指定されていません".encode("utf-8") sys.exit(1) infile = sys.argv[1] try: f = open(infile) except IOError: print >> sys.stderr, u'エラー: ファイル "%s" が開けませんでした'.encode("utf-8") % infile sys.exit(1) p.ParseFile(f) # 処理を実行
(2009/3/6)import文とprint文を微調整
ここでは、例として、エクスポートしたはてなダイアリーのXMLファイルの形式*1を持ったファイルを用意して実験する。
[任意]ファイル名: test.xml
<?xml version="1.0" encoding="UTF-8"?> <diary> <day date="2008-06-01" title=""> <body> 6/1の本文 </body> <comments> <comment> <username>6/1のコメント1ユーザ名</username> <body>6/1のコメント1本文</body> <timestamp>6/1のコメント1日時</timestamp> </comment> <comment> <username>6/1のコメント2ユーザ名</username> <body>6/1のコメント2本文</body> <timestamp>6/1のコメント2日時</timestamp> </comment> </comments> </day> <day date="2008-06-02" title=""> <body> 6/1の本文 </body> <comments> <comment> <username>6/2のコメント1ユーザ名</username> <body>6/2のコメント1本文</body> <timestamp>6/2のコメント1日時</timestamp> </comment> <comment> <username>6/2のコメント2ユーザ名</username> <body>6/2のコメント2本文</body> <timestamp>6/2のコメント2日時</timestamp> </comment> </comments> </day> <day date="2008-06-03" title=""> <body> 6/3の本文 </body> <comments> <comment> <username>6/3のコメント1ユーザ名</username> <body>6/3のコメント1本文</body> <timestamp>6/3のコメント1日時</timestamp> </comment> <comment> <username>6/3のコメント2ユーザ名</username> <body>6/3のコメント2本文</body> <timestamp>6/3のコメント2日時</timestamp> </comment> </comments> </day> </diary>
下は実行例。
$ ./expattest.py test.xml 要素の開始: diary 要素の開始: day 属性: date = 2008-06-01 属性: title = 要素の開始: body 文字データ: 6/1の本文 要素の終了: body 要素の開始: comments 要素の開始: comment 要素の開始: username 文字データ: 6/1のコメント1ユーザ名 要素の終了: username 要素の開始: body 文字データ: 6/1のコメント1本文 要素の終了: body 要素の開始: timestamp 文字データ: 6/1のコメント1日時 要素の終了: timestamp 要素の終了: comment 要素の開始: comment 要素の開始: username 文字データ: 6/1のコメント2ユーザ名 要素の終了: username 要素の開始: body 文字データ: 6/1のコメント2本文 要素の終了: body 要素の開始: timestamp 文字データ: 6/1のコメント2日時 要素の終了: timestamp 要素の終了: comment 要素の終了: comments 要素の終了: day 要素の開始: day 属性: date = 2008-06-02 属性: title = 要素の開始: body 文字データ: 6/1の本文 要素の終了: body 要素の開始: comments 要素の開始: comment 要素の開始: username 文字データ: 6/2のコメント1ユーザ名 要素の終了: username 要素の開始: body 文字データ: 6/2のコメント1本文 要素の終了: body 要素の開始: timestamp 文字データ: 6/2のコメント1日時 要素の終了: timestamp 要素の終了: comment 要素の開始: comment 要素の開始: username 文字データ: 6/2のコメント2ユーザ名 要素の終了: username 要素の開始: body 文字データ: 6/2のコメント2本文 要素の終了: body 要素の開始: timestamp 文字データ: 6/2のコメント2日時 要素の終了: timestamp 要素の終了: comment 要素の終了: comments 要素の終了: day 要素の開始: day 属性: date = 2008-06-03 属性: title = 要素の開始: body 文字データ: 6/3の本文 要素の終了: body 要素の開始: comments 要素の開始: comment 要素の開始: username 文字データ: 6/3のコメント1ユーザ名 要素の終了: username 要素の開始: body 文字データ: 6/3のコメント1本文 要素の終了: body 要素の開始: timestamp 文字データ: 6/3のコメント1日時 要素の終了: timestamp 要素の終了: comment 要素の開始: comment 要素の開始: username 文字データ: 6/3のコメント2ユーザ名 要素の終了: username 要素の開始: body 文字データ: 6/3のコメント2本文 要素の終了: body 要素の開始: timestamp 文字データ: 6/3のコメント2日時 要素の終了: timestamp 要素の終了: comment 要素の終了: comments 要素の終了: day 要素の終了: diary
関連記事:
*1:手元でエクスポートしたものを参考にしたので、完全な形かどうかは不明