読者です 読者をやめる 読者になる 読者になる

はてな認証APIとElementTreeを組み合わせる

Python

Pythonを使って、はてな認証APIで遊んでみました。
「はてな認証APIを使った認証システムをウェブアプリケーションに組み込む方法」に従って、認証処理を進めると、最終的に、認証API側からJSON、もしくはXMLで応答が返ってきます。
この応答結果をPythonオブジェクトに変換する方法を考えてみます。


JSONを使えば、話は簡単です。simplejsonjson-pyを使って、Python組み込み型に変換できます。

import simplejson

def parse_json(json_text):
    return simplejson.loads(json_text)
import json

def parse_json(json_text):
	return json.read(json_text)

この方法は、JSONライブラリに応答文書の解析を任せられるので、単純明快、最も素直な方法だと言えるでしょう。
一般的に言って、データ直列化にはJSONやYAMLを選択した方が、コードは簡潔になり、データの内容も明確になります。しかし、はてな認証APIの応答のような、単純な文書ならば、XMLでも充分JSONに太刀打ちできるはずです。


そこで、cElementTreeを使って、次のようなコードを書いてみました。

from cElementTree import ElementTree as etree

def parse_xml(reader)
    t = etree.parse(reader)
    err = t.find('/has_error').text
    if err == 'false':
        user_node = t.find('/user')
        return dict([(k, user_node.findtext(k)) for k in ("name", "image_url", "thumbnail_url")])
    else:
        return dict(message = t.find('/error/message').text)

ここでのreaderは、urllib2やファイルオブジェクトのような入力ストリームです。
XMLの構造に従って、has_error要素を調べ、真ならばerror要素を、偽ならばuser要素を処理していきます。
認証成功時にはname, image_url, thumbnail_urlをキーとするdictを返し、失敗時にはmessageをキーとするdictを返します。


parse_xml関数は7行。JSONの単純さには、まだまだ及びません。
user要素が含まれていればerror要素は含まれない、という条件を利用すれば多少は短くなりそうです。
それに、user要素をdictに変換する部分は、Elementオブジェクトがiterableであるという性質を利用すれば、もっと簡潔に書けそうですね。


以上の点をふまえて、次のバージョンを作ってみました。

from cElementTree import ElementTree as etree

def parse_xml(reader):
    tree = etree.parse(reader)
    user_node = tree.find("/user")
    if user_node:
        return dict([(element.tag, element.text) for element in user_node])
    else:
        return dict(message = tree.find("/error/message").text)

6行になり、だいぶすっきりしました。さらに頑張ってみます。
has_error要素に続く要素が、user要素かerror要素と仮定すれば、もっと短くなりそうです。

from cElementTree import ElementTree as etree

def parse_xml(reader):
	return dict([(e.tag, e.text) for e in etree.parse(reader).getroot()[1]])

これで、一行になりました!
欠点は、ElementTreeに慣れていないと、何を行っているか全く分からないところでしょうか。さらに、上のどのコードにも言えることですが、実際には例外処理を行い、もう少し防衛的なコードにしなくてはなりません。


ちなみに、cElementTreeの代わりに、libxml2によるElementTree API実装であるlxmlを使っても同じように動作します。


timeitを利用して、1000回試行した際のベンチマークを採ってみましたが、cElementTree一行バージョンの方が、かなり高速に動作しました。この結果はちょっと意外でした。

cElementTree 0.10670208931 sec
lxml 0.1233689785 sec
simplejson 0.423406124115 sec
json-py 1.7200050354 sec


メモリー上のXML、JSONを解析したので、ネットワークの影響は含まれていません。
また、XML版とJSON版で処理内容が多少異なるので、数値は目安程度のものです。