大学の課題で日本語の形態素解析をしている。
Java大好きな自分にJavaの課題がでたので喜んでコーディングしてみた。
まずMecab:形態素解析をしてくれるプログラム をダウンロード。
続いて解析用のJavaクラス:Morpheme
String surface; // 表層形
String posStr; // 品詞文字列
String[] pos; // 品詞階層
String conjForm; // 活用形
String conjType; // 活用型
String base; // 原形
String reading; // 読み
String pron; // 発音
String mecabLine;
static Process mecabPrc;
static PrintWriter mecabOut;
static BufferedReader mecabIn;
static String mecabCmd = "C:\\Program Files\\MeCab\\bin\\mecab.exe";
static String encoding = "EUC-JP";
public Morpheme(String line) {
mecabLine = line;
String[] arr = line.split("\t");
surface = arr[0];
String feat = arr[1];
String[] tokens = feat.split(",");
posStr = "";
pos = new String[4];
for (int i = 0; i <= 3; i++) {
pos[i] = tokens[i];
if (i > 0) {
posStr += ",";
}
posStr += tokens[i];
}
conjType = tokens[4];
conjForm = tokens[5];
base = tokens[6];
if (tokens.length > 7) {
reading = tokens[7];
pron = tokens[8];
} else {
reading = "";
pron = "";
}
}
public boolean isVerb(){
return pos[0].equals("動詞");
}
public boolean isNoun(){
return pos[0].equals("名詞");
}
public String toString() {
return "<形態素 表層=\"" + surface + "\"" +
" 品詞=\"" + posStr + "\"" +
" 活用形=\"" + conjForm + "\"" +
" 活用型=\"" + conjType + "\"" +
" 原形=\"" + base + "\"" +
" 読み=\"" + reading + "\"" +
" 発音=\"" + pron + "\" />";
}
static ArrayList<Morpheme> analyzeMorpheme(String str) {
if (mecabPrc == null) {
startMeCab();
}
mecabOut.println(str); // MeCabに文字列を送る
mecabOut.flush();
ArrayList<Morpheme> morphs = new ArrayList<Morpheme>();
try {
for (String line = mecabIn.readLine(); line != null; line = mecabIn.readLine()) {
// mecabから結果を受け取る
if (line.equals("EOS")) {
break;
} else {
morphs.add(new Morpheme(line));
}
}
} catch (IOException e) {
System.err.println("MeCabから形態素解析結果を受け取る際にIOExceptionが発生しました");
e.printStackTrace();
}
return morphs;
}
static void startMeCab() {
try {
mecabPrc = Runtime.getRuntime().exec(mecabCmd);
mecabOut = new PrintWriter(new OutputStreamWriter(mecabPrc.getOutputStream(), encoding));
mecabIn = new BufferedReader(new InputStreamReader(mecabPrc.getInputStream(), encoding));
} catch (IOException e) {
System.err.println("形態素解析器MeCabを起動できませんでした");
System.exit(-1);
}
}
public String getSurface(){
return surface;
}
public String getReading(){
return reading;
}
public String getPos(){
return pos[0];
}
public String getPos(int i){
return pos[i];
}
public String getConjugationForm(){
return conjForm;
}
public String getConjurgationType(){
return conjType;
}
public String getBaseform(){
return base;
}
public String getPronuciation(){
return pron;
}
//mainメソッドを動作確認のためだから、無くても大丈夫。
public static void main(String[] args) {
// TODO 自動生成されたメソッド・スタブ
// MeCabを起動
startMeCab();
// 文字列を形態素解析
ArrayList<Morpheme> morphs = analyzeMorpheme(""
+ "");
System.out.println(morphs);
for (int i = 0; i < morphs.size(); i++) {
// 形態素を1つずつmorphに格納するループ
Morpheme morph = morphs.get(i);
if(morph.isVerb()){
System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
// 表層の文字列を取り出すには,getSurfaceメソッド
String surface = morph.getSurface();
System.out.println("表層の文字列: " + surface);
// 品詞(第i+1階層)を取り出すには,getPos(i)
String pos1 = morph.getPos(0);
System.out.println(" 品詞第一階層: " + pos1);
String pos2 = morph.getPos(1);
System.out.println(" 品詞第二階層: " + pos2);
// 原形を取り出すには,getBaseformメソッド
String base = morph.getBaseform();
System.out.println(" 原形: " + base);
// 読みを取り出すには,getReadingメソッド
String read = morph.getReading();
System.out.println(" 読み: " + read);
}
}
※Mecabの実行ファイルのフォルダにパスを通してください。
Mecabとのやり取りをしてくれるクラス。説明しなくても読めばなんとなく分かるし、読まなくてもなんとなく分かる(?)このmainメソッドを走らせるとどういう構造か分かるので、ここからの拡張はキミ次第だ(ドヤッ
で、タイトルのwebページの形態素解析。webページに出てくる名詞の数を数える。
まずテキストの名詞を数えるNounCounterクラスをつくる。
static private HashMap<String,Integer> cnt = new HashMap<String,Integer>();
public static void main(String[] args) throws IOException{
// MeCabを起動
Morpheme.startMeCab();
System.out.print("[input URL]:");
Scanner stdIn = new Scanner(System.in);
// 文字列
String url=stdIn.nextLine();
String text = URLspliter.split(url);
// 文字列を形態素解析
ArrayList<Morpheme> morphs = Morpheme.analyzeMorpheme(text);
Iterator<Morpheme> i_m = morphs. iterator();
while(i_m.hasNext()){
Morpheme tmp = i_m.next();
if(tmp.isNoun()){
counter(tmp.surface);
}
}
Iterator<Entry<String, Integer>> i_h = cnt.entrySet().iterator();
while(i_h.hasNext()){
Entry<String, Integer> tmp = i_h.next();
System.out.println(tmp.getKey()+":"+tmp.getValue());
}
stdIn.close();
}
static void counter(String str){
if(!cnt.containsKey(str)){
cnt.put(str, 1);
}else{
cnt.put(str, cnt.get(str)+1);
}
}
}
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.*;
public class URLspliter {
static public String split(String url) throws IOException{
Document doc = Jsoup.connect(url).get();
String text = doc.body().text();
return text;
}
}
Jsoupがhtmlを引っ張ってきて、bodyタグから中身を出してくれます。すごいですね、文明の利器って
最初、これを、自分でやろうと(正規表現とStringクラスのメソッドreplaceAll()
とか使いながら)思いましたが、却下!!文明の利器に甘えることにしました。
実行結果:解析したURL:http://ameblo.jp/wakeupgirls/
[input]:http://ameblo.jp/wakeupgirls/
うま:1
?*)?:1
:≡( :3
夢:1
記事:4
メシアガレ:1
筋:1
鳥かご:1
後半:1
Girls:8
ロリ:1
ファミレス:1
メンバーブログ:3
ギフト:1
喉:1
ガチ:1
自分:3
多く:1
)/\(:1
本人:1
デビュー:2
瞬間:2
by:1
洋画:3
美:3
細胞:1
あと:4
女性:1
予告:1
お互い:1
作品:1
赤:1
センサー:1
石切:4
学校:1
報告:1
所:1
・´):1
!」:1
たん:2
ご飯:1
簡単:3
AD:2
意識:1
アサシン:1
皆さん:5
緊張:2
戦:1
のどか:1
判事:2
必見:1
アクセス:3
こないだ:1
明日:6
スペック:1
成人:1
仕事:1
初回:1
総括:2
+.(´∀`*).+:1
公園:1
テーマ:3
お金:1
みなみ:2
見た目:1
本文:1
トレ:1
菩薩:1
顔:1
時:2
Fate:1
西:1
*\(^:2
CD:2
通学:1
着目:1
期待:1
ちょ:2
くし:1
ハナヤマタ:3
的:2
感想:3
かって:1
おすすめ:2
NEW:1
こちら:3
1:2
2:1
魅力:1
布団:1
握手:5
物欲:1
情報:4
仕方:1
吹替:2
なに:1
村:1
Nanaminn:3
自己:1
よう:7
!!!!!!:1
話題:1
ページ:4
先週:1
本格:1
偽証:1
お話:1
能:1
ユニット:2
発売:2
年:1
たち:1
ぉ:1
元気:5
虎:2
方:3
舟:1
さ:1
マチアソビ:1
画像:2
何:2
そ:1
体:2
す:1
ミュータント・ワールド:1
オタク:1
必須:1
商品:1
ミリオンアーサー:1
盛りだくさん:1
映画:1
花:1
ピザ:1
大学:1
限定:1
涙:1
去年:1
ピグ:2
FIVE:1
勉強:2
ワグナー:1
ノリノリ:1
>>:2
全員:2
...
まぁこんなもんじゃないでしょうか。顔文字とかが変な感じになってたり、括弧とかがトークンとしてじゃなくて、単語として認識されてるのは不思議ですね。URLとか現れると残念な感じになるかも。。。
これでWUGちゃんがどんなことを書いてるのかすぐ分かりますね!!
最高だ!!!WGUちゃん!!!!
以上ただのWUGの宣伝でした。