Twitterのデータベース

Twitterでせっかくapi叩いたのにデータ一部だけとって残り捨てちゃうの勿体無くね?と思い、Twitterのデータベースをつくろうと思いたつ。
結論から言うと、yaml遅すぎて、読み込むのにめっちゃ時間かかる&大量のデータつめこんだらjavaがもう限界ってエラー吐き始める(twitterのツイートのstatusデータはいちいちuser情報とか入ってて階層深いし、ツイート全部合わせると地味に大きい)からあんま実用的じゃなかった。Twitter APIいちいち叩いてるほうが時間の節約になるという話。
あと発見として、Twitter APIにscreen_nameとかID投げるよりuserオブジェクト投げたほうがdownのエラー吐かれにくいっぽい。このへんもっと検証しないとだけど、なぜだかは不明。

0.YAMLを導入する。
CSVでちまちまと出力していたので、yamlに切り替える。これは結構革新的だった。ほぼrubyオブジェクトのまま保存できる!

db = YAML::Store.new("<filename>")
db.transaction do
db["<key>"] = <RubyObject>
end


1.エゴネットワークのメンバー、ネットワーク取得関数にTwitter DB作りを手伝わせる。

エゴネットワークのメンバー関数に埋め込む。

### fetching ego network members
#[0]=members [1]=newmembers [2]=user_timeline_db
def ego_network_members(account,hop,num_of_tweets,user_timeline_db)
account = Twitter.users(account)[0].id # converting from screen_name to numerical id
members = Array.new()
new_members = Array.new()
members[0]=account
# hopが1の時は
if hop==1
## user_timeline_db のセットアップ##
## プログラム本体##
# accountの200tweetsをとる

if user_timeline_db.class == Hash# user_timeline_dbの次元が十分でないエラーを検出
tweets=user_timeline_db[account]
else
user_timeline_db=Hash.new()
tweets=nil
end
# 以下accountのtweetsがDB上になかった時の処理
if tweets == nil
error_counter = 1
begin
tweets = Twitter.user_timeline(account,{:count => 200})
rescue
# ルーティーンの例外処理
STDERR.puts "Warning: #$!"
case $!
when Twitter::Error::Unauthorized
tweets == nil
puts "secret account : #{member}"
when Twitter::Error::BadRequest
sleep 30
retry
else
if error_counter <8
error_counter += 1
retry
else
error_counter == 8
tweets == nil
puts "fail : #{member}"
end
end
end
end

## tweetsが取得出来たので、返信相手の取得&DBの更新
unless tweets==nil
# 取れたtweetsに対して処理
for i in 0..num_of_tweets-1
members << tweets[i].in_reply_to_user_id
end
# user_timeline_dbを更新する
p tweets #test code
user_timeline_db[account] = tweets
puts "#{account} is success"
end

## members と new_members の整理
members.delete(nil) # in_reply_to_user_idでnilが返されるので除去
members.uniq! # ダブりを消す
new_members=members
new_members.delete(account) # account は唯一新しく取れたアカウントでないので消す
user_timeline_db_revised = user_timeline_db

else
# hopがひとつ小さいとこまで終わっている状態をとってくる
state = ego_network_members(account,hop-1,num_of_tweets,user_timeline_db)
old_members = state[0]
members = old_members
old_user_timeline_db = state[2]
step_old_members = state[1]
step_old_members.each do |member| # 前のステップで初出のアカウントだけを対象にhop=1でまわす
hogehoge=ego_network_members(member,1,num_of_tweets,old_user_timeline_db)
new_members = new_members + hogehoge[0]
if hogehoge[2]==Hash
hogehoge[2].each_key do |key|
old_user_timeline_db[key]=hogehoge[2][key]
end
end
end
members = members + new_members
members.uniq!
new_members = members - old_members
new_members.uniq!
user_timeline_db_revised = old_user_timeline_db
end
[members,new_members,user_timeline_db_revised]
end

### creating twitter social graph from a  set of members
## creating_twitter_graph[0]=graph creating_twitter_graph[1]=user_timeline_db
def creating_twitter_graph(members)
# return network
network = DirectedHashGraph.new()
count_member = 0
user_timeline_db = Hash.new()
# 対象メンバー
members.each do |member|
count = 0
# エラーをカウントする
error_counter = 1
## tweetsをとってくる
begin
tweets = Twitter.user_timeline(member,{:count => 200})
rescue
STDERR.puts "Warning: #$!" # error内容を吐く
case $!
when Twitter::Error::Unauthorized
tweets == nil
puts "secret account : #{member}"
when Twitter::Error::BadRequest
sleep 30
retry
else
if error_counter <5
error_counter += 1
retry
else
tweets == nil
puts "fail : #{member}"
end
end
else
puts "success : #{member}"
end
### 取ってきたツイートに対して処理をする
unless tweets ==nil
tweets.each do |tweet|
replying_person = tweet.in_reply_to_user_id # reply相手をとる
unless network[member,replying_person] == nil # edge already exist?
network[member,replying_person]=network[member,replying_person]+1 # edgeの重みを加える
else
network[member,replying_person]=1 # edgeをつくる
end
end
else
puts "#{member} is secret account"
end

count_member = count_member+1
puts "count member: #{count_member}"
user_timeline_db[member]=tweets
end
[network,user_timeline_db]
end

### ryouomoi

調べてみたらバグ多すぎわろた。idからscreen_nameに変換するやつもバグがあった。Twitter.users(*args) はargsの順番通りにuserオブジェクトの配列を返さないらしい。どうりでノードのラベリングが滅茶苦茶になるわけだ。。。

拡大できるほう

送信者 christopher226_egonetwork