conta's diary

思ったこと、やったことを書いてます。 twitter: @conta_

python + google app engineでspreadsheetにアクセス

spreadsheetをDBにするってなんかかっこいいよね!ってことでやってみました。
最初はgae+flaskでspreadsheetをイジイジしようと思ったんですが、
全然うまく行かなかったので、Googleさんが提供してくれてるwebappを使うことに。。。
とりあえず今回はspreadsheetにアクセスできることを確認しました。

*ここを参考にしました
http://code.google.com/apis/spreadsheets/data/1.0/developers_guide_python.html
http://code.google.com/apis/accounts/docs/AuthSub.html
http://gdata-python-client.googlecode.com/hg/pydocs/gdata.spreadsheet.service.html#SpreadsheetsService
http://mars.shehas.net/~tmatsuo/misc/gdata-ja.html

認証方法

調べた感じ、Googleさんの認証を行うには下記の3つの方法があるっぽい

(1)Oauth

TwitterFacebookと同じやり方。
ただ、アプリケーションの登録とかがめんどくさそう。

(2)AuthSub

Googleのログイン画面に飛ばされる感じ。
なんか楽そう。

(3)ClientLogin

GoogleアカウントとPassを関数にぶっこんでProgramableLogin的な感じ。
これが一番簡単だったけど、Google先生曰く、GAEでは使わないほうがベターだよね、らしい。
まぁ確かにパスワードを生でやり取りするのはちょっとね。。。
スクリプトを書いて自分のPCやサーバーから動かすならこの方法が楽かも。

(3)はネットで調べたらそこそこ出てくるので省略。
ってことで今回は(2)を試してみた。

方針

基本的にはここを参考にするとわかりやすい、きがする。
http://code.google.com/apis/accounts/docs/AuthSub.html

要約すると、
(1)auth_tokenという一回限りのトークンを発行
(2)そのトークンを使ってトークンをアップグレードしてセッション管理してね
みたいな感じらしいです。

a) まず、GenerateAuthSubURLって関数で認証URLを発行。
b) すると、googleの認証画面が出てきて、指定したサービスに対して許可しますか?って聞かれる。
c) Acceptすると、リダイレクトされる。この時に、リダイレクトURLの後ろにワンタイムトークンみたいなものがくっついてくる。
d) c)で作られたトークンを使ってUpgradeToSessionTokenで恒久的なトークンを作成。
って流れだと思います。

ソースコード

どっかのサンプルコードを改造しました。ソースコード汚い。。。
結構情報が古かったり、サンプルコードそのままじゃ動かなかったりしたので大変だった。。。
http://gdata-python-client.googlecode.comのPyDocを読みながら、試行錯誤して動きました。
この例では、コンソールに自分のプライベートなスプレッドシートの一覧が表示されます。

import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.api import users
import atom.url
import gdata.service
import gdata.alt.appengine
import gdata.spreadsheet.service
import settings
import logging


class Fetcher(webapp.RequestHandler):
    
    def GetAuthSubUrl(self):
        next = 'http://localhost:8080/'
        scope = 'https://spreadsheets.google.com/feeds/'
        secure = False
        session = True
        self.gd_client = gdata.spreadsheet.service.SpreadsheetsService()
        return self.gd_client.GenerateAuthSubURL(next, scope, secure, session);

    def get(self):
        # Write our pages title
        self.response.out.write("""<html><head><title>
            Google Data Feed Fetcher: read Google Data API Atom feeds</title>""")
        self.response.out.write('</head><body>')
        # Allow the user to sign in or sign out
        next_url = atom.url.Url('http', settings.HOST_NAME, path='/')
        
        logging.debug(users.get_current_user());
        if users.get_current_user():
            self.response.out.write('<a href="%s">Sign Out</a><br>' % (
              users.create_logout_url(str(next_url))))
        else:
            self.response.out.write('<a href="%s">Sign In</a><br>' % (
              users.create_login_url(str(next_url))))
            
        self.response.out.write('<div id="main"></div>')
        self.response.out.write(
            '<div id="sidebar"><div id="scopes"><h4>Request a token</h4><ul>')
        self.response.out.write('<li><a href="%s">Google Documents</a></li>' % 
                                (self.GetAuthSubUrl()))
        self.response.out.write('</ul></div><br/><div id="tokens">')

        # Initialize a client to talk to Google Data API services.
        client = gdata.service.GDataService()
        gdata.alt.appengine.run_on_appengine(client)
    
        session_token = None
        # Find the AuthSub token and upgrade it to a session token.
        auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
        
        
        if auth_token:
            self.gd_client.SetAuthSubToken(auth_token)
            logging.debug('upgrading...')
            self.gd_client.UpgradeToSessionToken()
            logging.debug('get feed...')
            feed = self.gd_client.GetSpreadsheetsFeed()
            logging.debug(feed)
            
        if session_token and users.get_current_user():
            client.token_store.add_token(session_token)
        elif session_token:
            client.current_token = session_token

        


def main():
    logging.getLogger().setLevel(logging.DEBUG)
    application = webapp.WSGIApplication([('/.*', Fetcher), ], debug=True)
    wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
    main()

これを、とりあえずローカルで実行。
認証後、ログに大量のXMLが流れてきます。
タイトルを見ると、ちゃんと自分のシートがあることが確認できます。

かなり時間がかかりました^^;
力不足を体感しまくり。。。

スプレッドシートをDBにするには、
gdata.spreadsheet.text_db
ってモジュールが使いやすかったので、それを使っていこうかと思います。

認証方法(3)のClientLoginを使って、DB操作方法だけ勉強してから
認証方法(2)で認証の勉強をして、
Webアプリケーションのに組み込んでいくとすんなりいけるかと。

SpreadsheetをDBにできたら、GAEのDB使わなくてもいいかもねー、とか思ったりしてます。
まぁやってみないとパフォーマンスもわからないよね。

ドキュメント全部英語だし、サンプルコード少ないし、結構大変だなーと思う次第です。

おしまい!