PythonとSAXでXMLを扱う

XMLを扱いたくなったのでメモメモ。

XMLを扱うためのAPIとして、DOM(Document Object Model)を構築する方法と、SAX(Simple API for XML)ってのを使う方法がある。
どちらもW3Cで標準化されているAPIだから、Pythonに限らず、いろんな言語で同じようにXMLを扱うことができる。
HTML上のJavaScriptなんかを触ってると、DOMはよく出てくるね。

DOMとSAXの違いは、DOMはメモリ上にXMLのデータを全部取り込んでから処理をするのに対して、SAXはデータを読み込んだら逐次処理していく点。
DOMは、XMLの構造をツリー構造として扱うことができるから、いろいろ高度なことができる。
ただ、メモリ上に全てのデータを展開するから、メモリを食うし、重い。
SAXは、XMLのタグの出現を「イベント」として扱うAPI
読み込んだデータをメモリ上にとっておかないから、非常に軽い。
ただし、後からXMLの構造をいじったりするのは苦手。


今回扱いたいデータはWikipediaのダンプデータ(Wikipedia:データベースダウンロード - Wikipedia)。
pages-articles.xml.bz2を解凍すると6GB程度になる。これを全部メモリ上に読み込むのは大変なので、SAXを使って見ることにする。


基本は以下のソース

import xml.sax
import xml.sax.handler
import sys

class Handler(xml.sax.handler.ContentHandler):
	def startElement(self, name, attrs):
		print "Start: " + name

	def endElement(self, name):
		print "End: " + name
	
	def characters(self, content):
		print "character:" + content
		return

def main():
	parser = xml.sax.make_parser()
	parser.setContentHandler(Handler())
	parser.parse(sys.stdin)
	return
	
if __name__=="__main__":
	main()

Handlerのメソッドとして、開始タグを見つけた場合、終了タグを見つけた場合、その他の文字を見つけた場合の処理を書いておく。
nameにタグ名、attrsに属性(辞書と同じように扱える)が入っているので適宜処理。

XMLには名前空間の考え方があって、あるXMLデータに別のXMLデータを埋め込むことが簡単にできる。
(例えば、HTML文章にSVG文章を埋め込んだり、SVG文章やXHTML文章にクリエーティブコモンズのライセンスをXML形式で埋め込むとか)

import xml.sax
import xml.sax.handler
import sys

class Handler(xml.sax.handler.ContentHandler):
	def startElementNS(self, name, qname, attrs):
		print "Start:", name, qname

	def endElementNS(self, name, qname):
		print "End:", name, qname
	
	def characters(self, content):
		print "character:" + content
		return

def main():
	parser = xml.sax.make_parser()
	parser.setContentHandler(Handler())
	parser.setFeature(xml.sax.handler.feature_namespaces, True)
	parser.parse(sys.stdin)
	return
	
if __name__=="__main__":
	main()

こうすると、nameが(名前空間,タグ名)という形式になる。attrのキーも(名前空間,属性名)になる。



shogo82148/svg2css @ GitHubを作るのにもSAXを使用中。はじめは「簡単な置換だからSAXでいけるだろー」ってSAXで作ったら、参照が必要になって、結局DOMっぽい物を作っているというね・・・。