TwitterのStreamingAPIを使ってみた(ruby & OAuth認証)

3b19eb0c-s

恐ろしい事に5ヶ月ぶりのブログ更新となりました。。。
今回はtwitterのStreamingAPIを利用してみた際につまづいた点とかの話です。
twitterAPIで、、 って非常に今更感があるんですけど、自分が非力でして、多々つまづいたのでtrackしておきます。

ポイントはサーバ証明書取得、謎に1分でコネクションが切れる件、の2点でした。

2013/5/14 追記
userstreamAPIをお手軽に叩くgemのライブリがあるって知らないでエントリーしてました。
gemで簡単にいくぜ っていう方はこっちのエントリを参考されたし。。

まずはuserstreamAPIを試してみる

すでに皆さんご存知のとおり、twitterAPIの中でもStreamin系には Public stream, User stream, Site stream の3種類あるそうです。
Publicはその名のとおり、色んな人のストリームデータをじゃんじゃん流せるAPIですが、/statuses/filter.jsonというURLとfollowパラメータを使えばフォローしているユーザのタイムラインだけGETする事も可能のようです。

UserstreamAPIは特定のユーザネームだけのストリームデータを1コネクションで流し続けるAPIで、今回は手始めにこちらのAPIを叩いてみる事にしました。
このAPIは、指定したユーザのtweet以外にも、フォローしているユーザのtweetや他人からのフォローだとか色んなイベントを受け取る事ができます。
という事なので、フォローしていないユーザのtweetをリアルタイムで流したい場合は public にあるstatuses/filter.jsonでほしいユーザのIDを個別に投げる っていう使い分けになるのでしょうか。

参考になったのはこちらのサイトでした。
gemの必要なパッケージリストや、ソースコードがそのまんま公開されているので普通の人ならこれを参考に30分もかからずに実装できるのでは・・というくらい参考になりました。

ところがちゃんとつないでstreamデータをパンパン流すことができるようになるまでに5hくらいハマってしまったっていう。。
情けない。。

ちなみにuserstreamをたたくソースはテンプレ化してるのか、ほとんどのサイトで以下のソースコードが使われていました。

require 'rubygems'
require 'net/https'
require 'twitter'
require 'oauth'
require 'json'
require 'pp'

class MyBot
		CONSUMER_KEY      = "XXXXX"
		CONSUMER_SECRET   = "XXXXX"
		ACCESS_TOKEN      = "XXXXX"
		ACCESS_TOKEN_SECRET  = "XXXXX"

		MY_SCREEN_NAME = "altarf8"
		BOT_USER_AGENT = "@#{MY_SCREEN_NAME}"

		HTTPS_CA_FILE_PATH   = "./ca.pem"

		def initialize
				@consumer = OAuth::Consumer.new(
				CONSUMER_KEY,
				CONSUMER_SECRET,
				:site => 'http://twitter.com'
				)
				@access_token = OAuth::AccessToken.new(
				@consumer,
				ACCESS_TOKEN,
				ACCESS_TOKEN_SECRET
				)
		end

		def connect
				uri = URI.parse("https://userstream.twitter.com/2/user.json?track=hogehoge")
				https = Net::HTTP.new(uri.host, uri.port)
				https.use_ssl = true
				https.ca_file = HTTPS_CA_FILE_PATH
				https.verify_mode = OpenSSL::SSL::VERIFY_PEER
				https.verify_depth = 5

				https.start do |https|
						request = Net::HTTP::Get.new(uri.request_uri)
						request["User-Agent"] = BOT_USER_AGENT
						request.oauth!(https, @consumer, @access_token)
						buf = ""
						https.request(request) do |response|
						   response.read_body do |chunk|
						      buf << chunk
						         while(line = buf[/.+?(\r\n)+/m]) != nil
						            begin
						               buf.sub!(line, "")
						               line.strip!
						               if line == "" then next end
						               status = JSON.parse(line)
						            rescue => err
						               pp err
						            end
						         yield status
						      end
						   end
						end
				end
		end

		def run
				loop do
						puts "run"
						t0 = Time.now
						begin
						   connect do |json|
						      if json['text']
						         pp json['text']
						      end
						   end
						rescue Timeout::Error, StandardError
						   puts "Twitterとの接続が切れた為、再接続します"
						   t1 = Time.now
						   puts "コネクション継続時間 ⇒ #{t1 - t0} sec"
						end
				end
		end

end # class END

if $0 == __FILE__
		MyBot.new.run
end

サーバ証明書の取得方法(詳細)

twitterAPIを利用するにあたってはTwitterDevelopersに行って、アプリケーション登録をすませなければなりません。
まぁ上で紹介した秀逸な参考サイトさんの方で細かく書かれているのでここではつまづかないと思いますが、私はそのあとのサーバ証明書でハマりまくりました。。。

