Mecabを用いたwebページの形態素解析(大学課題）

大学の課題で日本語の形態素解析をしている。
Java大好きな自分にJavaの課題がでたので喜んでコーディングしてみた。
まずMecab：形態素解析をしてくれるプログラム　を[ダウンロード](http://mecab.googlecode.com/svn/trunk/mecab/doc/index.html)。
続いて解析用のJavaクラス：Morpheme 

```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クラスをつくる。

```java: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);
    	}
    }
}

```

```java:URLsplitter
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タグから中身を出してくれます。すごいですね、文明の利器って :heart_eyes: 
最初、これを、自分でやろうと（正規表現とStringクラスのメソッド`replaceAll()`とか使いながら）思いましたが、却下！！文明の利器に甘えることにしました。

実行結果：解析したURL：[http://ameblo.jp/wakeupgirls/](http://ameblo.jp/wakeupgirls/)

```java:解析結果
[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
１: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の宣伝でした。