2011年3月4日

Google提供の python ライブラリで Google Reader API を使ってみた

Google のサービスと連携するには Google Data API を使う。Googleが公式にサポートしているので、とても安心して使える。RSS リーダーとしてよく使われている Google Reader 用のAPI も。。。は、まだ公式には無い。

まぁ、色々な人が解析しており、GoogleReaderAPIのページが、詳細にまとまっている。

なので、Google 提供の python ライブラリで、Google Reader API を叩いてみた

ただ、サンプルを書いたあとで、pyrfeed内にGoogleReader 用のライブラリが含まれていたのに気付いたので、そっちを使うのがいいのかも。

インストール

MacPorts を使っているので、さくっとインストール。

% sudo port install py27-gdata

そうでないならば、gdata-python-clientから最新版をインストールする。

Google Reader 用の下準備と認証

gdata-python-client には、Reader サービスの固有の API は無いので、ベースのモノを適当に設定する必要がある。

import gdata.service

config = { 'Email': 'foobar@gmail.com', 'Passwd': 'password' }

# Google Reader サービス設定
service = gdata.service.GDataService(account_type='GOOGLE',
                                     service='reader',
                                     server='www.google.com',
                                     source='MyReaderHoge')

# Google アカウント設定とログイン
service.ClientLogin(config['Email'],config['Passwd'])

# 認証トークン(SIDの値)
print service.GetClientLoginToken()

# 書き込み用のトークンの取得
token = service.Get('/reader/api/0/token',converter=lambda x:x)
print "token:", token

'api/0'以下のレスポンスが AtomPub 形式では無く、小細工が必要である。gdata-python-clientには、ちょうど隙間が開いてるようである。。。

未読件数を取得

'api/0/unread-count'を JSON 形式で取得する

query = gdata.service.Query(feed='/reader/api/0/unread-count',
                            params={'all':'true', 'output':'json'})
feed = json.loads(service.Get(query.ToUri(), converter=lambda x:x))
unreadcounts = feed['unreadcounts']

# 全体の未読数を一番前に移動しとく
for i, item in enumerate(unreadcounts):
    if re.match(r'^user/\d+/state/com.google/reading-list$',item['id']):
        unreadcounts.pop(i)
        unreadcounts.insert(0,item)
        break

# 全体の未読数
print int(unreadcounts[0]['count'] if len(unreadcounts) > 0 else 0)
#=> 1

# 未読があるラベルとその未読数
for item in unreadcounts:
    if not re.match(r'^user/\d+/label',item['id']): continue
    print "count:%(count)s id: %(id)s" % item

#=> count:1 id: user/16814486509924941024/label/android

すべてのラベルを取得

query = gdata.service.Query(feed='/reader/api/0/tag/list',
                            params={'all':'true', 'output':'json'})
feed = json.loads(service.Get(query.ToUri(), converter=lambda x:x))
tags = feed['tags']

for item in tags:
    tag = re.sub(r'^user/\d+/',r'',item['id'])
    print "tag: %s" % tag
#=> tag: state/com.google/starred
#=> tag: state/com.google/broadcast
#=> tag: label/android
#=> tag: label/misc
#=> tag: label/neta
#=> tag: state/com.blogger/blogger-following

ラベルを指定して取得

android 関連の記事を 5 件見てみるかいな。。。

query = gdata.service.Query(feed='/reader/atom/user/-/label/android',
                            params={'n': str(5)})
feed = service.Get(query.ToUri())
for entry in feed.entry:
    print "===="
    dom = xml.dom.minidom.parseString(entry.ToString())
    print dom.toprettyxml(indent="  ")
    print "===="
    print "href: %s" % entry.GetHtmlLink().href
    print "title: %s" % entry.title.text
    # print "%s" % entry.summary.text

未読を既読に変更

先の記事は、読んだから既読にする。

# token, feed は、上に記載の書き込み用トークンと直前で取得した記事リスト
for entry in feed.entry:
    i = entry.id.text
    s = entry.source.extension_attributes['{http://www.google.com/schemas/reader/atom/}stream-id']
    ret = service.Post(urllib.urlencode({'i':i,
                                         'a':'user/-/state/com.google/read',
                                         's':s,
                                         'T':token,
                                         }),
                       '/reader/api/0/edit-tag',
                       converter=lambda x:x,
                       extra_headers={'Content-Type': 'application/x-www-form-urlencoded'})
    print i,ret

何でも無いが、この部分には半日悩んでしまった。

何気ない POST パラメータを送信する POST 処理なのだが、gdata.service.Postaplication/atom+xml形式のデータ送信を想定するため、明示的に Content-Typeを指定する必要がある!気づくまで、「400 Bad Request」+「X-Reader-Google-Bad-Token: true」に悩まされてしまった。

新しいフィードを登録

sub = r'feed/http://headlines.yahoo.co.jp/rss/rps_dom.xml'
ret = service.Post(urllib.urlencode({'s':sub,
                                     'ac': 'subscribe',
                                     'T':token,
                                     }),
                   '/reader/api/0/subscription/edit',
                   converter=lambda x:x,
                   extra_headers={'Content-Type': 'application/x-www-form-urlencoded'})
print sub,'subscribe',ret

購読しているフィードを解除

sub = r'feed/http://headlines.yahoo.co.jp/rss/rps_dom.xml'
ret = service.Post(urllib.urlencode({'s':sub,
                                     'ac': 'unsubscribe',
                                     'T':token,
                                     }),
                   '/reader/api/0/subscription/edit',
                   converter=lambda x:x,
                   extra_headers={'Content-Type': 'application/x-www-form-urlencoded'})
print sub,'unsubscribe',ret

参考


[全文を読む]