Dropbox API(Python)を使ってみる

レンタルサーバ上にWordPressを構築する準備をしています。
で、バックアップをどうするかって話がでてきました。
サーバ上にバックアップ取得しておくのは当然なんだけど、リモートサイトにバックアップデータをコピーしておきたい。

いろいろ調べた結果、「Dropboxへファイル転送する」ことにした。
使う言語は、自分の勉強のためにもJavaがいいなぁ、と思ったけど、僕が使っているレンタルサーバではJavaが使えない。Rubyは気にあるけどあんまり使ったことない。
というわけで、Pythonでツール作ることにした。

使っている環境
FreeBSD8.1(64bit)
Python 2.6.5
dropbox-python-sdk-1.5.1


(1)はじめに
あたりまえのことだけど、まずはDropboxのアカウントを作成する必要があります。(こちらは手順省略)
なお、アカウント作成後は、ブラウザから操作できるので、自PC(Windows)にDropboxのクライアントを入れなくても大丈夫。

その後、Dropbox上で自分のアプリを登録します。
これは何かというと、DropboxではAPIへのアクセスにOAuth認証を使っているから。

通常、自分(人間)がDropboxにアクセスするときは、ユーザ・パスワードを入力します。
が、アプリがDropboxにアクセスするときに、ユーザ・パスワードではなくApp keyといったものを使います。そして、初回アクセス時に、ユーザにアクセス許可を求めます。許可をもらったら、次回以降はApp keyで自由にログインできるようになります。

これにより、第3者が作成したアプリを使うときに、自分のユーザ・パスワードをアプリに教えることなく、アプリを使用することができます。

ユーザ側では、「もし怪しいアプリがアクセスしてきたら、許可しなければいい」ということになります。もしくは許可設定を解除して、アクセス禁止にするとか。

というわけで、アプリ登録を行います。

(2)My appsの登録
Dropboxにログインした後、左下のほうにある「その他」から「開発者」を選択します

myapps01

Developer home画面に移るので、My appsをクリック。

myapps02

Create an appをクリック

myapps03

App typeは、Core APIのまま。
App nameは、アプリ名を適当につける。
Descriptionはアプリの説明。第3者にアプリを提供するときに、アプリ名といっしょにアプリの説明も表示される(たぶん)
Accessは、App folderにしておく。これにしておくと、Dropbox上に「アプリ名」のフォルダが作成されて、その中だけがアプリで操作できる領域になる。

myapps04

アプリ登録が終わると一覧に表示される。ここでOptionを選択すると、

myapps05

App keyとApp secretが発行されているはず。これをつかって、アプリのコーディングするときに、ユーザ・パスワードのかわりにこれを使う。(あと、Access typeもコードの中で指定することになる)

myapps06

つぎにSDKの準備です。

(3)Dropbox SDK

Developer home画面で、SDKsを選ぶとSDK一覧が出てきます。
今回はPython版をダウンロードします。
tutorialをクリックして、tutorialに進みます。

dropbox_sdk01

ダウンロードしたdropbox-python-sdk-1.5.1.zipを展開すると、いろいろ入っていますが、展開したフォルダ直下に、setup.py があります。
これを使って、普通は以下のコマンドでSDKをインストールするのですが、僕はやってません。。

python setup.py install

SDKを展開すると、「dropbox」というフォルダがあって、この中にライブラリが入っています。なので、これをコピーしました。自分が作成したツールと同じ階層に置いておけば使えます。

(注)~~~~~~~~~~
僕の場合、レンタルサーバ(共用サーバ)を使う予定なので、サーバ上に自由にライブラリインストールができません。なので、上記のコピペ方式で行いました。

ただ、正式には「–home オプション付きでインストールスクリプトを実行」すれば自分のホームディレクトリにインストールできるみたいなので、こちらのほうがいいと思います。

python setup.py install --home=[インストールしたい場所]

ただ、任意の場所にライブラリをインストールした場合、パスが見つからない場合があるので、注意が必要です。くわしくはこちらを参考にどうぞ。
さくらのレンタルサーバで Python 外部モジュールを使う
~~~~~~~~~~~~

さてこれで、SDKの準備ができたので、ためしに動作確認してみます

(4)SDK動作確認
まず、ソースコードを以下に記載します。
こちらは「Dropboxにアクセスし、ログイン許可をもらったあと、許可をくれたユーザ情報を表示する」といった処理になっています。
tutorialの初めのほうに載っているもの、そのままです。

