RubyでTwitterのタイムラインを取得してみる

[Ruby][Twitter][API]RubyでTwitterのタイムラインを取得してみる

前回までのあらすじ

  1. http://d.hatena.ne.jp/kk_Ataka/20101121/1290271210:title
  2. http://d.hatena.ne.jp/kk_Ataka/20101130/1291125511:title

前回までで、なんとかOAuth認証を突破する事ができました。目標は達成できたっちゃあできたんですが……せっかくなので、取得したアクセストークンを使ってTwitterからタイムラインを取得してみます!

タイムラインの取得方法

  1. signatureを作成する
  2. 以下のパラメータをアルファベット順にxxx=yyy&vvv=zzz……の形で連結した値を[http://api.twitter.com/1/statuses/home_timeline.json]のおしりにくっつけてGET or POST
    1. oauth_consumer_key
    2. oauth_nonce
    3. oauth_signature
    4. oauth_signature_method
    5. oauth_timestamp
    6. oauth_token
    7. oauth_version

取得はこれで完了。

signatureの作成方法は今までと同じで、

  1. 認証用の値を生成する(以下の3つの値を&で連結する)
    1. “GET”
    2. http://api.twitter.com/1/statuses/home_timeline.json“をエスケープしたもの
    3. oauth_signature以外のパラメータ[1]をアルファベット順に並べてxxx=yyy&vvv=zzz……の形で連結した値をエスケープしたもの
  2. 署名キーを生成する
    1. “consumer_secret&oauth_token_secret”
  3. キーを元に値をHMAC-SHA1方式で暗号化した値をbase64形式でエンコードする

です。OAuth認証して獲得したアクセストークン、oauth_tokenとoauth_token_secretを随所で使います。そして作成したsignatureと他のパラメータを[http://api.twitter.com/1/statuses/home_timeline.json]のおしりにくっつけて(今回は)GETリクエスト

戻ってきたタイムライン

タイムラインらしきものはjsonという形式のデータで戻って来ます。home_timeline.jsonのjson部分をxmlとかrssに変えるとxml形式のタイムライン、rss形式のタイムラインが返ってくるようですが今回はこのjsonを何とかしてみます。

jsonの詳細は後にググるとして、一人当たりの情報はこんな感じ。[2]

.......},{
    \"coordinates\":null,
    \"created_at\":\"Tue Dec 07 13:50:49 +0000 2010\",
.
(略)
.
    \"in_reply_to_screen_name\":null,
    \"in_reply_to_status_id_str\":null,
    \"id_str\":\"99999999999999999\",
    \"contributors\":null,
    \"retweet_count\":null,
    \"in_reply_to_user_id\":null,
    \"in_reply_to_user_id_str\":null,
    \"user\":{
.
(略)
.
        \"screen_name\":\"kk_Ataka\",
        \"profile_sidebar_border_color\":\"C0DEED\",
        \"follow_request_sent\":false,
        \"location\":\"Kawasaki, Kanagawa, Japan\",
.
(略)
.
        \"profile_link_color\":\"0084B4\"},
    \"geo\":null,
    \"retweeted\":false,
    \"id\":99999999999999999,
    \"text\":\"test\"
},{......

初見だとこれ×20件のデータが改行なしでずらりと画面に並ぶので、面食らったのですが、よく見てみると見覚えがあるデータもちらほら……。

例えば、userの中にあるscreen_nameはkk_Ataka……これ俺や! あとは、text:testも、testってツイートした後だったので、ツイート内容なのかなと。

リプライしたときはin_reply_to系のキーにリプライ相手のidやscreen_nameが入っていると思います。

んで、このデータをどうやって解析しようか……と思ってググってみると、jsonをparseしてハッシュにしてくれるプログラムが。@webos_goodiesさんの[http://webos-goodies.jp/archives/51071565.html:title]このプログラムをありがたく使わせていただきました

ソースはこんな感じ。上記のparserは下記のrbファイルと同じディレクトリ、もしくはパスの通っている場所にsimplejsonparser.rbとして配備しました。

require 'openssl'
require 'uri'
require 'net/http'
# http://webos-goodies.jp/archives/51071565.html
require 'simplejsonparser'
require 'nkf'
require 'time'

# signature作成
def signature(method, consumer_secret, oauth_token_secret, url, oauth_header)
    # signature_keyの作成
    # リクエストトークン時は"CONSUMER_SECRET&"(アンドが入っている)
    # アクセストークン時は"CONSUMER_SECRET&OAUTH_TOKEN_SECRET"として使用
    signature_key = consumer_secret + "&"
    if !oauth_token_secret.nil? then
        signature_key += oauth_token_secret
    end

    # oauth_headerのパラメータをソートして連結
    param = sort_and_concat(oauth_header)

    # httpメソッドとURLとパラメータを&で連結する
    value = method + "&" + escape(url) + "&" + escape(param)
    # hmac_sha1
    sha1 = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, signature_key, value)
    # base64
    base64 = [sha1].pack('m').gsub(/\n/, '')
    return base64
end

# 文字列のエスケープ(: / = %をエスケープする。. _ -はそのまま)
def escape(value)
    URI.escape(value, Regexp.new("[^a-zA-Z0-9._-]"))
end

# oauth_headerの情報をアルファベット順に並べ替え & で結合
def sort_and_concat(oauth_header)
    oauth_header_array = oauth_header.sort
    param = ""
    oauth_header_array.each do |params|
        for i in 1..params.length
            param += params[i-1]
            if i % params.length  == 0
                param += "&"
            else
                param += "="
            end
        end
    end
    param = param.slice(0, param.length-1)
end

# 自分のタイムライン取得用のURL
home_timeline_url = "http://api.twitter.com/1/statuses/home_timeline.json"

# Twitterで登録したらもらえる
consumer_key = "XXXXXXXXXXXXXXXXXXX"
consumer_secret = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"

# Twitterからもらえるアクセストークン
oauth_token = "11111111-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
oauth_token_secret = "VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV"

# oauthパラメータたち
oauth_header = {
    # Consumer Key
    "oauth_consumer_key" => consumer_key,
    # 一意な値(今回は適当に実装)
    "oauth_nonce" => "AAAAAAAA",
    # 署名方式(HMAC-SHA1)
    "oauth_signature_method" => "HMAC-SHA1",
    # リクエスト生成時のタイムスタンプ(ミリ秒)
    "oauth_timestamp" => Time.now.to_i.to_s,
    # バージョン(1.0)
    "oauth_version" => "1.0",

    # アクセストークン
    "oauth_token" => oauth_token 
}

# signature作成
oauth_header["oauth_signature"] = signature("GET", 
                    consumer_secret, 
                    oauth_token_secret, 
                    home_timeline_url, 
                    oauth_header)

# GETする
uri = URI.parse(home_timeline_url)
proxy_class = Net::HTTP::Proxy(ARGV[0], 8080)
http = proxy_class.new(uri.host)
http.start do |http|
    # oauth_headerのパラメータをソートして連結
    param = sort_and_concat(oauth_header)
    
    res = http.get(uri.path + "?#{param}")
    if res.code == "200" then
        json = res.body
        # simplejsonparserでparseしてもらう
        jsonparse = JsonParser.new.parse(json)

        # 逆順にして古い物から取得する
        jsonparse = jsonparse.reverse

        for i in 0..jsonparse.length-1

            # 日付フォーマットを年/月/日 時間に変更
            time = Time.parse(jsonparse[i]["created_at"]).strftime("%Y/%m/%d %X")

            # SJIS変換(コマンドプロンプトで見る用)
            # print NKF.nkf('-s', "■#{jsonparse[i]["user"]["screen_name"]} (#{jsonparse[i]["user"]["name"]}) #{time}\n #{jsonparse[i]["text"]}\n")
            print "■#{jsonparse[i]["user"]["screen_name"]} (#{jsonparse[i]["user"]["name"]}) #{time}\n #{jsonparse[i]["text"]}\n"
        end
    else
        print "#{res.code}\n"
    end
end

結果はこう。タイムラインゲットー!

[f:id:kk_Ataka:20101207233454j:image]

Windows用……

ソースの最後、parseも終わってその中からほしいキーの値を取り出してるところで。

            # SJIS変換(コマンドプロンプトで見る用)
            # print NKF.nkf('-s', "■#{jsonparse[i]["user"]["screen_name"]} (#{jsonparse[i]["user"]["name"]}) #{time}\n #{jsonparse[i]["text"]}\n")

SJIS変換してるところがあるんですが、これ、もともとWindowsのコマンドプロンプトで動かしたくて作っていたものなんです。でもコマンドプロンプトって文字コードがSJISのようでそのまま表示すると文字化け起こすのでこういう対応を。

一応軽くググった感じだと、コマンドプロンプトでもUnicode文字を読めるようなのですが……。

[1] oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_token, oauth_versionの6個っすねー。oauth_tokenを忘れずに。

[2] 本来は一行で取得します

関連記事(この記事の初版より古い記事はリンクがグレーで表示されます)

  1. 2010/11/30 [Ruby] [Twitter] [API] RubyでTwitterのOAuth認証をしてみる その2
  2. 2010/11/21 [Ruby] [Twitter] [API] RubyでTwitterのOAuth認証をしてみる
  3. 2011/01/23 [Ruby] [Twitter] [API] RubyでTwitterにツイートをキメてみる
  4. 2012/05/21 [SlideShare] [Heroku] [Ruby] [API] SlideShareのAPIを叩いてスライドをDLするRubyスクリプトをHerokuにデプロイした
  5. 2011/12/27 [Evernote] [Ruby] [API] EvernoteのAPIをRubyから叩きたい
  6. 2011/03/08 [Ruby] [API] RubyではてなのWSSE認証をしてはてブにブクマをポストする
  7. 2011/03/02 [Ruby] [API] Read it LaterをRubyで取得する
  8. 2017/03/30 [Ruby] rbenvを最新にして新しいバージョンのRubyをインストールできるようにする
  9. 2016/08/31 [Ruby] Rubyの変数DATABASE_URLでハマった話
  10. 2016/06/05 [Ruby] Rubyで自然言語っぽくcrontab管理できるwheneverを使う
  11. 2016/02/17 [Ruby] [Mac] El Capitan上に古いpumaをインストールすると失敗する
  12. 2015/02/28 [Ruby] rubyXLを使ってExcelを操作したい
  13. 2014/08/28 [Sphinx] [Ruby] [イベント] kawasaki.rb #015 でSphinx導入事例の発表をしてきました #kwskrb #sphinxjp
  14. 2014/07/13 [Sphinx] [Ruby] [イベント] kawasaki.rb #013 でSphinx導入事例の発表をしてきました と #011 #012 参加記録 #kwskrb #sphinxjp
  15. 2014/04/01 [Ruby] [Jekyll] [イベント] kawasaki.rb #010 で発表してきました #kwskrb