概要
Twitter検索機能のクソ仕様について
Twitterでは、ブラウザで表示した際の検索ボックスやTwitter APIから、ツイートを検索することができます。
しかし、このツイート機能には大きな問題があることが前から指摘されていました。
つまり、検索ワードが『名前』や『スクリーンネーム』にまで引っかかるのです。
これにより、検索しても欲しい結果と違うものが多くヒットする……といった悲劇が頻発することになります。
↓「みぽりん」で検索した結果の例。予想に反して、ガルパンとは縁のない検索結果になってしまうことが分かる
あの有名人も被害に!
この生地を書こうと思った切っ掛けは、この前岸田メル先生がTwitterで嘆いていたことから来ています。検索キーワードを「岸田メル -from:mellco」としても、名前に『岸田メル』を含むアカウントのツイートが引っかかってしまうとのこと。まったく嘆かわしい話です……。
解決策
そこで、とりあえずRubyで検索用プログラムを作ってみました。
require 'twitter'
# 検索キーワード
keyword = 'みぽりん'
# 自分のアカウントを弾きたい場合は''以外にする
exclude_account = ''
# 検索する範囲
search_tweets = 100
# Application-only auth
# consumer_keyとconsumer_secretは、自分でアプリを登録してからコピペすること
# 参考サイトの例:http://hello-apis.blogspot.jp/2013/03/twitterapi.html
client = Twitter::REST::Client.new(
consumer_key: 'YOUR_CONSUMER_KEY',
consumer_secret: 'YOUR_CONSUMER_SECRET',
)
# 検索キーワードを作成
if exclude_account != ''
query = "#{keyword} -from:#{exclude_account} -to:#{exclude_account}"
else
query = "#{keyword}"
end
# 検索結果を取得する
result_tweets = client.search(query, lang: 'ja', count: search_tweets, result_type: "recent", exclude: "retweets", since_id: nil)
# 検索結果を表示する
result_tweets.take(search_tweets).each do |tw|
# 名前やスクリーンネームが検索キーワードを含む場合は除外する
next if tw.user.name.downcase.include?(keyword.downcase)
next if tw.user.screen_name.downcase.include?(keyword.downcase)
# リプライが検索キーワードを含む場合は除外する
# (「@スクリーンネーム」といった形で本文中に使われることがあるため)
match_flg = false
tw.full_text.scan(/@[0-9a-zA-Z_]{1,15}/).each{|word|
if word.downcase.include?(keyword.downcase)
match_flg = true
break
end
}
next if match_flg
# ツイートを表示する
puts "#{tw.user.name}@#{tw.user.screen_name} #{tw.created_at}"
puts "https://twitter.com/#{tw.user.screen_name}/status/#{tw.id}"
puts tw.full_text
puts ''
end
これを使って、同じように「みぽりん」というワードで検索した例がこちらになります。
先ほどと違い、意図しないツイートがほどよく省かれているのが分かりますね。
注意点
Twitter関係のアプリケーションを開発したことがある人にとっては常識ですが一応説明しておきます。
ソースコード中に「YOUR_CONSUMER_KEY」と「YOUR_CONSUMER_SECRET」とある部分は、各々の環境毎に適宜置き換えてください。
これらは認証処理のために必須のフレーズで、アプリケーションをTwitter社に登録すれば受け取れます。
認証すると、APIの仕様上、様々な使いかたができることからダミー文字列としました。
追記
冷静に考えますと、どうせTwitter APIで『本文だけ』取り出せますので、そこだけピンポイントで検索しなおせば済む話でした。
というわけで書き換えたコードは次の通りです。
require 'twitter'
# 検索キーワード
keyword = 'YSR'
# 自分のアカウントを弾きたい場合は''以外にする
exclude_account = 'ysrken'
# 検索する範囲
search_tweets = 1000
# Application-only auth
# consumer_keyとconsumer_secretは、自分でアプリを登録してからコピペすること
# 参考サイトの例:http://hello-apis.blogspot.jp/2013/03/twitterapi.html
client = Twitter::REST::Client.new(
consumer_key: 'YOUR_CONSUMER_KEY',
consumer_secret: 'YOUR_CONSUMER_SECRET',
)
# 検索キーワードを作成
if exclude_account != ''
query = "#{keyword} -from:#{exclude_account} -to:#{exclude_account}"
else
query = "#{keyword}"
end
# 検索結果を取得する
result_tweets = client.search(query, lang: 'ja', count: search_tweets, result_type: "recent", exclude: "retweets", since_id: nil)
# 検索結果を表示する
result_tweets.take(search_tweets).each do |tw|
# 本文からリプライ部分を除去する
full_text = tw.full_text.gsub(/@[0-9a-zA-Z_]{1,15}/, '')
# 本文に検索キーワードを含まない場合は除外する
next unless full_text.downcase.include?(keyword.downcase)
# ツイートを表示する
puts "#{tw.user.name}@#{tw.user.screen_name} #{tw.created_at}"
puts "https://twitter.com/#{tw.user.screen_name}/status/#{tw.id}"
puts tw.full_text
puts ''
end