LoginSignup
4
4

More than 5 years have passed since last update.

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

Posted at

大学の課題で日本語の形態素解析をしている。
Java大好きな自分にJavaの課題がでたので喜んでコーディングしてみた。
まずMecab:形態素解析をしてくれるプログラム をダウンロード
続いて解析用のJavaクラス:Morpheme

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

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);
        }
    }
}

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/

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

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4