Twitter APIを制限目一杯まで実行したい
結論
あるAPIを残り何回叩けるか、いつ制限がリセットされるか
などの情報を取得できるTwitter APIがあるので、そいつから取得した情報を元に制限一杯まで実行すれば良い。
このAPI:
背景
やりたい事
あるTwitterアカウントをフォローしているユーザー
がその他にどんなアカウントをフォローしているのか
を調べたい。
最初に試した方法
あるTwitterアカウントをフォローしているユーザー
を取得したのち、それぞれのユーザーに対してフォローしているアカウント
の一覧を取ろうとした。- ruby gemのにTwitter APIをいい感じにラップしてくれるやつがあったので、それを使った。
最初に試した方法の課題点
あるTwitterカウントのフォロワー数
分APIを叩く事になる。- twitter APIには非常に厳しい実行回数制限がある。
- 実行回数の制限に引っかかると、例外を吐いてプログラムが終了するので全然はかどらない。
参考:
Rate Limits: Chart | Twitter Developers
対策
あるAPIを残り何回叩けるか、いつ制限がリセットされるか
などの情報を取得できるTwitter APIがあるので、そいつから取得した情報を元に制限一杯まで実行する。
実装
require 'twitter' # API制限を表現するクラス class APILimitStatus def initialize(client, target_end_point, target_api) @client = client @target_end_point = target_end_point @target_api = target_api setup_or_update_limit_status! check_arguments! end # もし制限に達していたら制限がリセットされるまでスリープする。 def sleep_to_reset_if_limit! setup_or_update_limit_status! if remaining <= 0 sleep(need_seconds_for_reset) end end private # 次回制限リセットまでの残り秒数 def need_seconds_for_reset now = Time.now.to_i (reset_at - now) > 0 ? (reset_at - now) : 0 end # 残り実行回数リセット時刻 def reset_at @limit_status[@target_end_point][@target_api][:reset] end # 残り実行可能回数 def remaining @limit_status[@target_end_point][@target_api][:remaining] end # limit_statusをupdateする。なければsetupする。 def setup_or_update_limit_status! limit_status = @client.__send__(:perform_get, '/1.1/application/rate_limit_status.json') @limit_status = limit_status[:resources] end # 制限を調べたいAPIの指定が間違っていたら雑な例外を出す。 def check_arguments! raise if @limit_status[@target_end_point].nil? raise if @limit_status[@target_end_point][@target_api].nil? end end # 設定 client = Twitter::REST::Client.new do |config| config.consumer_key = "" config.consumer_secret = "" config.access_token = "" config.access_token_secret = "" end SURVEY_USER_ID = "hoge" # あるidをフォローしているユーザーの一覧 follower_api_limit = APILimitStatus.new(client, :followers, :"/followers/ids") follower_api_limit.sleep_to_reset_if_limit! follower_ids = client.follower_ids(user_id: SURVEY_USER_ID).attrs[:ids] # ランダムな順番に並び替える。 # これなら一定数のfollower分のfriendが取得できた時点でサンプリングされたデータとして扱える follower_ids.shuffle! # あるidをフォローしているユーザーがフォローしているユーザーの取得 friend_api_limit = APILimitStatus.new(client, :friends, :"/friends/ids") follower_ids.each do |follower_id| friend_api_limit.sleep_to_reset_if_limit! begin friend_ids = client.friend_ids(user_id: follower_id).attrs[:ids] puts "#{follower_id},#{friend_ids.to_s}" rescue Twitter::Error => e # 鍵アカとかも例外になるので。 puts "#{follower_id},#{e.to_s}" end end