Twitter Streaming APIãRubyã§è©¦ãã¦ã¿ã
Streaming APIã¨ã¯
Twitterã®Streaming APIããåç¥ã§ããããã
2009å¹´4æé ãã試é¨çã«å ¬éããã¦ããAPIãªã®ã§ããåç¥ãªæ¹ãå¤ãã¨æãã¾ãã2009å¹´8æç¾å¨ã§ã¾ã αãã¹ãä¸ã§ããããããå©ç¨ããã¨ãpushåã§ãªã¢ã«ã¿ã¤ã ã«æ å ±ãåãåããã¨ãã§ãã¾ãã
Twitterã®APIã¯åºæ¬çã«pullåãªã®ã§ãã¯ã©ã¤ã¢ã³ããè½åçã«ãªã¯ã¨ã¹ããçºè¡ããªããã°æ å ±ãåãåããã¨ãã§ãã¾ããããããã1æéãããã®ãªã¯ã¨ã¹ãåæ°ã1ãªã¯ã¨ã¹ããããã®æ大ãã¼ã¿æ°ãªã©ãAPIã«ã¯æ§ã ãªå¶éãããããã¦ãã¾ãã®ã§ãä¾ãã°ãªã¢ã«ã¿ã¤ã æ§ã®å¿ è¦ãªããã°ã©ã ãã大éã®tweetsãå¿ è¦ãªçµ±è¨ããã°ã©ã ãªã©ã®ç¨éã§é常ã®APIãå©ç¨ããã®ã¯å³ãããã®ãããã¾ãã
ãã®ãããªæã¯Streaming APIã使ãã¨åé¡ã解決ããããããã¾ããã
éåæ°ã®APIã¯å©ç¨ã«è¨±å¯ãå¿ è¦ã§ãããã¾ãé常ã®APIã¨ã¯æ©è½ã®ãã¯ãã«ãç°ãªãããããèªåã®ã¿ã¤ã ã©ã¤ã³ãåå¾ãã¦è¡¨ç¤ºãããã¨ãããããªæ®éã®Twitterã¯ã©ã¤ã¢ã³ãã©ã¤ã¯ãªç¨éã«ã¯ä½¿ãã¾ããããã¢ã¤ãã¢ããããã°è²ã ã¨é¢ç½ããã¨ãã§ããã®ã§ã¯ãªããã¨æãã¾ãã
Streaming APIã®æ¦è¦
é常ã®APIã¯ã
- ã¯ã©ã¤ã¢ã³ãããTwitterãµã¼ãã¸HTTPæ¥ç¶ãéãã
- ã¯ã©ã¤ã¢ã³ãããªã¯ã¨ã¹ããéåºãã
- ãµã¼ããã¬ã¹ãã³ã¹ãéãè¿ãã
- æ¥ç¶ãéããã
ã¨ããæµãã«ãªãã¾ãã
Streaming APIã®å ´åãã¯ã©ã¤ã¢ã³ããTwitterãµã¼ãã¸æ¥ç¶ãéãããªã¯ã¨ã¹ããéãã¨ããã¾ã§ã¯åããªã®ã§ããããã®å¾ã¯ã¨ã©ã¼çã§åæãããã¾ã§ãã£ã¨æ¥ç¶ãç¶æãç¶ãã¾ãããã®éããµã¼ãããã¯æ¬¡ã ã«ã¬ã¹ãã³ã¹ãéããã¦ãã¾ãã®ã§ãã¯ã©ã¤ã¢ã³ãã¯é 次ãããåãåã£ã¦å¦çãã¦ãããã¨ã«ãªãã¾ãã
Streaming APIã使ãä¸ã§ã®æ³¨æç¹
詳ããã¯APIのドキュメントã«æ¸ãã¦ããã¾ãããéè¦ãªãã®ã®ã¿ã¾ã¨ããã¨ã
- åä¸ã¢ã«ã¦ã³ãããã®åææ¥ç¶æ°ã¯1æ¬ã®ã¿ãåä¸ã¢ã«ã¦ã³ããã2æ¬ä»¥ä¸ã®æ¥ç¶ããã£ãå ´åãå¤ãæ¥ç¶ã¯å¼·å¶çã«åæãããã*1
- 使ç¨ããHTTPã©ã¤ãã©ãª*2ã¯ãã¬ã¹ãã³ã¹ããã£ãå ¨ã¦èªã¿è¾¼ãã§ããè¿ãã¿ã¤ãã§ã¯ãªããé 次èªã¿è¾¼ãã§ãããã¿ã¤ãã®ãã®ã§ãªãã¦ã¯ãªããªãã
- ãµã¼ãã¯æ¥ç¶ã®ç¶æã®ããã«ç©ºè¡ãéãè¿ããã¨ãããã®ã§ãã¯ã©ã¤ã¢ã³ãã¯ããã許容ã§ãã*3ä½ãã§ãªãã¦ã¯ãªããªãã
- é常ã®statusã®ä»ã«ããã¦ã¼ã¶ãstatusãåé¤ãããã¨ããéç¥ãªã©ãä»ã®æ å ±ãæ··ãã£ã¦éããã¦ããã®ã§ãé©åã«å¦çãããã¨ã
- ã¨ã©ã¼ãéä¿¡ã®é 延ãªã©ãæ§ã ãªè¦å ã«ãã£ã¦æ¥ç¶ãåæããããã¨ãããã®ã§ãå¿ è¦ãªãã°èªåã§åæ¥ç¶ããä»çµã¿ãçµã¿è¾¼ããã¨ã
- ä¸é¨ã®APIã¯è¨å¤§ãªéã®ãã¼ã¿ãé£ç¶ãã¦è¿ãã®ã§ããããåããããã°ã©ã ã¯ããã©ã¼ãã³ã¹ã«é¢ãã¦ç¸å½ãªåªåãå¿ è¦ãããããªãã
- ãµã¼ãã¹ã®å質ã¯ãã¹ãã¨ãã©ã¼ãã§ããã
ã¨ãã£ãã¨ããã ã¨æãã¾ãã
使ã£ã¦ã¿ã
ç¾èã¯ä¸è¦ã«ãããâ¦ã¨ãããã¨ã§ã試ãã«ä¸åStreaming APIã使ã£ã¦ã¿ããã¨ã«ãã¾ãã
Streaming APIã®ç¨®é¡ã¯å¾è¿°ãã¾ãããããã§ã¯ã¨ãããã"spritzer" APIããµã³ãã«ã¨ãã¾ããããã¯Public Timelineããã©ã³ãã ã«æ½åºãããçºè¨ã延ã ã¨æµãã¦ããAPIã§ãã
#!/usr/bin/env ruby # coding: utf-8 require 'net/http' require 'uri' require 'rubygems' require 'json' USERNAME = '_USERNAME_' # ãããæ¸ãæãã PASSWORD = '_PASSWORD_' # ãããæ¸ãæãã uri = URI.parse('http://stream.twitter.com/spritzer.json') Net::HTTP.start(uri.host, uri.port) do |http| request = Net::HTTP::Get.new(uri.request_uri) # Streaming APIã¯Basicèªè¨¼ã®ã¿ request.basic_auth(USERNAME, PASSWORD) http.request(request) do |response| raise 'Response is not chuncked' unless response.chunked? response.read_body do |chunk| # 空è¡ã¯ç¡è¦ãã = JSONå½¢å¼ã§ã®ãã¼ã¹ã«å¤±æããã次㸠status = JSON.parse(chunk) rescue next # åé¤éç¥ãªã©ã'text'ãã©ã¡ã¼ã¿ãå«ã¾ãªããã®ã¯ç¡è¦ãã¦æ¬¡ã¸ next unless status['text'] user = status['user'] puts "#{user['screen_name']}: #{status['text']}" end end end
å®è¡ããã¨ãCtrl+Cãªã©ã§ããã»ã¹ãçµäºããã¾ã§ãæ¨æºåºåã«ããããã¨çºè¨ãåºã¦ããã¨æãã¾ããç§ã試ããã¨ãã¯ã ãããç§é5ã10çºè¨ç¨åº¦ã¨ãã£ãé度ã§ãããPublic Timelineã¯ã°ãã¼ãã«ãªã®ã§ã大åã¯è±èªã§åã¾ããã¨ã«ãªãã¾ãã
å®ç¨çã«ä½¿ãã®ã§ããã°ãåå¾ãããã¼ã¿ãæ¨æºåºåã«éãã®ã§ã¯ãªããä¾ãã°ãã¡ã¤ã«ããã¼ã¿ãã¼ã¹çµç±ããããã¯ããã»ã¹ééä¿¡ãªã©ã§å¥ããã»ã¹ã«å¼ã渡ããå¥ããã»ã¹ã§ãªã¢ã«ã¿ã¤ã ã«ãããã解æãã¦ãªãããã®çµ±è¨æ å ±ãå¾ãâ¦ã¨ãããããã使ãæ¹ã«ãªãã®ã§ã¯ãªããã¨æãã¾ãã*4
ã¡ãªã¿ã«ãä¸ã®ã³ã¼ãã¯ã¬ã¹ãã³ã¹ã«"Transfer-Encoding: chunked"ãã»ããããã¦ãããã¨ãæå¾ ããä½ãã«ãªã£ã¦ãã¾ããStreaming APIã§ã¯åºæ¬çã«ãããªã£ã¦ãããããªã®ã§åé¡ãªãã¨ã¯æãã¾ãããä»®ã«"Response is not chuncked"ã®ã¨ã©ã¼ãåºãããã§ããã°*5ã次ã®ããã«æ示çã«åºåãæåãæå®ãã¦ãã¼ã¿ãåãåºããããªå¦çã«ããã°å¤§ä¸å¤«ã ã¨æãã¾ãã
#!/usr/bin/env ruby # coding: utf-8 require 'net/http' require 'uri' require 'rubygems' require 'json' USERNAME = '_USERNAME_' # ãããæ¸ãæãã PASSWORD = '_PASSWORD_' # ãããæ¸ãæãã # Net::HTTPResponseã¯ã©ã¹ã«each_lineã¡ã½ããã追å module Net class HTTPResponse def each_line(rs = "\n") stream_check while line = @socket.readuntil(rs) yield line end self end end end uri = URI.parse('http://stream.twitter.com/spritzer.json') Net::HTTP.start(uri.host, uri.port) do |http| request = Net::HTTP::Get.new(uri.request_uri) request.basic_auth(USERNAME, PASSWORD) http.request(request) do |response| response.each_line("\r\n") do |line| status = JSON.parse(line) rescue next next unless status['text'] user = status['user'] puts "#{user['screen_name']}: #{status['text']}" end end end
ãã®éããã¼ã¿ã®åºåãã¯"\n"ã§ã¯ãªã"\r"ã§ãããã¨ã«æ³¨æãã¦ãã ããã*6"\n"ã¯æ¬æå ã«ãåºç¾ãå¾ãããã§ããç´°ãããã¨ã¯APIのドキュメントã«æ¸ãã¦ããã¾ãã
Streaming APIã®ç¨®é¡
Streaming APIã«ã¯2009/08/16ç¾å¨7ã¤ã®APIãç¨æããã¦ãã¾ããã使ãæ¹ã¯åºæ¬çã«å ¨ã¦åãã§ãã
ã¿ã¤ãå¥ã«å¤§ãã3ã¤ã«åãããã¨ãã§ãã¾ãã
Public Timelineåãæµãç³»
Public Timelineã®å 容ãã¤ã¾ãéµã®ããã£ã¦ããªãå ¨ã¦ã®çºè¨ãåå¾ãã¾ãã
ãã®ã¿ã¤ãã®APIã«ã¯"firehose", "gardenhose", "spritzer"ã®3ã¤ãç¨æããã¦ãã¾ãã
firehoseã¯Public Timelineã®å ¨ã¦ã®çºè¨ãå«ã¿ã¾ããå©ç¨ã«ã¯è¨±å¯ãå¿ è¦ã§ãã
gardenhoseã¯firehoseããã©ã³ãã ã«ãµã³ããªã³ã°ããçºè¨ãå«ã¿ã¾ããå©ç¨ã«ã¯è¨±å¯ãå¿ è¦ã§ãã
spritzerã¯gardenhoseãããæ´ã«å°ãªããµã³ããªã³ã°ããçºè¨ãå«ã¿ã¾ãã誰ã§ãå©ç¨ã§ãã¾ãã
GETã¡ã½ããã§ãªã¯ã¨ã¹ããéãã¾ãã"count", "delimited"ãã©ã¡ã¼ã¿ãåããã¨ãã§ãã¾ããã説æã¯çç¥ãã¾ãã
éµãããã¦ããªãå ¨ã¦ã®ã¦ã¼ã¶ã®å ¨ã¦ã®çºè¨ãæµãã¦ãã¾ãã®ã§ãå½ç¶firehoseã¯æãããéã®ãã¼ã¿ãéããã¦ãããã¨ã«ãªãã¾ããä¸çªéã®å°ãªãspritzerã§ãããªãã®å¢ãã§ãã®ã§ããã®è¾ºãæ±ãéã¯ååãªæ³¨æãå¿ è¦ã§ãããã
ç¹å®ã¦ã¼ã¶ã®çºè¨è¿½è·¡ç³»
"follow"ãã©ã¡ã¼ã¿ã§æå®ããã¦ã¼ã¶ã®çºè¨ãåã³ãã®ã¦ã¼ã¶ã«åããããçºè¨ã ããåå¾ã§ãã¾ãã*7éµä»ãã®ã¦ã¼ã¶ã®çºè¨ã¯å«ã¾ãã¾ããã
ãã®ã¿ã¤ãã®APIã«ã¯"birddog", "shadow", "follow"ã®3ã¤ãç¨æããã¦ãã¾ãã
birddogã¯followããã¦ã¼ã¶ã20ä¸ã¾ã§æå®ã§ãã¾ããå©ç¨ã«ã¯è¨±å¯ãå¿ è¦ã§ãã
shadowã¯followããã¦ã¼ã¶ã5ä¸ã¾ã§æå®ã§ãã¾ããå©ç¨ã«ã¯è¨±å¯ãå¿ è¦ã§ãã
followã¯followããã¦ã¼ã¶ã200ã¾ã§æå®ã§ãã¾ãã誰ã§ãå©ç¨ã§ãã¾ãã
POSTã¡ã½ããã§ãªã¯ã¨ã¹ããéãã¾ãã"follow"ãã©ã¡ã¼ã¿ã«ã¯ã追跡ãããã¦ã¼ã¶ã®ID*8ãã«ã³ãåºåãã§ä¸¦ã¹ã¦æå®ãã¾ãã"delimited"ãã©ã¡ã¼ã¿ã«ã¤ãã¦ã¯çç¥ãã¾ãã
ç¹å®ãã¼ã¯ã¼ãã®æ½åºç³»
"track"ãã©ã¡ã¼ã¿ã§æå®ãããã¼ã¯ã¼ããå«ãçºè¨ã ããåå¾ã§ãã¾ããéµä»ãã®ã¦ã¼ã¶ã®çºè¨ã¯å«ã¾ãã¾ããã
ãã®ã¿ã¤ãã®APIã«ã¯"track"ã®ã¿ãç¨æããã¦ãã¾ãã誰ã§ãå©ç¨ã§ãã¾ãã
POSTã¡ã½ããã§ãªã¯ã¨ã¹ããéãã¾ãã"track"ãã©ã¡ã¼ã¿ã«ã¯ããã¼ã¯ã¼ããã«ã³ãåºåãã§ä¸¦ã¹ã¦æå®ãã¾ãããã¼ã¯ã¼ãã¯50åã¾ã§ã1ã¤ã®ãã¼ã¯ã¼ãã®é·ãã¯1ã30ãã¤ãã§ãã
ãã¼ã¯ã¼ãã¯å¤§æåã»å°æåãåºå¥ãããåèªåä½ã§æ¤ç´¢ããã¾ããè¤æ°æå®ããå ´åã¯ORã«ãªãã¾ãã
ãã®âåèªåä½âãæ²è ã§ãç¾ç¶è±èªã§ã®åèªåä½â¦ã¤ã¾ãã¹ãã¼ã¹ããã®ä»è¨å·ã§åºåãããå ´åãããããããªãããã§ãæ¥æ¬èªãã¼ã¯ã¼ããæå®ããå ´åã¯ã»ã¨ãã©æ©è½ãã¾ããã
ãã ãããã·ã¥ã¿ã°ãªããã«å¯¾ããæ½åºã«ã¯ä½¿ãã¾ãã*9ãã¾ãä¾ãã°"nicovideo"ãæå®ããã¨ãã³ãã³åç»ã®URLãå«ãçºè¨ãæ½åºã§ãã¾ãã®ã§ã使ãããã«ãã£ã¦ã¯ãªãã¨ããªããã¨ãããã¾ãã
ããä¸å使ã£ã¦ã¿ã
æå¾ã«ããä¸ã¤ãµã³ãã«ããä»åº¦ã¯"track" APIã使ã£ã¦ã¿ã¾ãã
#!/usr/bin/env ruby # coding: utf-8 require 'net/http' require 'uri' require 'rubygems' require 'json' USERNAME = '_USERNAME_' # ãããæ¸ãæãã PASSWORD = '_PASSWORD_' # ãããæ¸ãæãã uri = URI.parse('http://stream.twitter.com/track.json') Net::HTTP.start(uri.host, uri.port) do |http| request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data('track' => 'bit') request.basic_auth(USERNAME, PASSWORD) http.request(request) do |response| raise 'Response is not chuncked' unless response.chunked? response.read_body do |chunk| status = JSON.parse(chunk) rescue next next unless status['text'] && status['text'].include?('http://bit.ly') user = status['user'] puts "#{user['screen_name']}: #{status['text']}" end end end
å®è¡ããã¨ãbit.lyã«ããç縮URLãå«ãã çºè¨ãããã¼ã£ã¨åºã¦ãã¾ãããããã«ããªãã®å¢ãã«ãªãã¾ãã®ã§æ³¨æãã¦ãã ããã
ãã¼ã¯ã¼ãã«ããããå«ããã¨ä¸æããããªããããªã®ã§ã"bit"ã§æ½åºãã¦ããã®å¾ã¹ã¯ãªããå ã§æ´ã«ãµããã«ããã¦ãã¾ãã
ãã®ãã¼ã¿ãå å·¥ããã°ãbit.lyã§ã©ããªURLã注ç®ãéãã¦ããããã¨ãã£ãçµ±è¨ãå¾ããã¨ãã§ãã¾ãã確ãæ¢ã«ãããªãµã¼ãã¹ããã£ãããã«è¨æ¶ãã¦ãã¾ããããããããããªæãã§Streaming APIãå©ç¨ãã¦ããã®ã§ã¯ãªãã§ããããã
ã¾ã¨ã
ã¾ã αãã¹ãä¸ãªãããå質é¢ã§ã¯é度ãªæå¾ ã¯ã§ãã¾ãããããã®APIèªä½ä»å¾ã©ãå¤ãã£ã¦ããããããã¾ããã*10ããããããã§ãã¢ã¤ãã¢æ¬¡ç¬¬ã§æ§ã ãªå¯è½æ§ãç§ãããã¦ããæ©è½ã ã¨æãã¾ããä¸åº¦Streaming APIã§éãã§ã¿ãã®ã¯ãããã§ãããã
*1:é常ã®APIã¨Streaming APIã®ä½µç¨ã¯å¯è½ã
*2:ãããã¯ããã«é¡ãããã®
*3:åã«ç¡è¦ããã°è¯ãã§ãã
*4:Rubyãªããã«ãã¹ã¬ããã§Queueã使ãã°ç°¡åã«ãã®é¡ã®é£æºãã§ãã¾ãã
*5:ä¸ã®ã³ã¼ãã®å ´åãèªè¨¼å¤±æãªã©ã§ã¨ã©ã¼ãçºçããéããããåºã¾ãã®ã§ãããã¯åºå¥ãããã注æãã¦ãã ããã
*6:"\r"ã®ãã¨ã«ã¯å¸¸ã«"\n"ãç¶ããããªã®ã§ãããã§ã¯ã¾ã¨ãã¦åºåãæåã¨ãã¦ãã¾ãã
*7:ããå³å¯ã«è¨ãã¨ã"in_reply_to_*"ãã©ã¡ã¼ã¿ã§ãã®ã¦ã¼ã¶ãæå®ããã¦ããçºè¨ã§ãã@ä»ãã§è¨åããã¦ãã¦ãã"in_reply_to_*"ãã©ã¡ã¼ã¿ã§æå®ããã¦ããªãå ´åã¯å«ã¾ãã¾ããã
*8:å¿ ãæ°å¤ã§æå®
*9:ããã·ã¥ã¿ã°ãæ¤ç´¢ããå ´åãã·ã£ã¼ã(#)ããã¼ã¯ã¼ãã«å«ããå¿ è¦ã¯ããã¾ãããåæã«ä¸æããã¨ãã£ã¦ããã¾ãã
*10:APIã®ã¡ã½ããåãã³ã¼ããã¼ã ã£ã½ãã§ãããæ£å¼ãªãªã¼ã¹ã®æã«ã¯å¤ãããããªæ°ããã¾ãã