Node.js+MeCab+StreamingAPIでTwitter実況を解析する

# 目的
　[前回記事](http://qiita.com/Suibari_cha/items/2589004e85999c6c5ed1)で、「Twitterのリアルタイムな形態素解析結果集計」を目標として挙げました。この途中経過について報告します。

# 結論
　**Twitter野球実況を形態素解析(品詞毎に分解)し、チーム毎に集計できました。**
　動作は以下の通り。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">形態素解析。 <a href="https://t.co/H65mETOz0W">pic.twitter.com/H65mETOz0W</a></p>&mdash; すいばり@&#39;16年度9試合2勝2敗 (@Suibari_cha) <a href="https://twitter.com/Suibari_cha/status/833282868017192960">2017年2月19日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

# 内容
　処理は以下の流れです。

1. TwitterStreamingAPIから**野球関連ツイートを拾う**
2. ツイートに**どのチームのワードが含まれているか**判定
3. **形態素解析**を行い、2で判定したチーム(のオブジェクト)に**結果を加算**
4. 以降、1の度に2～3繰り返し

## コード

```js
// 形態素解析クロージャ
function analyzeString() {
  const INDEX_WORD     = 0;
  const INDEX_WORDTYPE = 1;
  var result = {};
  for (key in q_json) result[key] = {};
  
  return function(text, cheer_teams) {
    mcb.parse(text, function(err, arr_words){
      if (err) throw err;
      arr_words.forEach(function(val, idx, arr){
        if (val[INDEX_WORDTYPE] == "名詞" ||
            val[INDEX_WORDTYPE] == "固有名詞") {
          var word = val[INDEX_WORD];
          if (checkJa(word)) {
            for (var i = 0; i < cheer_teams.length; i++) {
              // resultオブジェクトにその言葉があるか？
              if (result[cheer_teams[i]][word] != undefined) {
                // 言葉があるので加算する
                result[cheer_teams[i]][word] += 1;
              } else {
                // 言葉がないので追記
                result[cheer_teams[i]][word]  = 1;
              }
            }
          }
        }
      });
      var arr = [];
      arr_words.forEach( function(value, index, array){
        arr.push(value[0]);
      });
      console.log("words: " + arr);
      console.log('elements: ' + arr_words.length + ' words');
      console.log(result);
    });
  }
  
  function checkJa(str) {
    var isJapanese = false;
    
    for(var i=0; i < str.length; i++){
      if(str.charCodeAt(i) >= 256) {
        return isJapanese = true;
      }
    }
  }
}
```

## MeCabの選定理由
　当初は形態素解析のモジュールにはMeCabでなくkuromojiを使うつもりでした。
　しかし、**一度の解析に0.5秒強ほど時間がかかり、要件であるTwitter実況に追従できませんでした**(メモリリークしnodeが強制終了してしまう)。
　よってMeCabの非同期解析モジュール(mecab-async)を選定しました。
　同様の現象にあわれた方の解決のために、エラーログを記載しておきます。

```vim
Feb 16 22:43:09 localhost kernel: Out of memory: Kill process 17191 (node) score 913 or sacrifice child
Feb 16 22:43:09 localhost kernel: Killed process 17191, UID 500, (node) total-vm:2266088kB, anon-rss:381672kB, file-rss:76kBFeb 16 22:43:09 localhost kernel: node: page allocation failure. order:0, mode:0x280da

```

# 展望
1. MeCabデフォルトのIPA辞書では、当然、野球選手名を適切に解析できない("大谷翔平"→*["大谷","翔","平"]*となる。*["大谷翔平"]*と解析して欲しい)。MeCabではユーザ辞書追加も可能なので、野球選手の「フルネーム+読み」「苗字+読み」の辞書ファイルをスクレイピング等して作ろうか検討中。→[導入しました](http://qiita.com/Suibari_cha/items/aeeb3fb07b94207c1ff3)
2. [前回記事](http://qiita.com/Suibari_cha/items/2589004e85999c6c5ed1)同様に、Socket.ioで集計結果をクライアントに転送して、リアルタイムに結果を図で示すのも良さそう。→[D3.jsを導入しました](http://qiita.com/Suibari_cha/items/d30d7e5068775a591859)

# 参考
1. [Qiita: "続・node.js で社会性フィルター"](http://qiita.com/albno273/items/bcaf836a213b0f8d3add) @albno273 氏
2. [GitHub: "node-mecab-async"](https://github.com/hecomi/node-mecab-async) @hecomi 氏