Twitter gem を使って検索する場合には注意しないと API 規制を受けてしまうことがあります。この記事では検索する場合の tips 、そして :since_id
指定していても API 規制を受けてしまうことを解決するモンキーパッチを公開します。
#API上限
通常の認証:180回/15分(平均:5秒に1回まで)
Application-only auth:450回/15分(平均:2秒に1回まで)
#アクセスクライアントインスタンスの作り方
##通常の認証
require 'twitter'
client = Twitter::REST::Client.new(
consumer_key: 'YOUR_CONSUMER_KEY',
consumer_secret: 'YOUR_CONSUMER_SECRET',
access_token: 'YOUR_ACCESS_TOKEN',
access_token_secret: 'YOUR_ACCESS_SECRET',
)
##Application-only auth
require 'twitter'
client = Twitter::REST::Client.new(
consumer_key: 'YOUR_CONSUMER_KEY',
consumer_secret: 'YOUR_CONSUMER_SECRET',
)
#検索:Twitter::REST::Search#search(query, options = {})
##options
パラメータ:
###標準
-
:geocode
: 地点。"緯度,経度,距離"
のフォーマットで指定。距離は mi または km 。
例:"35.681382,139.766084,20km"
(東京駅を中心とした半径20km以内。スペースを空けるとダメ) -
:lang
: 言語。 ISO 639-1コード で指定。 -
:locale
: 地域。有効なのは"ja"
のみ。 -
:result_type
: 内容指定。デフォルトは"mixed"
。-
"recent"
: 最近のツイート。 -
"popular"
: ポピュラーなツイート。 -
"mixed"
: popular と recent を混ぜたツイート。
-
-
:count
: 取得数。デフォルトは15。最大は100。 -
:until
: 指定日まで検索。"YYYY-MM-DD"
のフォーマットで指定。ただし API では1週間までしか取得できず、:until
に1週間以前を指定してもやはり取得出来ない。 -
:since_id
: 指定 ID 以降から取得(最も古いツイート ID)。0
またはnil
を指定すれば指定してない状況と同じ。 残念なことに、検索結果が100件以上ある場合には無効(検索結果が100件以上でも有効にするモンキーパッチを後記します)。 -
:max_id
: 指定 ID 以前を取得(最も新しいツイート ID)。
###query
内での設定だがパラメータ指定も可能なもの
-
:from
: 指定アカウントからのツイートを検索。アカウント名(screen_name)を指定する。 -
:to
: 指定アカウントへのツイート(ただしツイート冒頭にある場合のみ)を検索。アカウント名(screen_name)を指定する。 -
:exclude
:"retweets"
を指定すると、検索結果から公式 RT を排除する。 -
:filter
:"links"
を指定すると、リンクを含むツイートのみを検索する。
参考:
- Twitter 検索API メモ - 超自己満足プログラミング
- Method: Twitter::REST::Search#search — Documentation for twitter (5.14.0)
#検索例
query = "#ブラバンツなんたら" # 4/15の夜に *私だけ* が投稿したハッシュタグ。121ツイートあります。
since_id = nil
result_tweets = client.search(query, count: 100, result_type: "recent", exclude: "retweets", since_id: since_id)
#=> #<Twitter::SearchResults:0x007ffb10d17cd0
@attrs=
{:statuses=>
[{:metadata=>{:iso_language_code=>"ja", :result_type=>"recent"},
:created_at=>"Wed Apr 15 15:39:04 +0000 2015",
:id=>588365933179117568,
:id_str=>"588365933179117568",
:text=>"今気付いた、3位に汁だったんだw #ブラバンツなんたら",
(略)
#検索結果インスタンス result_tweets
の取り扱い
##検索結果が少ない((一週間以内の)検索可能範囲全体で100件未満の)場合(1回で全ての結果が取得できる場合)
result_tweets
そのまま、もしくは result_tweets.to_a
を対象にしてイテレータを使う。
検索結果が100件以上ある場合にこの方法を使うと API 規制を受ける場合があります。(詳細は後記)。
##検索結果の全てが1回で取得出来ていない場合(検索可能範囲全体で検索結果が100件以上)
result_tweets.take(100)
などで取得数を指定してイテレータを使う。take(1000)
などと :count
指定数以上の数を指定した場合にも、指定数量を上限としたツイート数を取得してくれる。 take
メソッドの代わりに、引数付き first
メソッドでも OK 。
##検索結果が多い場合に result_tweets.to_a
を使うと
取得出来る(1週間以内のツイート) 全て の検索結果を取得しようとします。そのため、検索結果が非常に多い(:count
を100に設定していた場合、通常で最大18000件以上、Application-only authで最大45000件以上)場合には、 API を使い切るまでアクセスし、RateLimited
エラーで終了してしまいます。しかも規制が掛からないようにと考えて :since_id
を指定していても、その :since_id
が検索2回目以降に含まれる範囲にある場合には :since_id
指定は有効になりません(要注意ポイント)。 これは Twitter gem のバグだと思います。
対策としては
-
take
メソッドを使って取得数を決めてしまう -
後記するモンキーパッチ を当てて
:since_id
指定を確実に有効にする
といった方法を用いましょう。
##検索結果の各ツイートインスタンス Twitter::Tweet
のメソッド例
各ツイートインスタンスを tw
とします。
- ID :
tw.id
- ツイート本文:
tw.full_text
- アカウント名:
tw.user.name
- スクリーンネーム:
tw.user.screen_name
- ツイート数 :
tw.user.statuses_count
- フォロー数 :
tw.user.friends_count
- フォロワー数:
tw.user.followers_count
- リツイート数:
tw.retweet_count
- お気に入り数:
tw.favorite_count
- 添付画像URL:
tw.media.first.expanded_url.to_s
- リンク先URL:
tw.urls.map { |t| t.expanded_url.to_s }
(リンク先を複数有する場合もあるので)
###take
メソッドで取得数を指定してからイテレータを使う例
result_tweets.take(100).each_with_index do |tw, i|
puts "#{i}: @#{tw.user.screen_name}: #{tw.full_text}"
end
#=> 0: @riocampos: 今気付いた、3位に汁だったんだw #ブラバンツなんたら
1: @riocampos: パテルスキーもトップ10に入ったのか #ブラバンツなんたら
:
98: @riocampos: ルームポットのホーニヒ #ブラバンツなんたら
99: @riocampos: あたっこ。ルームポット? #ブラバンツなんたら
100件取得されました。
###取得数を指定せず検索結果インスタンスに対して直接イテレータを使う例
result_tweets.each_with_index do |tw, i|
puts "#{i}: @#{tw.user.screen_name}: #{tw.full_text}"
end
#=> 0: @riocampos: 今気付いた、3位に汁だったんだw #ブラバンツなんたら
1: @riocampos: パテルスキーもトップ10に入ったのか #ブラバンツなんたら
:
119: @riocampos: sporzaで #ブラバンツなんたら きた\o/
120: @riocampos: #ブラバンツなんたら かなぁ
全件121件取得されました。
#:since_id
指定を有効にするモンキーパッチ
module Twitter
class SearchResults
def next_page
return nil unless next_page?
hash = query_string_to_hash(@attrs[:search_metadata][:next_results])
since_id = @attrs[:search_metadata][:since_id]
hash[:since_id] = since_id unless since_id.zero?
hash
end
end
end
このモンキーパッチを使うと、 API へのアクセスが2回以上にわたる場合にも :since_id
が有効になります( https://github.com/sferik/twitter/pull/682 でプルリクエスト提出済み)。
#参考
本件に関してより詳しい内容はこちらに書いています。要点だけを Qiita に引用しました。