Google App EngineでOAuth使ってTwitterAPIを扱う

Google App Engine Pythonの話です。
Google App EngineTwitterAPIを扱うためには何使うのがいいのかなぁ」
とちょっとググってみたところ、Tweepyというのが見つかりました。
これを使ったサンプルもtweepy-examplesにあったのでそれなら何となくわかりそうだなぁと思い触ってみました。
(結局サンプルは使わなかったのですが、本人が作ってるぽいし多分見ておいて損はないんだと思う)



まず前提として、やりたいことは

  • App EngineからOAuthを使ったtwitterAPIを叩く
  • そのとき他人のプライベートなつぶやきデータを見たいわけではなく、単純にAPIがOAuth認証になってるからOAuthを使いたいだけ
    • つまりユーザが僕のアプリを使って認証するわけじゃないのでcallbackとかそういうの要らない

です。



ということで、自分のアプリに紐づいたConsumer 〜の2つと、そのアプリにアクセス許可をした証のAccess Token 〜の2つがあればいい。
(別のユーザの情報をやりとりするわけじゃないのでAccess Token 〜を作り出すためのRequest 〜は要らない。)




はじめにやらなくちゃいけないのはアプリに紐づいたConsumer 〜なので、Twitterにアプリの登録をしないといけないですね。
そうするとそのアプリ用のページが作成され、そこにConsumer keyとConsumer secretが書いてあるのでその2つを見ながら「後で使うよ」と肝に銘じておく。




で、そのページの右側にMy Access Tokenっていうのがあるのでそれをクリック。
そうすると自分用のAccess TokenとAccess Token Secretが出てくる。
今度はこの2つを見ながら「後で使うよ」と肝に銘じる。
(今「命じる」って変換出てきて違和感あったので調べたんですけど「命令する」ではなく「刻み付ける」だから「銘じる」なんですね。何で「銘じる」なのか考えたことなかった。)




さて、そしたらTweepyを持ってきます。
githubにあるので、git clone使えばいいですね。

git clone git://github.com/joshthecoder/tweepy.git

tweepyというディレクトリができるのでその中に入ります。

[5:48]% cd tweepy
CHANGELOG     INSTALL       README        setup.py      tools/
CONTRIBUTORS  LICENSE       doc/          tests.py      tweepy/

この中のtweepyってディレクトリが必要なものですね。
最終的にはこのディレクトリだけGoogle App Engine用に持ってけば良さそう。



実際に使えるか一旦試してみます。

[5:50]% python2.5 
>>> import tweepy

はい。問題なくimportできるみたい。



続いてさっき肝に銘じていた4つを代入。

>>> consumer_token = "xxxx"
>>> consumer_secret = "xxxx"
>>> access_token = "xxxx"
>>> access_token_secret = "xxxx"

そして認証処理用にセット。

>>> auth = tweepy.OAuthHandler(consumer_token, consumer_secret)
>>> auth.set_access_token(access_token, access_token_secret)

最後にTwitter APIを叩くためのインスタンス生成。

>>> oauth_api = tweepy.API(auth)

この作ったoauth_apiのメソッドがそのままTwitter APIのインタフェースになってるということですね。
おぉ。簡単っぽい。



どんなメソッドがあるか眺めてみます。

>>> dir(oauth_api)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', '_lookup_users', '_pack_image', 'add_list_member', 'api_root', 'auth', 'blocks', 'blocks_ids', 'cache', 'create_block', 'create_favorite', 'create_friendship', 'create_list', 'create_saved_search', 'destroy_block', 'destroy_direct_message', 'destroy_favorite', 'destroy_friendship', 'destroy_list', 'destroy_saved_search', 'destroy_status', 'direct_messages', 'disable_notifications', 'enable_notifications', 'exists_block', 'exists_friendship', 'favorites', 'followers', 'followers_ids', 'friends', 'friends_ids', 'friends_timeline', 'friendships_incoming', 'friendships_outgoing', 'geo_id', 'get_list', 'get_saved_search', 'get_status', 'get_user', 'home_timeline', 'host', 'is_list_member', 'is_subscribed_list', 'list_members', 'list_subscribers', 'list_timeline', 'lists', 'lists_memberships', 'lists_subscriptions', 'lookup_users', 'me', 'mentions', 'nearby_places', 'parser', 'public_timeline', 'rate_limit_status', 'related_results', 'remove_list_member', 'report_spam', 'retry_count', 'retry_delay', 'retry_errors', 'retweet', 'retweeted_by', 'retweeted_by_ids', 'retweeted_by_me', 'retweeted_to_me', 'retweets', 'retweets_of_me', 'reverse_geocode', 'saved_searches', 'search', 'search_host', 'search_root', 'search_users', 'secure', 'send_direct_message', 'sent_direct_messages', 'set_delivery_device', 'show_friendship', 'subscribe_list', 'test', 'trends', 'trends_available', 'trends_current', 'trends_daily', 'trends_location', 'trends_weekly', 'unsubscribe_list', 'update_list', 'update_profile', 'update_profile_background_image', 'update_profile_colors', 'update_profile_image', 'update_status', 'user_timeline', 'verify_credentials']