ずっと

SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
が消えない消えない。。

今回のhttpsを実行するにあたりNet::HTTPを使用したのですがca_fileとして証明書ファイルをサーバ側にも用意して指定しなくてはなりませんでした。
で、CAファイルの取得方法はこちらのブログで親切に案内されているのですが・・・

日本語がちゃんと読めない私は1時間ハマりました!\(^o^)/

まず、FireFoxを使ってDLはできるんですが、ちゃんとしたヤツを選ばないと全然動いてくれないっていう。
百聞は一見にしかずですので、以下キャプチャしました。 (ていうか自分が後でまた見たい用だったりする)

1.https://userstream.twitter.com/にアクセスする

すると、以下のBasic認証を要求されて最初「ハァ??」ってなります。

スクリーンショット_051213_041329_PM
ですが「キャンセル」を選択して、401 Error画面が出てきても大丈夫でした。 最終的にちゃんとした証明書GETできてました。ヨカタヨカタ。
スクリーンショット_051213_041522_PM ←こうなるから最初戸惑う。。。

2.ページ情報を開く

FireFoxだと現在最新は20.0.1ですが。。。 って、いつの間に20いったのよ。。。

alt でメヌー出しますね。 で、ツール > ページの情報 を選択。
「セキュリティ」「証明書を表示」で 証明書の詳細画面をひらきますね。

スクリーンショット_051213_041930_PM

3.証明書の選択とDL(※ここ重要)

証明書ビューアが開かれますが、この詳細タブをクリックしますね。
そんで、「証明書の階層」なるエリアで、 userstream.twitter.com をクリックする
っていう事が大変重要です。 クリックすると証明書のフィールドで、一番上の階層の名前が変わります。
なんか知らないけど、これじゃないとアカンようでした。

スクリーンショット_051213_042240_PM

そんで、最後に「エクスポート」で証明書の書き出し作業となります。

しかし、証明書の種類選択もあるようでして。。

スクリーンショット_051213_042334_PM

「証明書パスを含む x.509 証明書(PEM)」を選択してから「保存」とします。
最近仕事でvelisginさんの証明書作成やら自己認証局やら色々経験したんですが、 いまだにこの辺の仕組みが謎で仕方ありません。。
ちなみにファイル名は後でソースで指定するので何でもいいと思います。

ここで使うサーバ証明書の流用について(余談)

ちなみに、今回はuserstreamAPIを使ってみましたが
「publicのstreamAPI (stream.twitter.com/..)を使いたいときは別の証明書をGETするのか??」
と、疑問に思っていたのですが、大丈夫でした。

ちょうど今日は /statuses/filter.json のAPIを使ってみたのですが、全く同じCAファイルを指定して接続できました。

その後、うまく繋がったと思ったら1分くらいで接続が切れて再接続していた件

サーバ証明書の問題をクリアしてやっと接続できました。
そしてためしに自分のアカウントとかで適当にtweetしたところちゃんと一瞬で取り込めてガッツポーズ。

ところが、接続しっぱなしにしてたところコンソールに ”twitterとの接続が切れた為、再接続します” が、ずらーっと並んでる事が発覚。

え、? そんなに切れるの早いんだっけ?

と思い、コネクションの継続時間を調査(上記ソースの Time.now の差分をputsしてるところです)
すると、大体61~63秒で発生してる事が判明。

googole先生に聞いても特にHITせず。。 ぐぬぬ、と思って必死にデバッグする事に。。

結局、status = JSON.parse(line) の箇所でエラーをキャッチしてる事が判明しました。
#<JSON::ParserError: A JSON text must at least contain two octets!> ← これ
だいたい1分間隔くらいでJSONのparseエラーが吐かれてたので、 何をparseしてんのよ? とbufをppしてみると、\r\nが入ってる模様。

え。。? strip! してるんだけど。。

と思ったのですが、上記ソース(てか私ソース書換えたか?)では改行コードが単発で入ってきた場合、stripしたあと(そもそもそこまで行くのがおかしいんですが。。)空の文字列 ”” になってすぐにparseに突っ込まれてるようでした。

これの回避策ですが、結構ダサいんですがJSON.parseの直前かそこらで、

if  line == "" then next end

とかやったら回避できまして、コネクション張りっぱなしに成功しますた。 ダサいんですけどね。

まぁ、とりあえずこんな感じでユーザストリームを垂れ流す事に成功したので、これから何しようか妄想にふけってる最中でございます。。

しかし5ヶ月ぶりにブログ書いたら2時間くらいかかってしまった。。。
疲れたーーー

2013-05-12 | Posted in twitter API2 Comments » 


関連記事

コメント2件

 C | 2013.06.12 12:37

Excellent post. Thanks.

 altarfの管理人 | 2013.07.04 15:12

My pleasure 🙂

Comment





Comment



*