Elasticsearch+KibanaでTwitter Streamを収集する

データ収集・解析システムElasticsearchとWebフロントエンドKibanaを使い、Twitter Streamを収集する方法のメモ。

環境

Ubuntu 14.04.1 LTS 64bit版

$ uname -a
Linux vm-ubuntu64-twitter 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.1 LTS
Release:        14.04
Codename:       trusty

Java Runtime Environmentのインストール

ElasticsearchはJavaで実装されているので、Javaの実行環境をインストールする。 Java実装としてはOracle's JavaとOpenJDKがサポートされているので、ここではOpenJDKのランタイムをインストールする。

$ sudo apt-get install default-jre

Elasticsearch、Kibana Plugin、Twitter River Pluginのインストール

下のサイトに書いてある手順を参考に、まずはElasticsearchの最新版をダウンロードし展開する。

$ wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.3.4.tar.gz

$ tar zxvf elasticsearch-1.3.4.tar.gz

$ cd elasticsearch-1.3.4/

次に、Kibanaをインストールする。 ApacheなどのHTTPサーバを使ってKibanaを利用する方法もあるが、ここではKibana Pluginを使うことにする。

$ bin/plugin -url http://download.elasticsearch.org/kibana/kibana/kibana-latest.zip -install elasticsearch/kibana3

Twitter Streamの収集を行うプラグインTwitter River Pluginをインストールする。

$ bin/plugin -install elasticsearch/elasticsearch-river-twitter/2.3.0

Elasticsearchをバックグラウンドで起動する。

$ bin/elasticsearch -d

http://localhost:9200/_plugin/kibana3/にアクセスして、Kibanaのダッシュボードが表示されることを確認する。

Twitter Stream用のカスタムマッピングを作成する

前述のサイトを参考に、Twitter Stream用のカスタムマッピングを作成する。

$ vi twitter.json
{
  "settings": {
    "analysis": {
      "tokenizer": {
        "ngram_tokenizer": {
          "type": "nGram",
          "min_gram": 3,
          "max_gram": 3,
          "token_chars": ["letter", "digit"]
        }
      },
      "analyzer": {
        "facet_analyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "keyword",
          "filter": ["cjk_width", "lowercase"]
        },
        "ngram_analyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "ngram_tokenizer",
          "filter": ["cjk_width", "lowercase"]
        }
      }
    }
  },
  "mappings": {
    "status" : {
       "_all": {
         "enabled": true,
         "analyzer": "ngram_analyzer"
       },
       "properties" : {
         "created_at" : {
           "type" : "date",
           "format" : "dateOptionalTime"
         },
         "hashtag" : {
           "properties" : {
             "end" : {
               "type" : "long"
             },
             "start" : {
               "type" : "long"
             },
             "text" : {
               "type" : "string",
               "index": "analyzed",
               "analyzer": "facet_analyzer"
             }
           }
         },
         "in_reply" : {
           "properties" : {
             "status" : {
               "type" : "long"
             },
             "user_id" : {
               "type" : "long"
             },
             "user_screen_name" : {
               "type" : "string",
               "index": "analyzed",
               "analyzer": "facet_analyzer"
             }
           }
         },
        "language" : {
          "type" : "string",
          "index": "analyzed",
          "analyzer": "facet_analyzer"
        },
        "link" : {
          "properties" : {
            "display_url" : {
              "type" : "string"
            },
            "end" : {
              "type" : "long"
            },
            "expand_url" : {
              "type" : "string"
            },
            "start" : {
              "type" : "long"
            },
            "url" : {
              "type" : "string"
            }
          }
       },
       "location" : {
         "type" : "geo_point"
       },
       "mention" : {
         "properties" : {
           "end" : {
             "type" : "long"
           },
           "id" : {
             "type" : "long"
           },
           "name" : {
             "type" : "string",
             "index": "not_analyzed"
           },
           "screen_name" : {
             "type" : "string",
             "index" : "not_analyzed"
           },
           "start" : {
             "type" : "long"
           }
         }
       },
       "place" : {
         "properties" : {
           "country" : {
             "type" : "string"
           },
           "country_code" : {
             "type" : "string"
           },
           "full_name" : {
             "type" : "string"
           },
           "id" : {
             "type" : "string"
           },
           "name" : {
             "type" : "string"
           },
           "type" : {
             "type" : "string"
           },
           "url" : {
             "type" : "string"
           }
         }
       },
       "retweet" : {
         "properties" : {
           "id" : {
             "type" : "long"
           },
           "retweet_count" : {
             "type" : "long"
           },
           "user_id" : {
             "type" : "long"
           },
           "user_screen_name" : {
             "type" : "string"
           }
         }
       },
       "retweet_count" : {
         "type" : "long"
       },
       "source" : {
         "type" : "string",
         "index": "analyzed",
         "analyzer": "facet_analyzer"
       },
       "text" : {
         "type" : "string",
         "index": "analyzed",
         "analyzer": "ngram_analyzer"
       },
       "truncated" : {
         "type" : "boolean"
       },
       "user" : {
         "properties" : {
           "description" : {
             "type" : "string",
             "index": "analyzed",
             "analyzer": "ngram_analyzer"
           },
           "id" : {
             "type" : "long"
           },
           "location" : {
             "type" : "string",
             "index": "analyzed",
             "analyzer": "facet_analyzer"
           },
           "name" : {
             "type" : "string",
             "index": "not_analyzed"
           },
           "profile_image_url" : {
             "type" : "string"
           },
           "profile_image_url_https" : {
             "type" : "string"
           },
           "screen_name" : {
             "type" : "string",
             "index" : "not_analyzed"
           }
         }
       }
     }
   }
 }
}

