GroongaとTwitter StreamAPI 
で 
お手軽データ解析 
2014/09/04 
ConoHa presents - Groonga "How-To" Talks
自己紹介 
• 斉藤弘信(@hironobu_s) 
• 開発&インフラ屋 
• 所属:GMOインターネット 
最近ConoHaの中の人になりました
あらすじ 
• このイベントの雑用係として打ち合わせに参加 
• そこにいたGroongaの方とSennaの話をする 
• 登壇<= イマココ
今日やること 
• github.com/gc37/20140904-groonga 
• Groonga初心者の体験談 
• Twitter StreamAPIを使ってデータを収集 
• Groongaにストア+ちょっとしたデータ解析 
• PHP + GroongaHTTPサーバ 
• インフラとしてConoHa VPS(CentOS6.5)
Twitter Steraming API 
• HTTPで接続すると永遠にツイートデータが流れ 
てくる 
• 流れてくるのは全ツイートの1%程度のサンプル 
• 接続するエンドポイントによってとれる情報が違 
う 
• 今回は日本(lang=ja)を指定して取得してみた。
今回作ったもの
PHPとGroonga 
• GroongaのHTTPサーバを使用 
• PHPからはcURLでアクセス 
• 簡易的なGroongaアクセスクラスを書いた 
(groonga.php) 
• Mroongaの方が簡単だったか・・・
• github.com/gc37/20140904-groonga 
使用例 
<?php 
require_once 'groonga.php'; 
$groonga = new Groonga('localhost', 10041); 
$cond = [ 
'table' => 'TableName', 
'match_columns' => 'test_col', 
'query' => 'hoge', 
'limit' => 10, 
'offset' => 0 
]; 
$r = $groonga->execute('select', $cond); 
// ヒット数 
var_dump($r->count); 
// 列情報 
var_dumP($r->headers); 
// 実行結果 
var_dumP($r->results); 
example.php
• インスタンスを作る 
• 実行するコマンドのパラメータを配列にする 
• コマンド名を渡してexecute()メソッドを叩く 
• いじょう
実行結果 
int(161) <=== ヒット数 
array(10) { <=== 検索結果 
[0]=> 
array(7) { 
["_id"]=> 
int(90032) 
["created_at"]=> 
float(1408890604) 
["date"]=> 
string(10) "2014-08-24" 
["name"]=> 
string(11) "hiroshi5s38" 
["screen_name"]=> 
string(36) "レンタルサーバーの選び方" 
["source"]=> 
string(11) "sakura_blog" 
["text"]=> 
string(106) "『サーバーの悩み』レンタルサーバー比較- サービスの比…|http://t.co/j1AtQfUXOL" 
} 
• ヒット数、列情報、実行結果がオブジェクトで返る 
• 実行結果が連想配列になる
実装 
• github.com/gc37/20140904-groonga 
/** 
* Groongaサーバにリクエストを送信して、結果をJSONで返す 
* 
* @param string $cmd コマンド名 
* @param array $params コマンドに渡すパラメータ配列 
* @return array 
*/ 
private function sendRequest($cmd, $params = []) 
{ 
$url = sprintf('http://%s:%d/d/%s', $this->host, $this->port, $cmd); 
$url .= '?' . http_build_query($params); 
// リクエストを送信 
$curl = $this->initializeCurl(); 
curl_setopt($curl, CURLOPT_URL, $url); 
$body = curl_exec($curl); 
if( ! $body) { 
throw new RuntimeException('HTTP Request fail.'); 
} 
// レスポンスをデコードする 
$json = json_decode($body, true); 
if( ! $json) { 
throw new RuntimeException('Incorrect datatype for JSON.'); 
} 
// この要素が0未満の場合はコマンド実行失敗で、[0][3]にメッセージが入るっぽい。 
if($json[0][0] < 0) { 
throw new RuntimeException($json[0][3]); 
} 
return $json; 
}
• PHPは便利機能だらけなので、Groonga HTTPク 
ライアントがすぐ作れる 
• http_build_query: QueryStringを作ってくれる 
• cURL: HTTPのことは全部面倒見てくれる 
• json_decode: JSON文字列を連想配列に変換
データ取得スクリプト概要 
• twapi_streaming.php 
• Phirehoseライブラリを使わせていただいた 
(https://github.com/fennb/phirehose) 
• StreamingAPIからのJSONデータをパースして 
Groonga用のJSONオブジェクトを作る 
• 自作GroongaクラスとPhirehoseクラスの合わせ技
Phirehoseライブラリの使い方 
<?php 
require_once 'phirehose/lib/Phirehose.php'; 
require_once 'phirehose/lib/OauthPhirehose.php'; 
// OAuth 
$access_token = '************************************************'; 
$access_token_secret = '******************************************'; 
class TestStream extends OauthPhirehose 
{ 
public function enqueueStatus($status) 
{ 
// ここに実装を書くだけ 
// $status が1ツイートのJSONデータになっている 
// 
// また今回の場合、自動的に以下のエンドポイントが選択される 
// https://stream.twitter.com/1.1/statuses/filter.json 
} 
} 
$s = new TestStream($access_token, $access_token_secret); 
$s->setLang('ja'); 
$s->consume();
Groongaへの保存 
<?php 
• twapi_streaming.php 
// Groongaにデータを保存する 
// $dataはツイートオブジェクト 
$tweet = [ 
'_key' => $data->id_str, 
'text' => $data->text, 
'source' => $data->source, 
'name' => $data->user->name, 
'screen_name' => $data->user->screen_name, 
'created_at' => $time 
]; 
if($data->geo != null) { 
$geo = $data->geo->coordinates[0] . 'x' . $data->geo->coordinates[1]; 
$tweet['geo'] = $geo; 
} 
$data = [ 
'table' => 'TwSource', 
'values' =>json_encode([ $tweet ]) 
]; 
$r = $this->groonga->execute('load', $data);
Groonga側の構成
インデックス作成 
• ツイートデータをTwSourceテーブルに保存 
• textカラムにツイート本文が含まれる 
• このカラムに全文検索インデックスを設定
<?php 
$g = new Groonga('localhost', 10041); 
// 既存テーブル削除 
try { 
$params = [ 
'name' => 'TwIndex' 
]; 
$g->execute('table_remove', $params); 
} catch(Exception $e) {} 
// インデックステーブルを作成 
$params = [ 
'name' => 'TwIndex', 
'flags' => 'TABLE_PAT_KEY|KEY_NORMALIZE', 
'key_type' => 'ShortText', 
'default_tokenizer' => 'TokenMecab', 
]; 
$g->execute('table_create', $params); 
// インデックスカラムを作成 
$params = [ 
'table' => 'TwIndex', 
'name' => 'text', 
'flags' => 'COLUMN_INDEX|WITH_POSITION', 
'type' => 'TwSource', 
'source' => 'text' 
]; 
$g->execute('column_create', $params);
• ここはGroongaドキュメントの通り 
• 自作Groongaライブラリを通してインデックスを 
作成する 
• 今回はトークナイザにMeCabを使用した
データ分析スクリプト概要 
• select1.php, select2.php 
• 簡単な分析をする 
• 対象は8/24〜9/2までのツイートデータ4,380,001 
件 
• (ただし8/29、8/29、8/30の一部は事故で取得失敗・・・) 
• キーワードの出現頻度を見てみましょう
急上昇ワード 
• Yahoo急上昇ワードに登場するワードを、今回の 
解析基盤で調べてみます。 
• 8/25(火) PSN障害(ゲーム) 
• 8/26(水) ランドクルーザー(車) 
• 8/27(木) ヨンア(モデル)、芝幸太郎(実業家)
[hiro@MBP]# ./select_burst.php PSN障害 
2014-08-24: 15 
2014-08-25: 16 
2014-08-26: 12 
2014-08-27: 2 
2014-08-28: 0 
2014-08-29: 0 
2014-08-30: 0 
2014-08-31: 0 
2014-09-01: 0 
[hiro@MBP]# ./select_burst.php ランドクルーザー 
2014-08-24: 0 
2014-08-25: 84 
2014-08-26: 20 
2014-08-27: 5 
2014-08-28: 4 
2014-08-29: 0 
2014-08-30: 0 
2014-08-31: 1 
2014-09-01: 5 
[hiro@MBP]# ./select_burst.php ヨンア 
2014-08-24: 0 
2014-08-25: 2 
2014-08-26: 209 
2014-08-27: 29 
2014-08-28: 14 
2014-08-29: 0 
2014-08-30: 0 
2014-08-31: 0 
2014-09-01: 2 
[hiro@MBP]# ./select_burst.php 芝幸太郎 
2014-08-24: 0 
2014-08-25: 0 
2014-08-26: 135 
2014-08-27: 10 
2014-08-28: 1 
2014-08-29: 0 
2014-08-30: 0 
2014-08-31: 0 
2014-09-01: 0 
25日 
26日 
27日
時事ワード 
• デング熱 
• 急性の熱性感染症。戦後初の国内感染を確認 
• ↑このニュースが出たのが27日 
[hiro@MBP]# ./select_burst.php デング熱 
2014-08-24: 0 
2014-08-25: 0 
2014-08-26: 0 
2014-08-27: 332 
2014-08-28: 756 
2014-08-29: 0 
2014-08-30: 0 
2014-08-31: 38 
2014-09-01: 837 
(29日、30日、31日はデータ取得失敗)
まとめ
Groongaは速かった 
• Senna経験者で今回初めてGroongaを触った人の感想です 
• とにかく速い。インデックスにあるカラムなら400万件でも一 
瞬で結果が返る 
• データが小さい。メモリに全部載る(今回のデータで3.7GB) 
• 全文検索インデックスの作成がずいぶん短い。最初はほんとに 
作られてるのか疑った。 
• HTTPサーバは簡単にプログラムが書けたので助かった。オー 
バーヘッドがあるかも?
時間がなくて試せなかった 
• suggestプラグインで補完、提案を試したい 
• Mroonga 
• Geoデータ(ツイートには結構、緯度経度が入ってた)
ご清聴 
ありがとうございました
宣伝
ConoHaリニューアルしました
オブジェクトストレージ 
容量無制限、転送量課金無し、今日から使えます 
月額450円/100GB
ブログ書いてます
ConoHa支援プログラム
もちろんVPSも
ConoHaを始めてみよう 
• https://www.conoha.jp/ 
• 全員1500円分のクーポン付き 
• さらに来場者限定で 
3000円クーポン 
• 併せて4500円分使えます! 
• VPSでもオブジェクトストレージで 
も使えます! 
• ストレージだけなら10ヶ月使えます 
! 
おわり

20140903groonga発表資料

  • 1.
    GroongaとTwitter StreamAPI で お手軽データ解析 2014/09/04 ConoHa presents - Groonga "How-To" Talks
  • 2.
    自己紹介 • 斉藤弘信(@hironobu_s) • 開発&インフラ屋 • 所属:GMOインターネット 最近ConoHaの中の人になりました
  • 3.
    あらすじ • このイベントの雑用係として打ち合わせに参加 • そこにいたGroongaの方とSennaの話をする • 登壇<= イマココ
  • 4.
    今日やること • github.com/gc37/20140904-groonga • Groonga初心者の体験談 • Twitter StreamAPIを使ってデータを収集 • Groongaにストア+ちょっとしたデータ解析 • PHP + GroongaHTTPサーバ • インフラとしてConoHa VPS(CentOS6.5)
  • 5.
    Twitter Steraming API • HTTPで接続すると永遠にツイートデータが流れ てくる • 流れてくるのは全ツイートの1%程度のサンプル • 接続するエンドポイントによってとれる情報が違 う • 今回は日本(lang=ja)を指定して取得してみた。
  • 6.
  • 7.
    PHPとGroonga • GroongaのHTTPサーバを使用 • PHPからはcURLでアクセス • 簡易的なGroongaアクセスクラスを書いた (groonga.php) • Mroongaの方が簡単だったか・・・
  • 8.
    • github.com/gc37/20140904-groonga 使用例 <?php require_once 'groonga.php'; $groonga = new Groonga('localhost', 10041); $cond = [ 'table' => 'TableName', 'match_columns' => 'test_col', 'query' => 'hoge', 'limit' => 10, 'offset' => 0 ]; $r = $groonga->execute('select', $cond); // ヒット数 var_dump($r->count); // 列情報 var_dumP($r->headers); // 実行結果 var_dumP($r->results); example.php
  • 9.
    • インスタンスを作る •実行するコマンドのパラメータを配列にする • コマンド名を渡してexecute()メソッドを叩く • いじょう
  • 10.
    実行結果 int(161) <===ヒット数 array(10) { <=== 検索結果 [0]=> array(7) { ["_id"]=> int(90032) ["created_at"]=> float(1408890604) ["date"]=> string(10) "2014-08-24" ["name"]=> string(11) "hiroshi5s38" ["screen_name"]=> string(36) "レンタルサーバーの選び方" ["source"]=> string(11) "sakura_blog" ["text"]=> string(106) "『サーバーの悩み』レンタルサーバー比較- サービスの比…|http://t.co/j1AtQfUXOL" } • ヒット数、列情報、実行結果がオブジェクトで返る • 実行結果が連想配列になる
  • 11.
    実装 • github.com/gc37/20140904-groonga /** * Groongaサーバにリクエストを送信して、結果をJSONで返す * * @param string $cmd コマンド名 * @param array $params コマンドに渡すパラメータ配列 * @return array */ private function sendRequest($cmd, $params = []) { $url = sprintf('http://%s:%d/d/%s', $this->host, $this->port, $cmd); $url .= '?' . http_build_query($params); // リクエストを送信 $curl = $this->initializeCurl(); curl_setopt($curl, CURLOPT_URL, $url); $body = curl_exec($curl); if( ! $body) { throw new RuntimeException('HTTP Request fail.'); } // レスポンスをデコードする $json = json_decode($body, true); if( ! $json) { throw new RuntimeException('Incorrect datatype for JSON.'); } // この要素が0未満の場合はコマンド実行失敗で、[0][3]にメッセージが入るっぽい。 if($json[0][0] < 0) { throw new RuntimeException($json[0][3]); } return $json; }
  • 12.
    • PHPは便利機能だらけなので、Groonga HTTPク ライアントがすぐ作れる • http_build_query: QueryStringを作ってくれる • cURL: HTTPのことは全部面倒見てくれる • json_decode: JSON文字列を連想配列に変換
  • 13.
    データ取得スクリプト概要 • twapi_streaming.php • Phirehoseライブラリを使わせていただいた (https://github.com/fennb/phirehose) • StreamingAPIからのJSONデータをパースして Groonga用のJSONオブジェクトを作る • 自作GroongaクラスとPhirehoseクラスの合わせ技
  • 14.
    Phirehoseライブラリの使い方 <?php require_once'phirehose/lib/Phirehose.php'; require_once 'phirehose/lib/OauthPhirehose.php'; // OAuth $access_token = '************************************************'; $access_token_secret = '******************************************'; class TestStream extends OauthPhirehose { public function enqueueStatus($status) { // ここに実装を書くだけ // $status が1ツイートのJSONデータになっている // // また今回の場合、自動的に以下のエンドポイントが選択される // https://stream.twitter.com/1.1/statuses/filter.json } } $s = new TestStream($access_token, $access_token_secret); $s->setLang('ja'); $s->consume();
  • 15.
    Groongaへの保存 <?php •twapi_streaming.php // Groongaにデータを保存する // $dataはツイートオブジェクト $tweet = [ '_key' => $data->id_str, 'text' => $data->text, 'source' => $data->source, 'name' => $data->user->name, 'screen_name' => $data->user->screen_name, 'created_at' => $time ]; if($data->geo != null) { $geo = $data->geo->coordinates[0] . 'x' . $data->geo->coordinates[1]; $tweet['geo'] = $geo; } $data = [ 'table' => 'TwSource', 'values' =>json_encode([ $tweet ]) ]; $r = $this->groonga->execute('load', $data);
  • 16.
  • 17.
    インデックス作成 • ツイートデータをTwSourceテーブルに保存 • textカラムにツイート本文が含まれる • このカラムに全文検索インデックスを設定
  • 18.
    <?php $g =new Groonga('localhost', 10041); // 既存テーブル削除 try { $params = [ 'name' => 'TwIndex' ]; $g->execute('table_remove', $params); } catch(Exception $e) {} // インデックステーブルを作成 $params = [ 'name' => 'TwIndex', 'flags' => 'TABLE_PAT_KEY|KEY_NORMALIZE', 'key_type' => 'ShortText', 'default_tokenizer' => 'TokenMecab', ]; $g->execute('table_create', $params); // インデックスカラムを作成 $params = [ 'table' => 'TwIndex', 'name' => 'text', 'flags' => 'COLUMN_INDEX|WITH_POSITION', 'type' => 'TwSource', 'source' => 'text' ]; $g->execute('column_create', $params);
  • 19.
    • ここはGroongaドキュメントの通り •自作Groongaライブラリを通してインデックスを 作成する • 今回はトークナイザにMeCabを使用した
  • 20.
    データ分析スクリプト概要 • select1.php,select2.php • 簡単な分析をする • 対象は8/24〜9/2までのツイートデータ4,380,001 件 • (ただし8/29、8/29、8/30の一部は事故で取得失敗・・・) • キーワードの出現頻度を見てみましょう
  • 21.
    急上昇ワード • Yahoo急上昇ワードに登場するワードを、今回の 解析基盤で調べてみます。 • 8/25(火) PSN障害(ゲーム) • 8/26(水) ランドクルーザー(車) • 8/27(木) ヨンア(モデル)、芝幸太郎(実業家)
  • 22.
    [hiro@MBP]# ./select_burst.php PSN障害 2014-08-24: 15 2014-08-25: 16 2014-08-26: 12 2014-08-27: 2 2014-08-28: 0 2014-08-29: 0 2014-08-30: 0 2014-08-31: 0 2014-09-01: 0 [hiro@MBP]# ./select_burst.php ランドクルーザー 2014-08-24: 0 2014-08-25: 84 2014-08-26: 20 2014-08-27: 5 2014-08-28: 4 2014-08-29: 0 2014-08-30: 0 2014-08-31: 1 2014-09-01: 5 [hiro@MBP]# ./select_burst.php ヨンア 2014-08-24: 0 2014-08-25: 2 2014-08-26: 209 2014-08-27: 29 2014-08-28: 14 2014-08-29: 0 2014-08-30: 0 2014-08-31: 0 2014-09-01: 2 [hiro@MBP]# ./select_burst.php 芝幸太郎 2014-08-24: 0 2014-08-25: 0 2014-08-26: 135 2014-08-27: 10 2014-08-28: 1 2014-08-29: 0 2014-08-30: 0 2014-08-31: 0 2014-09-01: 0 25日 26日 27日
  • 23.
    時事ワード • デング熱 • 急性の熱性感染症。戦後初の国内感染を確認 • ↑このニュースが出たのが27日 [hiro@MBP]# ./select_burst.php デング熱 2014-08-24: 0 2014-08-25: 0 2014-08-26: 0 2014-08-27: 332 2014-08-28: 756 2014-08-29: 0 2014-08-30: 0 2014-08-31: 38 2014-09-01: 837 (29日、30日、31日はデータ取得失敗)
  • 24.
  • 25.
    Groongaは速かった • Senna経験者で今回初めてGroongaを触った人の感想です • とにかく速い。インデックスにあるカラムなら400万件でも一 瞬で結果が返る • データが小さい。メモリに全部載る(今回のデータで3.7GB) • 全文検索インデックスの作成がずいぶん短い。最初はほんとに 作られてるのか疑った。 • HTTPサーバは簡単にプログラムが書けたので助かった。オー バーヘッドがあるかも?
  • 26.
    時間がなくて試せなかった • suggestプラグインで補完、提案を試したい • Mroonga • Geoデータ(ツイートには結構、緯度経度が入ってた)
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 35.
    ConoHaを始めてみよう • https://www.conoha.jp/ • 全員1500円分のクーポン付き • さらに来場者限定で 3000円クーポン • 併せて4500円分使えます! • VPSでもオブジェクトストレージで も使えます! • ストレージだけなら10ヶ月使えます ! おわり