読者です 読者をやめる 読者になる 読者になる

鴨川にあこがれる日々

軽い技術っぽい記事かいてます

TwitterAPIを使ってソーシャルグラフを描く(データ収集編)

Ruby Graph R JavaScript HTML

この記事はシステム主専攻ver1.0 Advent Calendar 2014の5日目の記事です.
現在は2015年1月ですが,細かいことは置いておきましょう.


システム主専攻ver1.0 Advent Calendar 2014 - Adventar

はじめに

2014年の記事を振り返ると手を出したまま放置したものが多々ありました.

  • julia:もうやってません
  • node.js:記事書いてからやってません
  • Javascript:フロントエンドはもう嫌だ
  • ソーシャルグラフ:集めるといったまま放置


Twitterでフォロー関係にあるアカウント一覧をCSVで取得 By Ruby - You have completed regicide!

風呂敷をたたむためにソーシャルグラフ(以下グラフ)を描きます.

環境

概要

Twitterのフォロー・フォロワー関係でグラフを描画します.
Twitterアカウントをノード,フォロー・フォロワー関係をエッジとすれば有向グラフにできます.
利用するアカウントは2015年1月21日の@nozawa0301のフォロー・フォローワー様の和集合の830アカウントです.

おおまかに分けると以下の手順になります.

  1. TwitterAPIで@nozawa0301のフォロワーとフォローのアカウントIDを取得(今回)
  2. 収集したデータでネットワークを描画(次回)

です.

用語説明

有向グラフですが,もちろん無向グラフもあります.
グラフはエッジ(辺)とノード(頂点)から構成されます.
無向グラフはエッジの向きを考慮しません
f:id:another16javac:20150124052333p:plain

「ワタシはベイマックス」
















ダダ滑りです.ごめんなさい
これは,2つのノードと1つのエッジからなる無向グラフです.

有向グラフはエッジの向きを考慮します.
f:id:another16javac:20150124052445p:plain
これは,ノード1からノード2へエッジがつながっています.
Twitterのフォロー・フォロワー関係に当てはめれば,
アカウント1はアカウント2をフォローしている.
アカウント2はアカウント1をフォローしていない,
という関係を表現します.

データ収集

RubyTwitterのGemを使い,グラフ描画に必要なデータを収集を行います.

主に使ったメソッドは2つです.

  1. friend_ids
  2. follower_ids

2つとも1回につき5000アカウントのIDしか取得できません.
また,それぞれ15分につき15回まで実行できます.

まずは,自分のフォローとフォロワーの和集合を取得します.

friend_idsメソッドとfollower_idsメソッドでアカウントIDを取得し,重複を取り除きます.
自分のアカウントでは,フォロワー・フォロワーともに5000に満たないため,1回で集まりました.
これで830のアカウントIDが集まりました.

次にフォロー・フォロワー関係の収集です.

先に考えたのがfriendship(id1,id2)メソッドでの関係抽出です.
任意の2つのアカウントIDを引数に与えてフォロー・フォロワー関係を取得できます.
上限は15分につき180回です.

もう一つは取得した830のアカウントID1つ1つに対してfriend_idsメソッドでの関係抽出です.
フォローしている情報だけを使って関係抽出ができます.
f:id:another16javac:20150124052445p:plain
1からみれば2をフォローしていますが,
2からみれば1からフォローされているので,
1のフォローしている情報だけ取得すれば2のフォロワーの情報は不要です.


APIの制限を考えてすべて集めるのにかかる時間を比較します.
friendshipを使った場合
830*829/2=344,035回メソッドを実行します.
15分で180回実行できるので,全部実行するには約478時間かかります.
つまり20日かかることになります.

後者を使った場合は
アカウント分の830回実行します.
15分で15回実行できるので,全部実行するには約14時間です.
半日と少しですね.

時間の短い後者を採用します.
ただし,わたしをフォローしているが,わたしがフォローしていない非公開アカウントについてはメソッドが実行できません.非公開アカウントはfriend_idsでは取得不可でした (2014/1/24)
そのため,friend_idsを実行する対象から除外しました.
また上限である5000以上のアカウントをフォローしているアカウントも除外しました*1

コードを書いたら適当にデータベースを準備,VPSにcronジョブを登録してデータを集めます.

以下コードです.

# -*- coding: utf-8 -*-
require 'active_record'
require 'sqlite3'
require 'twitter'
ActiveRecord::Base.configurations = YAML.load_file('database.yml')
ActiveRecord::Base.establish_connection(:development)

class User < ActiveRecord::Base
end

class Relation < ActiveRecord::Base
end

# アプリケーションキー 個人ごと
consumer_key = "lisp"
consumer_secret = "clojure"
access_token = "scheme"
access_token_secret = "sbcl"

# 認証
client = Twitter::REST::Client.new do |config|
  config.consumer_key = consumer_key
  config.consumer_secret = consumer_secret
  config.access_token = access_token
  config.access_token_secret = access_token_secret
end

users = User.where(:profile =>nil).take(15)

users.each do |data|
  sourceId = data.id
  begin
    # 上限が5000超えるとめんどいので今回は飛ばす
    if client.user(sourceId).friends_count > 5000
      user = User.where(:id => sourceId).first
      # profile属性を更新
      user.update_attribute(:profile, "OverData")
      next
    end
    client.friend_ids(sourceId).each do |target|
      next unless User.where(:id => target).first
      relation =  Relation.new
      relation.id = Relation.last.id + 1
      relation.source = sourceId
      relation.target = target
      relation.save!
    end

    user = User.where(:id => sourceId).first
    # profile属性を更新
    user.update_attribute(:profile, client.user(sourceId).description)
  rescue
    user = User.where(:id => sourceId).first
    user.update_attribute(:profile, "locked")
  end
end

DBはsqlite3です.
Userテーブルには,830のアカウントIDだけを予め登録しておきます.
friend_idsで取得データを使い,Relationテーブルにはフォロー(source),フォロワー(target)のIDを登録します.
Relationを取得したかどうかのフラグとして,Userテーブルのprofileがnilかどうかで判別しています.

あと5時間くらいで,データがすべて集まりそうなので,途中段階のデータを使ったグラフの描画で締めます.
f:id:another16javac:20150124063624p:plain

収集が終わり次第,いくつかの方法でグラフを描画します.

*1:Cursorを進めれば取得可能ですが,15*5000を超えるアカウントもあり,だいぶ面倒というのが本音です