Twitter Streamを収集する

Twitter Streamの収集を行うためのシェルスクリプトを作成する。

$ vi twitter_create_index.sh
$ vi twitter_start.sh
$ vi twitter_stop.sh

内容は順に以下の通り。OAuth関連のトークンは別途取得し置き換える。 ここではtypeプロパティをuserとすることで、認証を行ったユーザのUserStreamを収集するように設定している。

curl -XPUT 'localhost:9200/twitter' -d @twitter.json
curl -X PUT 'localhost:9200/_river/twitter/_meta' -d '{
  "type": "twitter",
  "twitter": {
    "oauth": {
      "consumer_key": "*** YOUR Consumer key HERE ***",
      "consumer_secret": "*** YOUR Consumer secret HERE ***",
      "access_token": "*** YOUR Access token HERE ***",
      "access_token_secret": "*** YOUR Access token secret HERE ***"
    },
    "type": "user"
  },
  "index": {
    "index": "twitter",
    "type": "status",
    "bulk_size": 100,
    "flush_interval" : "5s"
  }
}'
curl -XDELETE 'localhost:9200/_river/twitter'

インデックスを作成し、Twitter Streamの収集を開始する。

$ bash twitter_create_index.sh
{"acknowledged":true}

$ bash twitter_start.sh
{"_index":"_river","_type":"twitter","_id":"_meta","_version":1,"created":true}

停止するには、twitter_stop.shを実行すればよい。

KibanaのDashboardを設定する

ブラウザからKibanaにアクセスし、トップページに表示されたBlank Dashboardのリンクをクリックする。

次に、右上のConfigure Dashboardをクリックし、次の通り設定し、Saveボタンで保存する。

  • Generalタブ: Titleを「Twitter Dashboard」に変更
  • Indexタブ: Default Indexを「twitter」に変更
  • Rowsタブ: 「Graph」「Status」の二つを作成
  • Timepickerタブ: Time Fieldを「created_at」に変更

右上のSaveボタンから、Dashboard設定を保存する。 パネルの追加などの変更は自動的に保存されないので、適当なタイミングでSaveするようにするとよい。

上から二つ目の「Add panel to empty row」を選択し、パネルを追加する。

  • Select Panel Type: table
  • Title: Tweets
  • Span: 12
  • Sort: created_at

収集されたTweet一覧がJSONフォーマットで表示されるので、左側のFieldsから表示すうフィールドとして「user.screen_name」「user.name」「text」「created_at」を選択する。

さらに、一番上の「Add panel to empty row」を選択し、パネルを追加する。

  • Select Panel Type: histogram
  • Title: Tweet Count
  • Span: 12
  • Time Field: created_at

Tweet数のヒストグラムが表示されるので、ViewからIntervalを1mなどに変更する。

ヒストグラムの左側にある緑色のボタン(Add Panel)から、さらにパネルを追加する。

  • Select Panel Type: terms
  • Title: Hashtag
  • Field: hashtag.text
  • Style: Pie
  • Missing: unchecked

同様に、Mention先ユーザについてのパネルを追加する。

  • Select Panel Type: terms
  • Title: Mention
  • Field: mention.name
  • Style: Pie
  • Missing: unchecked

Tweetしているユーザについてのパネルを追加する。

  • Select Panel Type: terms
  • Title: Tweet User
  • Field: user.name
  • Style: Pie
  • Missing: unchecked

最後に、右上のSaveボタンからDashboardを保存する。 さらにAdvancedからSave as Homeを選択すれば、このDashboardをデフォルトのDashboardに設定することができる。

Kibana Dashboard
Dashboard完成図

Dashboardを操作する

右上のTime FilterからAuto Refreshを選ぶことで、Dashboardを一定間隔で更新させることができる。 上部のQueryで日本語を使う際は、text:"日本語"といったようにダブルクォートをつける。