ぬけラボ

φ(..)メモメモ

Twitter Streaming API + Comet(ストリーミング)

今更ながらTwitter Streaming APIを使ってみます。

Twitter Streaming APIとは

サーバにリクエスト投げて要求したXMLとかJSONファイルが返ってくるREST APIと違って、
Twitterのサーバにコネクション張りっぱなしの繋ぎっぱなしのStreaming API

1時間あたりのアクセス数によるAPI制限などは無いようですが、
1IPアドレスに1コネクションという制限があり、複数のブラウザで同時に扱うことは出来ないようです。

Streaming API Methods

それでは、Streaming APIを使ってツイートを検索してみましょう。
プログラミング言語PHPJavascriptを使用します。

実行環境 : Windows7_64bit, PHP5.3.5, Chrome or Firefox

Streaming APIへのアクセスはhttpsオンリーなので、
php.iniにopenssl.dllの設定が必要です。
XAMPPだとphp.iniに一行書き足すだけで良かった気がします。

php.ini

extension=php_openssl.dll

macの人はこちらを参照
http://jaspan.com/openssl-support-php-under-mamp


今回はハッシュタグに#nicovideoが設定されているツイートだけを表示するようにします。
注)現時点だと検索ワードに日本語は設定できないようです。

Streaming APIではBasic認証を使用します。usernameとpasswordは適切に設定してください。

stream.php

<?php
//タイムアウトをしないように設定
set_time_limit(0);

tweetStream('username', 'password', '#nicovideo');

/**
 * @param string $user		Twitterのログイン名
 * @param string $password	Twitterのパスワード
 * @param string $keyword	検索キーワード 
 **/
function tweetStream($user, $password, $keyword){
	$keyword = urlencode($keyword);
	$url = "https://{$user}:{$password}@stream.twitter.com/1/statuses/filter.json?track={$keyword}";

	if($stream = fopen($url, 'r')){
		while(!feof($stream)){
			$tweet= json_decode(fgets($stream), true);
			$user = $tweet['user']['screen_name'];
			$text = $tweet['text'];

			if(!empty($user)){
				$timeline = '<strong>'.$user.'</strong> : '.$text.PHP_EOL;
				echo $timeline;
				ob_flush();
				flush();
				sleep(1);
			}
		}
		fclose($stream);
	}
}

↓↓↓ 実行結果はこんな感じになります ↓↓↓

stream.phpでob_flush(); flush();を設定しているため、通信が完了しなくてもバッファをどんどん表示してくれます。

しかしながら、Streaming APIの仕様上、Twitterのサーバとコネクションを張りっぱなしな訳で、
タブのスピナーが回りっぱなしです。。。

Ajaxを使って、タブのスピナーが表示されないようにしましょう。
ついでに表示も最新のツイートが一番上に挿入されるように変更します。
改良にはJavascriptjQueryを使用します。

js/stream.js

$(function(){
	var ajax = $.ajaxSettings.xhr();
	ajax.open('post', 'stream.php', true);
	$('#tweet').html('Loadig...');
	ajax.send(null);

	ajax.onreadystatechange = function(){
		if(ajax.readyState == 2){
			$('#tweet').html('');
		}else if(ajax.readyState == 3){
			var data = ajax.responseText;
			var lines = data.split("\r\n");
			var line = lines[lines.length-2];

			if(line){
				$('<li>'+line+'</li>').prependTo('#tweet').hide().fadeIn('slow');
			}
		}else if(ajax.readyState == 4){
			$('#tweet').prepend('接続が切れました。リロードしてください。').css('color', 'red');
		}
	}
});

index.html

<!DOCTYPE HTML>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
	</head>
	<body>
		<h2>Twitter #nicovideo Stream</h2>
		<ul id="tweet"></ul>	
		<script type="text/javascript" src="http://www.google.com/jsapi"></script>
		<script type="text/javascript">google.load("jquery", "1.7");</script>
		<script type="text/javascript" src="js/stream.js"></script>
	</body>
</html>

最後にツイート中のリンクをクリック出来るようにaタグを自動生成したり、
ユーザの画像なども表示するようにstream.phpを修正してみます。

stream.php

<?php
//タイムアウトをしないように設定
set_time_limit(0);

/**
 * ハッシュタグに#nicovideoが設定されているツイートだけ表示
 * usernameとpasswordは適切に設定してください
 **/
tweetStream('username', 'password', '#nicovideo');

/**
 * @param string $user		Twitterのログイン名
 * @param string $password	Twitterのパスワード
 * @param string $keyword	検索キーワード 
 **/
function tweetStream($user, $password, $keyword){
	$keyword = urlencode($keyword);
	$url = "https://{$user}:{$password}@stream.twitter.com/1/statuses/filter.json?track={$keyword}";

	if($stream = fopen($url, 'r')){
		while(!feof($stream)){
			//tweet data
			$tweet= json_decode(fgets($stream), true);
			//user name
			$user = $tweet['user']['screen_name'];
			//user image
			$userImage = $tweet['user']['profile_image_url'];
			//tweet text
			$text = $tweet['text'];

			if(!empty($user)){
				$timeline =  '<img src="'.$userImage.'">'.' <strong>'.$user.'</strong> : '.makeLink($text).PHP_EOL;
				echo $timeline;
				ob_flush();
				flush();
				sleep(1);
			}
		}
		fclose($stream);
	}
}

/**
 * リンク作成
 * @param string The url string
 * @return string
 **/
function makeLink($string){
	/*** make sure there is an http:// on all URLs ***/
	$string = preg_replace("/([^\w\/])(www\.[a-z0-9\-]+\.[a-z0-9\-]+)/i", "$1http://$2",$string);
	/*** make all URLs links ***/
	$string = preg_replace("/([\w]+:\/\/[\w-?&;#~=\.\/\@]+[\w\/])/i","<a target=\"_blank\" href=\"$1\">$1</A>",$string);
	/*** make all emails hot links ***/
	$string = preg_replace("/([\w-?&;#~=\.\/]+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?))/i","<A HREF=\"mailto:$1\">$1</A>",$string);

	return $string;
}

サンプルコードはgithubにアップロードしました。
https://github.com/hiro-su/twitter-streaming-api-test


↓↓↓ 実行結果はこんな感じになります ↓↓↓

stream.jsですが、stream.phpの表示データをAjaxで読込みます。

AjaxXmlHttpRequestのステータスが3(読込んだデータを解析中)の時にツイートを表示するようにしています。
こうすることで、サーバからレスポンスを受け取るためのクライアントリクエストが発生しません。
クライアントからのリクエストが無くてもサーバからレスポンスを受け取れます。

通常のAjaxだとステータスが4(通信完了)でレスポンスを受け取ります。
ステータスが4になるにはクライアントからサーバへのリクエストとサーバからクライアントへのレスポンスのやり取りが完了した時です。
このようなクライアントからのリクエストが無くてもサーバからレスポンスを得られる手法をComet(ストリーミング)と言うそうです。


これを利用してニコニコ動画の右から左へ流れるコメント的なのを近いうちに作ってみたいと思います。