形態素解析API(Powered by kuromoji)

katsu-tech.hatenablog.com

前回、ユーザ辞書を含めたIPADICのjarを作成しました。そこでこのjarを使ってyahooの日本語形態素解析APIのようなものを作成したいと思います。とりあえず、仕様はこんな感じ。

・HTTP で動作する
・インプットはPOSTで送られるファイル
形態素解析した結果をJSONで返却
・返却対象は名詞のみで出現回数とのセット

サーバはいくつか候補はあると思うんですが、軽量なJettyを使用することにします。Play Frameworkでも作ったんですがセットアップ以外はそんなに変わらないのでJettyだけ説明します。HTTPメソッドをフル活用する場合はPlay Framework をオススメします。

EclipseでMevenプロジェクトを作成しpom.xmlに以下の通り追記します。
合わせて、前回作成した「kuromoji-ipadic-0.9.0.jar」をビルドパスに追加します。

<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-server</artifactId>
	<version>9.3.8.RC0</version>
</dependency>
<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.3.1</version>
</dependency>
<dependency>
	<groupId>com.atilika.kuromoji</groupId>
	<artifactId>kuromoji-core</artifactId>
	<version>0.9.0</version>
</dependency>
<dependency>
	<groupId>net.arnx</groupId>
	<artifactId>jsonic</artifactId>
	<version>1.3.0</version>
</dependency>

「commons-fileupload」は、POSTでファイルを受けるときに使うと便利なライブラリらしいので今回使用することにします。
jsonic」は、「シンプルかつ高機能なJSONエンコーダー/デコーダーライブラリ」です。
セキュリティ、エラー処理、HTTPステータスコードなど一切考慮されていないサンプルコードはこんな感じです。

public class KuromojiServer extends AbstractHandler {
	
	public void handle(String target, Request baseRequest, HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		response.setContentType("text/html;charset=utf-8");
		response.setStatus(HttpServletResponse.SC_OK);
		
		if (ServletFileUpload.isMultipartContent(request)) {

			DiskFileItemFactory factory = new DiskFileItemFactory();
			ServletFileUpload upload = new ServletFileUpload(factory);
			
			factory.setSizeThreshold(1024);
			upload.setSizeMax(-1);
			upload.setHeaderEncoding("UTF-8");
			
			try {
				List<FileItem> list = upload.parseRequest(request);
				for (FileItem item : list) {
					if (!item.isFormField()) {
						String text = new String(item.get(), "UTF-8");
						MorphologicalAnalysis service = new MorphologicalAnalysis();
						Map<String, Integer> result = service.getCount(service.getNoun(service.tokenize(text)));
						System.out.println(JSON.encode(result));
						response.getWriter().println(JSON.encode(result));
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		baseRequest.setHandled(true);
	}

	public static void main(String[] args) throws Exception {
		Server server = new Server(9001);
		server.setHandler(new KuromojiServer());

		server.start();
		server.join();
	}

}

ロジックはこんな感じ。

public class MorphologicalAnalysis {

	public static Tokenizer tokenizer = new Tokenizer();

	public List<Token> tokenize(String text) {
		return tokenizer.tokenize(text);
	}

	public List<Token> getNoun(List<Token> tokens) {
		List<Token> result = new ArrayList<>();
		for (Token token : tokens) {
			List<String> features = Arrays.asList(token.getAllFeaturesArray());
			if (features.get(0).equals("名詞")
					&& (features.get(1).equals("一般") || features.get(1).equals("固有名詞"))) {
				result.add(token);
			}
		}

		return result;
	}

	public Map<String, Integer> getCount(List<Token> tokens) {
		Map<String, Integer> result = new HashMap<>();
		for (Token token : tokens) {
			if (result.containsKey(token.getSurface())) {
				result.put(token.getSurface(), result.get(token.getSurface()) + 1);
			} else {
				result.put(token.getSurface(), 1);
			}
		}
		
		return result;
	}
}

KuromojiServerでJava実行し「http://localhost:9001」でアクセスできれば成功です。
POSTなのでHTMLから呼び出します。
呼び出し元はこんな感じ。

<html>
<head>
</head>
<body>
	<form action="http://localhost:9001/" method="post" enctype="multipart/form-data">
		<input type="file" name="upfile">
		<input type="submit" value="送信">
	</form>
</body>
</html>

実行可能Jarファイルとしてjarにして以下の記事のようにしてあげればサービスのようにHTTP経由で利用することができます。ただし環境によってはヒープサイズでエラーが発生するかもしれませんので適宜変更してください。(僕の環境では、-Xms300m -Xmx300m で動作確認済みです。)

katsu-tech.hatenablog.com

Apache経由にする場合は、httpd.confに以下の内容を追記して再起動します。

<Location /kuromoji/>
    ProxyPass http://localhost:9001/
</Location>

※Location のパスは任意です。今回は「/kuromoji/」としました。
HTTPS化していればセキュアに利用できます。