home_timelineとかそれっぽいのがありますね。



(ちなみに、tests.pyをself.apigrepすれば、APIをどうやって使ってるかの例がいくつか見れそうです。)

[5:59]% ack self.api tests.py
        self.api = API(BasicAuthHandler(username, password))
        self.api.retry_count = 2
        self.api.retry_delay = 5
        self.api.public_timeline()
        self.api.home_timeline()
        self.api.friends_timeline()
        self.api.user_timeline()
        self.api.user_timeline('twitter')
        self.api.mentions()
        self.api.retweeted_by_me()
# ... 省略

home_timelineみたいに名前がそのまんまのメソッドは良いのですが、インタフェースと名前が違うメソッドは困ります。
そんな時はTwitterのAPIドキュメントにあるAPIのURLでtweepy/api.pyを検索すれば良さそうです。
こんな感じに書いてあるので、ファイルの中身の該当場所を確認すれば良さげ。

[5:59]% ack statuses/home_timeline tweepy/api.py
    """ statuses/home_timeline """
        path = '/statuses/home_timeline.json',

allowed_paramに受け取れる引数が書いてあるのでそれを渡せばいいみたい。



大体わかったところでメソッド呼び出し。
statuses/showはどうやらget_statusってメソッドらしい。
使ってみます。

>>> status_id = '36771601944940544'
>>> res = oauth_api.get_status(id=status_id)
>>> res
<tweepy.models.Status object at 0x58dd30>

行けたっぽい。



中身はどんな感じなっているかというと、

>>> dir(res)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__getstate__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', '_api', 'author', 'contributors', 'coordinates', 'created_at', 'destroy', 'favorite', 'favorited', 'geo', 'id', 'id_str', 'in_reply_to_screen_name', 'in_reply_to_status_id', 'in_reply_to_status_id_str', 'in_reply_to_user_id', 'in_reply_to_user_id_str', 'parse', 'parse_list', 'place', 'retweet', 'retweet_count', 'retweeted', 'retweets', 'source', 'source_url', 'text', 'truncated', 'user']

APIの戻りがそのままプロパティになってるみたいですね。



確認。

>>> print res.text
ツイッターなどのネットにおいて女の子が「お兄ちゃん」とかガチ呼びしたり「甘いもの大好きです♪」とか自己紹介文に書いてるような人よりも、「おっぱい!」「○○(キャラ名)ペロペロ」とか変態発言してる人のほうが美人が多いことを男は知らないかもしれない。ので、公式RTして教えてあげよう。

上手くいってます。



他のメソッドも試してみます。

>>> res = oauth_api.retweeted_by(id=status_id)
>>> res
[<tweepy.models.Status object at 0x5b0750>, <tweepy.models.Status object at 0x5b0770>, <tweepy.models.Status object at 0x5b07b0>, <tweepy.models.Status object at 0x5b07d0>, <tweepy.models.Status object at 0x5b07f0>, <tweepy.models.Status object at 0x5b0810>, <tweepy.models.Status object at 0x5b0830>, <tweepy.models.Status object at 0x5b0850>, <tweepy.models.Status object at 0x5b0870>, <tweepy.models.Status object at 0x5b0890>, <tweepy.models.Status object at 0x5b08b0>, <tweepy.models.Status object at 0x5b08d0>, <tweepy.models.Status object at 0x5b08f0>, <tweepy.models.Status object at 0x5b0910>, <tweepy.models.Status object at 0x5b0930>, <tweepy.models.Status object at 0x5b0950>, <tweepy.models.Status object at 0x5b0970>, <tweepy.models.Status object at 0x5b0990>, <tweepy.models.Status object at 0x5b09b0>]

複数結果が帰ってくる場合はリストになるみたいですね。



中身の確認。

>>> dir(res[0])
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__getstate__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', '_api', 'contributors_enabled', 'created_at', 'description', 'destroy', 'favorite', 'favourites_count', 'follow_request_sent', 'followers_count', 'following', 'friends_count', 'geo_enabled', 'id', 'id_str', 'is_translator', 'lang', 'listed_count', 'location', 'name', 'notifications', 'parse', 'parse_list', 'profile_background_color', 'profile_background_image_url', 'profile_background_tile', 'profile_image_url', 'profile_link_color', 'profile_sidebar_border_color', 'profile_sidebar_fill_color', 'profile_text_color', 'profile_use_background_image', 'protected', 'retweet', 'retweets', 'screen_name', 'show_all_inline_media', 'statuses_count', 'time_zone', 'url', 'utc_offset', 'verified']

こっちも同様にプロパティになってるみたい。



念のため確認。

>>> print res[0].id_str
61465161

ちゃんと取得できてます。



ということで、Tweepyで簡単に操作できるようです。
詳しい使い方はdocに書いてあるんだろうけど、僕がpythonのドキュメントの作り方を知らないので見てないです。
結構色んな所にサンプルもあるし、多分どうにかなるんじゃないだろうか。