フロントエンドだけで可能!?Chrome拡張に形態素解析を組み込む

# はじめに
Chrome拡張を作っている時にユーザが入力する任意の短文を適切に品詞分解したくなりました。

例えば「すもももももももものうち」という有名な短文があるがこれを品詞に分解すると
「すもも」「もも」などの名詞や「も」という助詞に分けることができます。

![fruit_soldum.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540446/6d280d8b-db94-cb6a-a0e1-e8c278d15189.png)


これを機械的に実現する方法として「形態素解析」が広く用いられていますが、Chrome拡張で実現するためにフロントだけでどうにかする方法は無いかと探していたらkuromoji.jsというライブラリを見つけました。

kuromoji.js
https://github.com/takuyaa/kuromoji.js/

kuromoji
https://www.atilika.com/ja/kuromoji/


今回は、Chrome拡張への組み込みに少し苦労したので
誰でも簡単に組み込んでデモできるようになるまでの流れを作りたいと思います。


# 準備
Chrome拡張を作るためのベースの知識がある前提で話しますが、手順にそうと誰でも拡張機能が作れます。
またストアにアップロードしなくてもローカルで試せますのでそのような手順になってます。

## ディレクトリ

下記の構成で準備します

```
extension
    ├── manifest.json
    └── script.js
```

## ベースファイル
``` manifest.js
{
  "manifest_version": 2,
  "name": "拡張機能の名前",
  "description": "拡張機能の説明",
  "version": "0.0.1",
  "permissions": [
    "*://example.com/*"
  ],
  "content_scripts": [
    {
      "matches": ["*://example.com/*"],
      "js": [
        "kuromoji/build/kuromoji.js",
        "script.js"
      ]
    }
  ],
  "web_accessible_resources": ["/kuromoji/dict/*"]
}
```
マニフェストファイルは拡張の構成を定義するファイルです。
- manifest_version (定義ファイルの形式のバージョン)
- name (拡張の名前)
- description (拡張の説明)
- version (拡張のバージョン)
- permissions (拡張機能の実行のために求める権限、ブラウザの機能や特定のページに対して行う)
- content_scripts (WEBページで実行するJavaScriptの指定、特定のページに対して実行することもできる)
- web_accessible_resources (拡張機能がアクセスできるアセットの指定)

設定のポイントは
- https://example.com というサイトで実行するスクリプトを想定
- script.jsにメインの処理
- 形態素解析のために必要な辞書をweb_accessible_resourcesで許可する
ところです




``` script.js
// 辞書の場所を指定 extensionAPIを利用して相対的にパスを指定する
const dicPath = chrome.extension.getURL("/kuromoji/dict/")

kuromoji.builder({ dicPath: dicPath }).build(function (err, tokenizer) {
    // tokenizer is ready
    var path = tokenizer.tokenize("すもももももももものうち");
    console.log(path);
});
```
メインの処理はここに記載します。
kuroomoji.jsのサンプル通りに特定の文書を解析して結果をコンソールログで確認したいと思います。


## kuromojijsの導入
拡張のディレクトリの中にとりあえずcloneしてきます。
そのままだとディレクトリ名に「.」が入るので「kuromoji」と改めて定義しています。

```
cd extension
git clone https://github.com/takuyaa/kuromoji.js.git kuromoji
```

現状のファイル構成はこうなります。
manifestやscriptを記述する際にkuromoji以下のディレクトリがある事を前提に記述していたので、疑問に思ったと思う箇所もあると思いますが見比べてみるとわかると思います。

```
extension
    ├── kuromoji
    │   ├── CHANGELOG.md
    │   ├── LICENSE-2.0.txt
    │   ├── NOTICE.md
    │   ├── README.md
    │   ├── bower.json
    │   ├── build
    │   ├── demo
    │   ├── dict
    │   ├── example
    │   ├── gulpfile.js
    │   ├── jsdoc.json
    │   ├── package-lock.json
    │   ├── package.json
    │   ├── src
    │   └── test
    ├── manifest.json
    └── script.js
```

## 拡張機能をChromeに

拡張機能はストアに公開しなくてもローカルファイルを読み込むことで動作の確認ができます

Chromeブラウザで `chrome://extensions/` を開きます。

「パッケージ化されていない拡張機能を読み込む」ボタンをクリックして、先程作成した「extension」ディレクトリを選択します

この時、問題がある場合はパッケージが読み込めずにエラー文が表示されます。
エラー内容を読むと例えばmanifest.jsonの記述に問題があるなどがわかるので該当箇所の作成手順に問題ないか見直します。

晴れて読み込まれたら

https://example.com を開きます

Developer toolを開いてコンソールに形態素解析されたワードが配列で表示されるはずです。

```
(7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {word_id: 415760, word_type: "KNOWN", word_position: 1, surface_form: "すもも", pos: "名詞", …}
1: {word_id: 93220, word_type: "KNOWN", word_position: 4, surface_form: "も", pos: "助詞", …}
2: {word_id: 1614710, word_type: "KNOWN", word_position: 5, surface_form: "もも", pos: "名詞", …}
3: {word_id: 93220, word_type: "KNOWN", word_position: 7, surface_form: "も", pos: "助詞", …}
4: {word_id: 1614710, word_type: "KNOWN", word_position: 8, surface_form: "もも", pos: "名詞", …}
5: {word_id: 93100, word_type: "KNOWN", word_position: 10, surface_form: "の", pos: "助詞", …}
6: {word_id: 62510, word_type: "KNOWN", word_position: 11, surface_form: "うち", pos: "名詞", …}
length: 7
__proto__: Array(0)
```

以上で形態素解析を用いたChrome拡張のベースができあがりました。

Chrome拡張からサーバーと通信すれば形態素解析ももっと自由度が高く実現できるとは思いますが
ブラウザのリソースだけで処理が完結する事は理想的だと思います。

今回はここまでですが
- WEBページから抽出した文字を解析する (名詞のみを取り出すなど)
- 文字にふりがな、よみがなを振る
などの展望が見れるので活用できたらいいなと思っています。