はてブロ@ama_ch

https://twitter.com/ama_ch

python-twitterのGetFriends()で100人以上取得する

昨日、フレンドリストを100人以上取得するためにtwitter.pyを色々いじりました。その結果、

users = api.GetFriends(取得ページ番号)

こんな感じで呼び出せるようになりました。ページ番号で3を指定すると、ID*1順で201〜300番を取得します。
でもこれだといちいちページ番号を指定しないといけないので面倒です。
そこで今回はGetFriends()に引数を与えたりごちゃごちゃいじくって機能拡張*2してみました。


前回同様、twitter.pyの最後に以下を付け足します。

PAGELIMIT = 10  # number of friends can get once
class TwitterApi(Api):
  def GetFriends(self, pagebegin=1, pageto=PAGELIMIT, user=None):
    friends = []
    cntpage = pagebegin
    while True:
      print "Getting friends: %d - %d" % (cntpage*100-99, cntpage*100)
      if user:
        url = 'http://twitter.com/statuses/friends/%s.json?page=%d' % (user, cntpage)
      else:
        url = 'http://twitter.com/statuses/friends.json?page=%d' % cntpage
      json = self._FetchUrl(url)
      data = simplejson.loads(json)
      currentfriends = [User.NewFromJsonDict(x) for x in data]
      friends += currentfriends
      if len(currentfriends) % 100 != 0 or currentfriends == [] or cntpage == pageto:
        break
      cntpage += 1
      if cntpage == pagebegin+PAGELIMIT:
        print 'You can get friends only to 1000 for not to be API-limt.'
        print 'To get next 1000 friends, call: GetFriends(%d, %d, "%s")' % (pagebegin+PAGELIMIT, pagebegin+(PAGELIMIT*2-1), user)
        break
    return friends

ちくしょう綺麗に書けない・・・PAGELIMITなんてGetFriends()メソッド内でしか使わないのにクラスの外で宣言してるしw でもここに書かないと動かなかった。どうすればいいんだー


解説します。

GetFriends(取得開始ページ, 取得終了ページ, ユーザー名)

という形で使います。すべての引数は省略可能です。省略した場合、自分のフレンドリストを頑張って取得します。
1時間に70回までしかリクエストできない点,何千人単位でfollowingを持つユーザーもいる点*3を考慮して、この拡張版でも1度に取得できるのは「1000人まで(10ページ)」にしてあります。増やしたい・減らしたい場合はソース最初のPAGELIMITという変数から変更できます。
取得ユーザーのfollowingが1000人以内なら何事もなく終了します。
取得ページ範囲が10ページを超える場合、1000件まで取得した後以下のようなメッセージを吐きます。

You can get friends only to 1000 for not to be API-limt.
To get next 1000 friends, call: GetFriends(pagebegin, pageto, user-id)

最後のGetFriends()の引数には、実行時に与えていた引数に応じて次の1000件を取得するための値が入ります。


使用例を何個か書いておきます。
・following1000人以内の場合

>>> import twitter
>>> api = twitter.TwitterApi("ユーザー名", "パスワード")
>>> users = api.GetFriends()  #取得範囲の指定なし
Getting friends: 1 - 100
Getting friends: 101 - 200
>>> len(users)  #取得ユーザー数を確認
129

followingが1000人以内だと、引数の指定などは特に必要ありません。Twitterがちゃんと応答してくれれば無事に全員取得できます。


・followingが1000人を超える場合1

>>> # following1001人以上のユーザーを指定
>>> # 取得終了ページを0にすると引数指定なしと同じ動作をする(全員取得しようとする)
>>> users = api.GetFriends(1, 0, "ユーザー名")
Getting friends: 1 - 100
Getting friends: 101 - 200
Getting friends: 201 - 300
Getting friends: 301 - 400
Getting friends: 401 - 500
Getting friends: 501 - 600
Getting friends: 601 - 700
Getting friends: 701 - 800
Getting friends: 801 - 900
Getting friends: 901 - 1000
You can get friends only to 1000 for not to be API-limt.
To get next 1000 friends, call: GetFriends(11, 20, "ユーザー名")
>>> len(users)
1000

1000人まで取得後、警告と次の1000人を取得するための呼び出し方が表示されます。


・followingが1000人を超える場合2

>>> # 取得範囲を10ページきっかり指定する
>>> users = api.GetFriends(63, 72, "ユーザー名")
Getting friends: 6201 - 6300
Getting friends: 6301 - 6400
Getting friends: 6401 - 6500
Getting friends: 6501 - 6600
Getting friends: 6601 - 6700
Getting friends: 6701 - 6800
Getting friends: 6801 - 6900
Getting friends: 6901 - 7000
Getting friends: 7001 - 7100
Getting friends: 7101 - 7200
>>> len(users)
1000

こちらも1000人取得するけど、10ページぴったりなので警告は出ません。


・7000人まで1度に取得する方法*4
PAGELIMITを書き換えても良いんだけど、いちいちモジュール書き換えるのは面倒&気分悪いので。
followingが7000人以内なら、この方法で一括取得できます。大体API制限に引っかかるか、503とか502が返ってきて失敗すると思うけどw

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import twitter

api = twitter.TwitterApi("ユーザー名", "パスワード")
friends = [];  page = 1;
while True:
    users = api.GetFriends(page, page+9, "フォロー多いユーザー")
    friends += users
    if len(users) % 100 != 0 or users == [] or page == 70:
        break
    page += 10
print "%d friends" % len(friends)

すっごい時間かかるよ!特に5000人とか超えてくると取得中なの忘れて読書とかできちゃう感じ。
実行結果

$ python p-twitter.py
Getting friends: 1 - 100
Getting friends: 101 - 200
Getting friends: 201 - 300
--- 中略 ---
Getting friends: 2101 - 2200
Getting friends: 2201 - 2300
Getting friends: 2301 - 2400
2306 friends

いっぱい取得できました。


・その他tips?
結構(かなり)ページ取得に時間がかかるので、フリーズしている訳ではないことを表すために、デフォルトでは「Getting〜」と処理の過程をprintしています。「フレンドリストを取得して〜して〜する」という風に他にも色々処理をする場合、「Getting〜」の表示が邪魔になると思うので、その時はprintの行をコメントアウトしてください。↓
#print "Getting friends: %d - %d" % (cntpage*100-99, cntpage*100)


このエントリー書くためにAPI制限ひっかかりまくって、結局1日以上かかった><
TwitDir - a Twitter directory - Top 100 followersによると1番following多い人63458人だってwwwすげぇな!
followingを取得するために色々やってみたけど、「following取得→ファイルに保存するプログラム」を作って、何かしたい時はそのファイルから読み込むのが1番実用的な気がします。取得に結構時間かかる上にTwitterが不安定だからよく失敗しちゃう。

*1:これはTwitter上で見れない。アカウント取得順になる模様

*2:になってる自信はあまりない

*3:1度のリクエストで取得できるのは100件までだからね!

*4:API制限ギリギリまで取得しちゃうのです