フォト蔵APIのPython wrapperを作る Vol.2 multipart/form-data

前回からの続きです。

フォト蔵APIのPython wrapperを作る Vol.1 Basic認証 - hachi's blog

multipart/form-data

さあ、認証も通ったのでいよいよ写真のアップしてみましょう。 しかし、写真をアップロードするためには、multipart/form-dataを使う必要があります。 multipart/form-dataとは、テキストデータと画像などのバイナリデータをバウンダリで区切って まとめてサーバに送る方法です。 詳しくは RFC 2388 を読んでください。

残念ながら、Pythonの標準ライブラリだけではこのmultipart/form-dataでデータをPOSTすることができません。 PyPIにはMultipartPostHandlerっていうライブラリもあるのですが、 今回はこれは使わずに自分で実装していきます。

例によって、うだうだ説明するよりソース読んだほうが分かりやすいでしょ。

#!/usr/bin/env python
import httplib
import mimetypes

boundary = '-----photozou.py-----'
disposition = 'Content-Disposition: form-data; name="%s"'
#アップロードする写真のデータ
values = open(file_name, 'rb').read()
#写真の情報
data = {}
data['album_id'] = album_id
data['photo_title'] = photo_title
data['description'] = description
data['tag'] = tag
data['comment'] = comment
data['data_type'] = data_type
data['year'] = year
data['month'] = month
data['day'] = day

#送信するbodyを作る
lines = []
lines.append('--' + boundary)
lines.append(disposition % 'photo' + '; filename="%s"' % file_name)
lines.append('Content-Type: %s' % mimetypes.guess_type(file_name)[0])
lines.append('')
lines.append(values)
for k, v in data.iteritems():
    lines.append('--' + boundary)
    lines.append(disposition % k)
    lines.append('')
    lines.append(v)
lines.append('--' + boundary + '--')
lines.append('')
data_str = '\r\n'.join(lines)

#ヘッダを作る
header = {'Authorization' : 'Basic %s' % auth_info}
header['Content-Type'] = 'multipart/form-data; boundary=%s' % boundary
conn = httplib.HTTPConnection('api.photozou.jp')
conn.request('POST', '/rest/photo_add/', data_str, header)
res = conn.getresponse()
print res.status, res.reason
print res.read()

辞書に格納したデータをバウンダリで区切りながらbodyの文字列を作っていきます。 画像ファイルの場合は、ファイル名を書いたり、mimetypeを指定したりしなきゃダメみたいです。 大事なポイントとしては、バウンダリをbodyに追加するときは先頭に'--'を付加するって事。 そして最後のバウンダリは先頭と最後に'--'を付加する必要ありです。 それと改行コードがCRLFって事。

そんなこんなで

写真をアップロードするという最大の山場を越えましたので、 後はぼちぼちと他の機能をラップしていくだけです。 全APIをラップできたら、それ使ったアプリもつくりたいですね。

参考ページ

urllib2のみでmultipart/form-dataを送る - YAMAGUCHI::weblog

Http client to POST using multipart/form-data « Python recipes « ActiveState Code

フォト蔵APIのPython wrapperを作る Vol.1 Basic認証

はじめに

整理してない写真データが増えてきたので、とりあえずバックアップがてらフォト蔵に上げたいとずっと思っていたのですが、 なんたって今まで撮りためた数GBある写真をいまさら手動で上げれるほど暇じゃありません。 幸いにも、フォト蔵にはAPIが公開されているので、 PythonからAPI叩いて自動でアップロードできるようにしようっていうのが今回の目的。 その中で技術的なポイントを幾つかまとめていければと思っています。

環境について

開発環境はこんな感じ。 標準ライブラリだけを使って実装しようと思っているので、 基本的にpython2.7さえあれば、 OSはwindowsでもmacでもlinuxでも動くはずです。

作ったものはBitbucketにあげておきます。

https://bitbucket.org/bananapenguin/photozou.py

Basic認証

フォト蔵APIはユーザ認証にBasic認証を採用しています。 Basic認証は名前の通り基本的な認証方法ですが、 ネットワークが盗聴されていたら一発でパスワードがバレてしまうというちょっと危ない認証方法でもあります。 しかし、実装が簡単なので未だにいろんなところで使われていたりします。

認証が通らなければ話が進まないので、まずはこのBasic認証部分を実装していきます。 フォト蔵APIにはnopという認証テスト用のAPIがあります。これを使って試していきます。

実際に動くものがあったほうがわかりやすいと思うので、先にコードを載せます。

#!/usr/bin/env python

import httplib
import base64

auth_info = base64.b64encode('%s:%s' % ('username', 'password'))
header = {'Authorization' : 'Basic %s' % auth_info}
conn = httplib.HTTPConnection('api.photozou.jp')
conn.request('GET', '/rest/nop/', '', header)
res = conn.getresponse()
print res.status, res.reason
print res.read()

実行結果

200 OK
<?xml version="1.0" encoding="UTF-8" ?>
<rsp stat="ok">
<info>
<user_id>1021877</user_id>
</info>
</rsp>

認証に成功するとこんな感じのXMLが返ってきます。

認証失敗の場合はこんな感じ

401 Unauthorized
<rsp stat="fail">
  <err code="ERROR_UNKNOWN" msg="Log in failed." />
  <err code="AUTH_FAILURE" msg="Authorization Required" />
</rsp>

つまり、 Basic認証ってユーザ名とパスワードを:(コロン)で繋いで、 base64エンコードした文字列を こんな感じでhttpヘッダーに追加するだけでいいんですね。

Authorization: Basic XXXXXXXXXXXXXXXXXXX

このbase64の文字列がバレたら、パスワードもバレるってこと。 このあたりがあまりよろしくないと言われている所以です。

何はともあれ

認証に成功したので、nopのラッパーも完成です。 簡単ですね。たった5行でhttpでアクセスしてBasic認証までできるんだから。 pythonって素晴らしい。

次は実際に写真をアップするphoto_addのラッパーを作っていきます。

フォト蔵APIのPython wrapperを作る Vol.2 multipart/form-data - hachi's blog