PythonでElementTreeを使ってXMLをパースする際に注意すべき点がある

 PythonXMLを扱う際には、ElementTreeというライブラリを使うとSAXっぽいAPIXMLをパースできる。Python 2.5以上なら、デフォルトでxml.etree.ElementTreeというモジュールがあるのでそれを使うと良い。もしくは、cElementTreeモジュール(仕様は同じだがCで書かれてて速い)を使うと良い。

import xml.etree.ElementTree as ElementTree
parser = ElementTree.XMLParser(target = ParserTarget())
ElementTree.parse(filename, parser)

 とかやると動く。ParserTargetクラスはあらかじめ自分で定義しておくもので、このクラスにstart, end, data, closeの4種類のメソッドを定義しておくと、パーサがXMLをパースしながらそれらのメソッドを適宜呼び出してくれるので、XMLから有用な情報を取り出すことができる。startは開きタグ、endは閉じタグ、dataはタグ内のデータ文字列を引数として呼び出される。closeはなんに使うのか忘れた。どのタグに対してこのメソッドを呼ぶ、みたいなカスタマイズなどはできないので、そこら辺のメソッドディスパッチは自分で書く必要があるけれど、そういった部分はどうせパースするXMLの構造に合わせたアドホックなものになりがちなので、ライブラリとしてはこれで必要十分という感じだと思う。
 注意すべき点というのは、同じタグに対するdataメソッドが複数回に分けて呼ばれる場合がある、という事である。与えられるデータが大きかった場合の事を想定すると仕様としてはこうなる事は納得できるが、テストで小さなデータばかりを扱っていると、なかなかこの問題に気づかない。実際、問題の原因が分からず、3日ほど悩んでしまった。(Decという3文字のデータがD ecと2回に分割されて呼び出されたりするのである。)
 処理内容にもよるが、データが分割されて送られてくると処理が非常に面倒くさい。この問題を解決するためには、これまでdataメソッドですぐにデータを処理していたのを、endメソッドまで処理を遅延させるのが一番簡単である。
 簡単であるとは言え、実際には以下のようにそれなりにめんどくさい処理が必要になる

  • startメソッドで今どんなタグを開いている最中なのかをインスタンス変数に保存しておく。
  • dataメソッドではdataを一時的にインスタンス変数にいれる。同一タグに対する2回目以降のメソッド呼び出しでは、ちゃんとデータを連結するようにする。
  • endメソッドが呼び出された際に、インスタンス変数に入れておいたデータを処理する。

 ここまで書いてきておいてなんだが、SAXで処理することってあんまりないような気がしてきた。xpathを使うよね普通は。