APP_KEY、APP_SECRETは、「(2)My appsの登録」の最後で確認した値を入力します。
ACCESS_TYPEは、app_folderを設定します。

# Include the Dropbox SDK libraries
from dropbox import client, rest, session

# Get your app key and secret from the Dropbox developer website
APP_KEY = 'INSERT_APP_KEY_HERE'
APP_SECRET = 'INSERT_SECRET_HERE'

# ACCESS_TYPE should be 'dropbox' or 'app_folder' as configured for your app
ACCESS_TYPE = 'app_folder'

sess = session.DropboxSession(APP_KEY, APP_SECRET, ACCESS_TYPE)

request_token = sess.obtain_request_token()

url = sess.build_authorize_url(request_token)

# Make the user sign in and authorize this token
print "url:", url
print "Please visit this website and press the 'Allow' button, then hit 'Enter' here."
raw_input()

# This will fail if the user didn't visit the above URL and hit 'Allow'
access_token = sess.obtain_access_token(request_token)

client = client.DropboxClient(sess)
print "linked account:", client.account_info()

アプリ実行前に、ブラウザでDropboxにログインした状態にしておきます。その後、アプリを実行すると、

python test.py

以下のような、URLが返ってきます。

url: https://www.dropbox.com/1/oauth/authorize?oauth_token=**********
Please visit this website and press the 'Allow' button, then hit 'Enter' here.

「こちらのURLにアクセスし、許可ボタンを押した後、Enterをしてください」とのこと。

なので、指定されたURLにブラウザで接続し、許可ボタンを押します。

test_exec01

成功しました、って画面が返ってくればOK。

test_exec02

その後、アプリ実行中の端末でEnterすると、

url: https://www.dropbox.com/1/oauth/authorize?oauth_token=**********
Please visit this website and press the 'Allow' button, then hit 'Enter' here.

linked account: {u'referral_link': u'https://www.dropbox.com/referrals/**********', u'display_name': u'ユーザ名', u'uid': ID番号, u'country': u'JP', ~~(省略)~~, u'email': u'メールアドレス'}

ユーザ情報らしき値が、かえってきます。

さて、ここで1つ問題があります。
このアプリを実行すると、毎回この「許可ボタンを押す」作業を行わなくてはなりません。
これだと、毎回実行するたびに人手が必要になるため、「定期的に自動実行させてファイル転送する」といったことができません。

tutorialを読み進めていっても、この問題はそのままです。
一度許可をもらったら、次回以降は「許可済み」と認識してもらう形にする必要があります。

マニュアルのどこかに解決策があるのかもしれませんが、う~ん、よくわからんなぁ・・・。
「Cookieみたいなものに、認証許可コードみたいなものを保存しておく」ってかんじだと思うんだけど。

しばし悩んだ結果、DropboxSDK内のExampleがすごい参考になる、っていうか、僕がやりたいことが全部あるじゃないか、ってことに気付いたので、つぎにExampleの動作確認に移ります。

(5)DropboxSDKに付属のExampleを試す。お、これいいよ!!
DropboxSDKのフォルダ内に、exampleというフォルダがあります。

dropbox-python-sdk-1.5.1\example

このなかに、cli_client.pyというサンプルがあります。
実行すると、Dropbox> というプロンプトが返ってきます。
その状態でコマンド入力して、Dropboxに対して操作できます。
こんな感じになります。

(注)LANG設定をしていない場合、lsコマンドでエラーが出ます。export LANG=ja_JP.UTF-8 を実行してから、cli_client.pyを起動してください。

# python cli_client.py
Dropbox>
Dropbox> login
url: https://www.dropbox.com/1/oauth/authorize?oauth_token=*******
Please authorize in the browser. After you're done, press enter.

Dropbox>
Dropbox> ls
test.txt
新しいテキスト ドキュメント.txt
Dropbox>
Dropbox> logout
Dropbox> exit
#

初回実行時は、まずloginします。するとauthenticationが求められますので、URLにアクセスして「許可ボタン」を押します。
その後、lsと打つと、dropbox上のファイル一覧が表示されました!!

終了するときは、logoff, exitとコマンド入力するのですが、上記ではlogoffをしていません。

