PHPでTwitterのBotを作ってみる

Twitterで自分のタイムラインに表示されたメッセージにキーワードを見つけたら、何か反応を投稿するボットを作ってみる。

仕様

  • /homeと/repliesからメッセージを1ページ取得する。
  • メッセージにキーワードがあれば対応するメッセージを投稿する。
  • repliesを優先してレスする。

実装

PEAR::HTTP_Clientを使う。コードは結構な量になってるので、要所だけ書く。
class TwitterBotとして実装。

require_once "HTTP/Client.php";

タイムラインの取得

Twitterはベーシック認証のみを正式サポートしているのでPEAR::HTTP_Clientでベーシック認証を行う。

$basic = array('Authorization'=>'Basic '.base64_encode($this->username.':'.$this->password));

$client = new HTTP_Client(null, $basic);
$client->get("http://twitter.com/".$page);

$response = $client->currentResponse();		
$body = mb_convert_encoding($response['body'], "UTF-8","JIS,UTF-8,SJIS,EUC-JP");

$this->usernameにはユーザ名(メアドではない)、$this->passwordにはパスワードをセット。$pageには"home"や"replies"を渡す。
$responseには取得結果が格納されていて、'code'、'headers'、'body'のキーを持っている。'body'を取得して、UTF-8にコンバートする。

スクレイピング

取得したHTMLから目的の情報だけを取り出す。取り出したい情報は

  • ステータス番号 ($status_number)
  • ユーザ名 ($username)
  • メッセージ ($word)
  • @先 ($at)

の4つ。
うまいやり方が分からなかったので、strpos()とsubstr()で目的の情報が含まれる部分を愚直に取り出した後、preg_match()で正規表現マッチングして情報を抜き出す。これをwhileループで回す*1

$st = $ed = 0;

while(true)
{
	$st = strpos($body, '<tr class="hentry"', $ed);
	if($st===false) break;
	$ed = strpos($body, '<td class="thumb vcard author">', $st) or error_message("strpos error.");
	$line = substr($body, $st, $ed-$st);
	
	preg_match('/<tr class="hentry" id="status_(\d+)">/', $line, $matches);
	$status_number = $matches[1];
	
	$st = strpos($body, '<strong>', $ed) or error_message("strpos error.");
	$ed = strpos($body, '</strong>', $st) or error_message("strpos error.");
	$line = substr($body, $st, $ed-$st);
	
	preg_match('/<a href="http:\/\/twitter.com\/.+" title=".+">(.+)<\/a>/', $line, $matches);
	$username = $matches[1];
	
	$st = strpos($body, '<span class="entry-title entry-content">', $ed) or error_message("strpos error.");
	$ed = strpos($body, '<span class="meta entry-meta">', $st) or error_message("strpos error.");
	$line = substr($body, $st, $ed-$st);
	$line = str_replace("\n", "", $line);
	
	preg_match('/content">(.+)<\/span>/', $line, $matches);
	$word = trim($matches[1]);
	
	if(preg_match('/@<a href="\/(.+)">(.+)<\/a>/', $word, $matches))
	{
		$at = $matches[1];
	}
	else
	{
		$at = "";
	}
	
	$twitter_list[] = array("status_number"=>$status_number, "username"=>$username, "word"=>$word, "at"=>$at);
}

return array_reverse($twitter_list);

これでreturnされた配列には、昔の発言から順にメッセージが格納されている。foreach($twitter_list as $data)とでもして、$data["word"]などを参照すればよい。


これらのデータを用いてキーワードを判定したりは大丈夫だと思うので、割愛する。

メッセージを投稿

投稿するメッセージが決まったら、Twitterにポストする。

$basic = array('Authorization'=>'Basic '.base64_encode($this->username.':'.$this->password));

$client = new HTTP_Client(null, $basic);
$client->post("http://twitter.com/status/update", array("status"=>$reply_message));

これだけでOK。

他に必要な実装

チェックしたステータス番号の保存

ステータス番号はユーザに依らず、新しいメッセージほど大きくなっている。前回最後にチェックしたステータス番号以下なら処理をスキップすればよい。

定期更新

ブラウザからアクセスして使うことを想定しているので、定期的にリロードしたい。60秒に1回リロードしたければHTMLのヘッダに以下を記述する。

<meta http-equiv="Refresh" content="60">

*1:もっと良いやり方、ライブラリ等があれば教えてください。