Twitterから取得した「つぶやき」を品詞に分解する
前回のログで、TwitterからTweetとアカウント名称を取得した。
「エコノミスト」誌(6/4号)の特集記事のように、Tweetに含まれる語句のワードカウントをとるには、先のデータを品詞分解(形態素解析)しなければならない。
ここでは、形態素解析エンジンとして(以前に使ったことがある)Mecab、MecabをJavaプログラムから利用するために、そのJavaバインディングをつかう。
開発はEclipse Juno。mecabはMacBook Pro(OSX Lion)にインストールしたが、Linuxでも基本的には同じでよい。
以下のサイトを参考にさせてもらった。
Qiita: MacにMecabをインストールする (2013.3)
superdry memorandum :-D:MecabのJavaバインディング
Macにmecabをインストールする
mecabは本家サイトからmecab-0.996.tarをダウンロード、解凍する。
Makeをする必要があるので、必要に応じて、Xcode Command Line Toolとautoconf, automakeをインストールする。
解凍したディレクトリに移動し、以下でインストールする。
./configure make sudo make install
つぎに、Mecab-ipadicをインストールする。
これもダウンロードサイトからmecab-ipadic-2.7.0-20070801.tarをダウンロードして、解凍する。
以下でインストールする。
./configure --with-charset=utf8 make sudo make install
Javaバインディングのインストール
MecabのBindingのページをみると、SwigというC, C++でかかれたプログラム(今回の場合、Mecab)と、Web系言語のブリッジをするソフトウェアを使っているようだ。
SWIGの Supported languageをみると、Perl, Python, Ruby, Javaなどがサポートされている。
本家サイトから、mecab-java-0.996.tarをダウンロードし、解凍する。
参考サイトにしたがって、MakeFileを以下のように修正し、解凍してできたディレクトリに移動してsudo makeする。
TARGET=MeCab JAVAC=javac JAVA=java JAR=jar CXX=c++ INCLUDE=/System/Library/Frameworks/JavaVM.framework/Headers PACKAGE=org/chasen/mecab LIBS=`mecab-config --libs` INC=`mecab-config --cflags` -I$(INCLUDE) -I$(INCLUDE)/linux all: $(CXX) -O3 -c -fpic $(TARGET)_wrap.cxx $(INC) $(CXX) -shared $(TARGET)_wrap.o -o lib$(TARGET).so $(LIBS) $(JAVAC) $(PACKAGE)/*.java -J-Dfile.encoding=UTF8 $(JAVAC) test.java -J-Dfile.encoding=UTF8 $(JAR) cfv $(TARGET).jar $(PACKAGE)/*.class -J-Dfile.encoding=UTF8: test: env LD_LIBRARY_PATH=. $(JAVA) test clean: rm -fr *.jar *.o *.so *.class $(PACKAGE)/*.class cleanall: rm -fr $(TARGET).java *.cxx
Javaプログラム
Eclipse(Juno)にJavaプロジェクトを作成する。ここでは、mecabsampleとした。デフォルトパッケージにtest.javaを入れ、以下の用に修正する。ここでも、参考サイトを参考にさせていただいた。
このサンプルは、「太郎は二郎にこの本を渡した。」を3通りの方法で品詞分解するものとなっている。
import java.io.File; import org.chasen.mecab.MeCab; import org.chasen.mecab.Tagger; import org.chasen.mecab.Model; import org.chasen.mecab.Lattice; import org.chasen.mecab.Node; public class test { static { try { File f = new File("/Users/tetsuya/mecab-java/libMeCab.so"); System.load(f.toString()); } catch (UnsatisfiedLinkError e) { System.err.println("Cannot load the example native code.\nMake sure your LD_LIBRARY_PATH contains \'.\'\n" + e); System.exit(1); } } public static void main(String[] argv) { System.out.println(MeCab.VERSION); Tagger tagger = new Tagger(); String str = "太郎は二郎にこの本を渡した。"; System.out.println(tagger.parse(str)); Node node = tagger.parseToNode(str); for (;node != null; node = node.getNext()) { System.out.println(node.getSurface() + "\t" + node.getFeature()); } System.out.println ("EOS\n"); Model model = new Model(); Tagger tagger2 = model.createTagger(); System.out.println (tagger2.parse(str)); Lattice lattice = model.createLattice(); System.out.println(str); lattice.set_sentence(str); if (tagger2.parse(lattice)) { System.out.println(lattice.toString()); for (node = lattice.bos_node(); node != null; node = node.getNext()) { System.out.println(node.getSurface() + "\t" + node.getFeature()); } System.out.println("EOS\n"); } lattice.add_request_type(MeCab.MECAB_NBEST); lattice.set_sentence(str); tagger2.parse(lattice); for (int i = 0; i < 10; ++i) { if (lattice.next()) { System.out.println("nbest:" + i + "\n" + lattice.toString()); } } } }
これを実行すると以下の結果がコンソールに出力される。
0.996 太郎 名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー は 助詞,係助詞,*,*,*,*,は,ハ,ワ 二郎 名詞,固有名詞,人名,名,*,*,二郎,ジロウ,ジロー に 助詞,格助詞,一般,*,*,*,に,ニ,ニ この 連体詞,*,*,*,*,*,この,コノ,コノ 本 名詞,一般,*,*,*,*,本,ホン,ホン を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 渡し 動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ 。 記号,句点,*,*,*,*,。,。,。 EOS BOS/EOS,*,*,*,*,*,*,*,* 太郎 名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー は 助詞,係助詞,*,*,*,*,は,ハ,ワ 二郎 名詞,固有名詞,人名,名,*,*,二郎,ジロウ,ジロー に 助詞,格助詞,一般,*,*,*,に,ニ,ニ この 連体詞,*,*,*,*,*,この,コノ,コノ 本 名詞,一般,*,*,*,*,本,ホン,ホン を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 渡し 動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ 。 記号,句点,*,*,*,*,。,。,。 BOS/EOS,*,*,*,*,*,*,*,* EOS 太郎 名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー は 助詞,係助詞,*,*,*,*,は,ハ,ワ 二郎 名詞,固有名詞,人名,名,*,*,二郎,ジロウ,ジロー に 助詞,格助詞,一般,*,*,*,に,ニ,ニ この 連体詞,*,*,*,*,*,この,コノ,コノ 本 名詞,一般,*,*,*,*,本,ホン,ホン を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 渡し 動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ 。 記号,句点,*,*,*,*,。,。,。 EOS 太郎は二郎にこの本を渡した。 太郎 名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー は 助詞,係助詞,*,*,*,*,は,ハ,ワ 二郎 名詞,固有名詞,人名,名,*,*,二郎,ジロウ,ジロー に 助詞,格助詞,一般,*,*,*,に,ニ,ニ この 連体詞,*,*,*,*,*,この,コノ,コノ 本 名詞,一般,*,*,*,*,本,ホン,ホン ........
取得したTweetを品詞分解するJavaプログラム
前回のログで、Twitter API経由で取得したTweetとアカウント名をテキストファイルに書き出した(tweets.txt)。
これを読み込んでMecabで品詞分解し、「名詞」のみを別のファイル(tweets_noun.txt)に書き出すプログラムを作成する。
HadoopのMapReduceを使ってワードカウントを取りたいので、1名詞で1行出力する。
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.StringTokenizer; import org.chasen.mecab.MeCab; import org.chasen.mecab.Tagger; import org.chasen.mecab.Node; public class TestTweet { static { try { File f = new File("/Users/tetsuya/mecab-java/libMeCab.so"); System.load(f.toString()); } catch (UnsatisfiedLinkError e) { System.err.println("Cannot load the example native code.\nMake sure your LD_LIBRARY_PATH contains \'.\'\n" + e); System.exit(1); } } public static void main(String[] argv) { System.out.println(MeCab.VERSION); try { // 読み込み用 File file = new File("[path to the file]/tweets.txt"); BufferedReader br; br = new BufferedReader(new FileReader(file)); // 書き出し用 File ofile = new File("[path to the file]/tweets_noun.txt"); PrintWriter pw; // 追記で書き込む //pw = new PrintWriter(new BufferedWriter(new FileWriter(ofile,true))); pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(ofile,true),"UTF-8"))); // 読み込み String str = br.readLine(); while(str != null){ Tagger tagger = new Tagger(); Node node = tagger.parseToNode(str); for (;node != null; node = node.getNext()) { String sf = node.getSurface(); String ft = node.getFeature(); // ハッシュタグとURLの削除 StringTokenizer sta = new StringTokenizer(ft, ","); //トークンの出力 while(sta.hasMoreTokens()) { if(sta.nextToken().equals("名詞")){ pw.println(sf); //System.out.println(sf + "\t" + ft); } } } str = br.readLine(); } pw.close(); br.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println ("EOS\n"); } }