exitのみで終了させると、token_store.txt というファイルが残ります。このファイルが、「アプリ許可済み」状態を保持してくれます。中には「token.key, token.secret」が記載されている模様。この状態であれば、次回実行時にauthentication「許可ボタン」を求められません。
(logoffで終了すると、token_store.txtファイルは削除されれ、次回実行時は再びauthentication「アプリの許可」を求められます)

ファイル転送時は、putコマンドで行えます。

Dropbox> put put.txt put.txt
Dropbox> ls
put.txt
test.txt
新しいテキスト ドキュメント.txt
Dropbox>

ファイル名変更は、mvで

Dropbox> mv put.txt rename.txt
Dropbox>
Dropbox> ls
rename.txt
test.txt
新しいテキスト ドキュメント.txt

削除は、rmで

Dropbox> rm rename.txt
Dropbox>
Dropbox> ls
test.txt
新しいテキスト ドキュメント.txt
Dropbox>

さてこれで、やりたいことは実現できそうです。最後に、cli_client.pyの中を軽く見ておきたいと思います。

(6)cli_client.pyを読む

ざっくりとcli_client.pyのつくりを見てみます。簡略化したものを以下に書きます。

 
def command(login_required=True):     # ←ここは、デコレータ定義
    """a decorator for handling authentication and exceptions"""
        (処理内容)

class DropboxTerm(cmd.Cmd):
    def __init__(self, app_key, app_secret):
        (処理内容)

    @command()
    def do_ls(self):
        (処理内容)

    @command()
    def do_cd(self, path):
        (処理内容)

    @command(login_required=False)
    def do_login(self):
        (処理内容)
        :         :
        :         :

class StoredSession(session.DropboxSession):
    """a wrapper around DropboxSession that stores a token to a file on disk"""
    TOKEN_FILE = "token_store.txt"

    def load_creds(self):
        (処理内容)

    def write_creds(self, token):
        (処理内容)

    def delete_creds(self):
        (処理内容)

    def link(self):
        (処理内容)
        :         :
        :         :

def main():
    if APP_KEY == '' or APP_SECRET == '':
        exit("You need to set your APP_KEY and APP_SECRET!")
    term = DropboxTerm(APP_KEY, APP_SECRET)
    term.cmdloop()

こんなのあるって、知らなかったんだけど、このサンプルでは、Cmdオブジェクトのcmdloopメソッドを使い、CLIコマンドラインを作っています。
メソッドdo_*****()を用意しておくと、コマンド*****が使えるようになる。
サンプルでは、DropboxTermクラスでCmdオブジェクトを使っていて、いろいろコマンドが定義されています。

(参考)
[Python-FIT][Python] Python の cmd パッケージがマイナーで不遇
5.19.1 Cmdオブジェクト

Dropboxのauthentication(アプリの許可済み)の状態を保持する処理については、StoredSessionクラスの中で行っています。その中のlinkメソッドで「許可を取得」し、write_credsメソッドでtoken.key, token.secretをtoken_store.txtに書き込んでいます。

なので、サンプルに記載されているものを流用すれば、自分のやりたかったことは実現できそうです。
とりあえず一安心。。


12/26追記
cli_client.pyの中で、class DropboxTermの中の各メソッド(def do_***)の前に、@command()がついています。これはPythonのデコレータになります。
Javaで言うところのアノテーションみたいな書き方なんだけど、機能は全く違う模様。

「def command」の関数定義のところでデコレータの定義がされています。中の処理は、Dropboxへのauthentication許可済みかどうかをチェックしています。
これを@command()といった形で書くことで、デコレートしている。
デコレータとして使うことにより、class DropboxTermの中の各メソッド(def do_***)が実行されるときに、「def command」の処理も組み込まれる。

cli_client.pyの場合、「def do_***」が実行されるとき、最初にまずDropboxにログイン済みかどうかチェックするようになっている。

(参考)
Pythonのデコレータ構文: 綺麗なコードが良い!ぶろぐ
Pythonのデコレーターの使い方 – mfumiの日記

こうしてみると、デコレータは便利そうに思えたんだけど、詳しいことをいろいろ調べてみると、自分で使いこなすのは難しそう・・・。
特に、デコレータへの引数渡し、の部分がよくわからん。

(参考)
Python – デコレータ

以上


2/13追記
今回、cli_client.pyを試してみましたが、そのままではバックアップ用のスクリプトとして使えません。
なので、少し改修して使えるようにしました。

詳細を、次回のエントリーに載せました。
→ Dropbox API(Python)を使ってみる2 « Walk on apps.

Advertisements