Fluentd+ElasticSearch+Kibanaでアイドルデータ分析基盤を作ってみたの回で、FluentdとTwitter Streaming APIを使ってS3にツイートデータを保存したので、このデータをHadoopを使って解析してみます。
今回はMeCabを使って形態素解析してワードカウントを取るような教科書的なMapReduceを試してみました。Hadoop Streamingを使ってPythonでMapper、Reducerを書いていきます。
環境
- OS: Mac OS X(El Capitan)
- Python: 2.7.11
- Hadoop: 2.7.1
Hadoopのインストール&設定
以下のURLを参考にインストール&設定すればOK。Macなら brew install hadoopで一発。インストールしたら、/.bashrcか/.zshrcにこんな感じで設定しておきます。環境変数HADOOP_CLASSPATHに$HADOOP_HOME/libexec/share/hadoop/tools/lib/*を入れないと"ls: No FileSystem for scheme: s3n hadoop"とエラーが発生するので注意。
export HADOOP_VER=2.7.1
export HADOOP_HOME=/usr/local/Cellar/hadoop/$HADOOP_VER
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:"$HADOOP_HOME/libexec/share/hadoop/tools/lib/*"
Macで擬似分散モードを試すときは[システム環境設定]>[共有]でリモートログインを許可しておく。
Hadoop StreamingのMapper、Reducer
Mapperは対象のデータを標準入力で受け取り、Key/Valueを標準出力に吐き出すようなコード、ReducerはMapperの標準出力を入力としてKey/Valueを集計するようなコードをそれぞれ書けばOKです。今回はMapperでMeCabを使って形態素解析をするため、MeCabのインストールを行います。MeCabのダウンロードはこちら。$ tar xvf mecab-0.996.tar.gz
$ cd mecab-0.996
$ ./configure
$ make
$ sudo make install
辞書もインストール
$ tar xvf mecab-ipadic-2.7.0-20070801.tar.gz
$ cd mecab-ipadic-2.7.0-20070801
$ ./configure --with-charset=utf8
$ make
$ sudo make install
Pythonバインディングのインストール
$ tar xvf mecab-python-0.996.tar.gz
$ cd mecab-python-0.996
$ python setup.py build
$ sudo python setup.py install
ユーザ辞書の元CSVデータ作成
乃木坂,1288,1288,*,名詞,固有名詞,*,*,*,*,乃木坂,ノギザカ,ノギザカ
欅坂,1288,1288,*,名詞,固有名詞,*,*,*,*,欅坂,ケヤキザカ,ケヤキザカ
...
ユーザ辞書を作成
$ /usr/local/libexec/mecab/mecab-dict-index -d/usr/local/lib/mecab/dic/ipadic \
-u /path/to/file/user.dic -f utf-8 -t utf-8 /path/to/file/user_dic.csv
MeCabにユーザ辞書を設定(/usr/local/lib/mecab/dic/ipadic/dicrc)
userdic = /path/to/file/user.dic
Mapperの実装
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import json
import MeCab
import re
regex = re.compile(r'[ぁ-ん。、!?!-/:-@≠\[-`{-~a-zA-Z\d_]+')
mt = MeCab.Tagger("-Ochasen")
for line in sys.stdin:
obj = json.loads(line)
text = obj['text']
# RTを含むツイートは除く
if text.find('RT') > -1:
continue
m = mt.parseToNode(text.encode('utf-8'))
while m:
# 一文字、空白な品詞は無視。正規表現によるフィルタリング内容は要検討。
if m.surface == '' or len(m.surface) == 1 or regex.match(m.surface):
m = m.next
continue
print("{0}\t1".format(m.surface))
m = m.next
Reducerの実装
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
word_map = {}
for line in sys.stdin:
word, count = line.strip().split('\t')
word_map[word] = word_map[word] + int(count) if word in word_map else int(count)
for k, v in sorted(word_map.items(), key=lambda x:x[1], reverse=True):
print("{0}\t{1}".format(k, v))
Mapperでツイート内容を形態素解析かけるときは、encodeしないと以下のようなエラーが発生するので注意
Traceback (most recent call last):
File "/path/to/hadoop_mapper.py", line 12, in <module>
m = mt.parseToNode(obj['text'])
File "/usr/local/lib/python2.7/site-packages/MeCab.py", line 282, in parseToNode
def parseToNode(self, *args): return _MeCab.Tagger_parseToNode(self, *args)
TypeError: in method 'Tagger_parseToNode', argument 2 of type 'char const *'
S3のデータを利用する設定
core-site.xmlの設定はこんな感じで↓
<configuration>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
<property>
<name>fs.s3n.awsAccessKeyId</name>
<value>{AWS_ACCESS_KEY_ID}</value>
</property>
<property>
<name>fs.s3n.awsSecretAccessKey</name>
<value>{AWS_SECRET_ACCESS_KEY}</value>
</property>
</configuration>
以下のコマンドを実行して問題なくS3にアクセスできることを確認します。
$ hadoop fs -ls s3n://{BUCKET_NAME}
Hadoopの起動
$ hadoop jar ${HADOOP_HOME}/libexec/share/hadoop/tools/lib/hadoop-streaming-2.7.1.jar \
-input s3n://{BUCKET_NAME}/20160319_*_twitter.gz \
-output /output -mapper ./hadoop_mapper.py -reducer ./hadoop_reducer.py
結果の確認は以下のコマンドで
$ hadoop fs -cat /output/part-00000 | less
3/19のツイート収集結果はこんな感じ
乃木坂:2565
西野七瀬:532
白石麻衣:436
欅坂:426
橋本奈々未:369
生駒里奈:356
希望:327
齋藤飛鳥:302
永島聖羅:286
生田絵梨花:274
高山一実:257
生:251
写真:241
紅白:239
衣装:237
提供:236
松村沙友理:213
人:210
日:187
衛藤美彩:180
中:167
北野日奈子:150
深川麻衣:147
正規表現のフィルタリングをキツ目に設定しているのでメンバーが良い感じに出力できています。卒業コンサートがあった永島聖羅が高め。アンダーでは北野日奈子強いです。