沖縄の地名が難しすぎる問題をLINE BOTで解決した（い） #linebrain #ood2019 （過去記事）


> **※別メディアで2019.12.17に公開した記事の移植です。**

こんにちは、[のびすけ](https://twitter.com/n0bisuke)です。

[沖縄オープンデイズ2019(#ood2019)のハッカソン](https://togetter.com/li/1442347)で、バス路線検索のアプリケーションを作ろうとしていたチームがありました。

沖縄を観光中の観光客が、`バス停の名前を入力すると路線検索をしてくれる`といったアプリケーションを作ろうとしていたチームがあったのですが、 **それ以前の課題**(後述)がありそうだなぁと思ったので、ちょっと僕なりにアプローチしてみました。

> 実はハッカソン中に作ってはいたのですが、なかなかまとめられず今に至る…… 
> 遅れてすみません。([Node.jsアドベントカレンダー](https://qiita.com/advent-calendar/2019/nodejs)の皆様)

## 沖縄の地名、読めない。

> ![スクリーンショット 2019-12-16 21.38.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/f5b4e85b-2db1-4529-d421-b468339e5475.png "スクリーンショット 2019-12-16 21.38.55.png")

**「斎場御嶽」**。皆さん読めますか? 僕には読めませんでした。てか読み方聞いて衝撃でした。

他にも`勢理客`や`保栄茂`なども手強いです。

そうなんです、 **バス停を発見してバス路線を調べようとしても、そもそも読み方が分からない問題**があります。

## 町で見かけたとき、調べるのに時間がかかりすぎる。

元のテキストがあってコピペできる状況なら、ググればすぐ読み方は分かりますよね。

ただ、バス停や駅などの外にいる状況でこれらの字面を見たとして、パッと検索出来るでしょうか？

「斎場御嶽」の場合、僕だったら

1. 「さいとう」と入力
2. 「斎藤」に変換
3. 藤の字を削除して「斎」だけ残す
4. 次の文字も1~3を繰り返して作る
5. 1~4を繰り返して最終的にググって読み仮名を知る

みたいな感じになるかなと思いました。

これは **バス停や駅などの一刻を争う場面ではかなりのタイムロス**なのでは無いでしょうか？

ということで、この問題を解決していきたいなと思います。

## LINE BRAIN OCRで何とかならないか

ちょうど[LINE BRAIN OCR](https://www.linebrain.ai/ocr/)のAPIがベータリリースされていたのを思い出したので使えないか試してみました。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/35f8d9c0-c538-c352-d659-f04179fb8a63.png" width="500px">

DEMOページでとりあえず手書きの「斎場御嶽」を読み込ませたら、けっこう汚い字でもそれっぽく読んでくれました。

> <img src="https://i.gyazo.com/390be76bde0152e0713b894fa2676184.jpg" width="500px">
> https://www.instagram.com/p/B57Yq7ijJg2/

これで、 **街で見かけた地名などの写真を投げるとテキストで返してくれるLINE BOTが作れそう**ですね。

## OCR APIをNode.jsから扱ってみる

ということで実装です。LINE BRAIN OCR  APIをNode.jsから触ってみました。一応、ここだけを切り出してQiitaにもメモしてあります。

現時点だと[LINE DEV DAY 2019](https://linedevday.linecorp.com/jp/2019/)に参加して登録したがドキュメントにアクセスできる模様です。
(もしかしたらそれ以外のルートでアクセスできる人もいるかもしれませんが)

> 参考: [Node.jsでLINE BRAIN OCR APIを使う](https://qiita.com/n0bisuke/items/00d646685b1cb468e26a)

LINE OCRのサンプルリクエスト（ドキュメントは一般公開されてないっぽい）に以下のようなコードがあり、これをNode.jsで再現してみました。

```bash
curl -X POST https://ocr-devday19.linebrain.ai/v1/detection \
     -H 'X-ClovaOCR-Service-ID: PMqTDgBsucfsyvi7pJEsbIxMIUeNQWDg' \
     -H "Content-Type: multipart/form-data" \
     -F "image=@./sample.jpg" \
     -F scaling=false
```

axiosでのヘッダーの指定方法で[form-data](https://www.npmjs.com/package/form-data)が提供してくれる、`form.getHeaders()`を使う必要がある点で若干ハマったのでNode.jsからmultipart/form-dataで画像を送りたい人は気をつけてください。

> 参考: [Node.jsから画像をmultipart/form-dataでPOSTするメモ (axios利用)](https://qiita.com/n0bisuke/items/1d1cfc330997f93e2ee8)

```js
'use strict';

const fs = require('fs');
const FormData = require('form-data');
const axios = require('axios');

const url = `https://hogehoge.com/hogehoge`; //ポスト先のエンドポイントURL
const imagePath = `./public/image.png`; //画像のパス
const file = fs.createReadStream(imagePath);

const form = new FormData();
form.append('image', file);

const config = {
    headers: {
        'X-HOGEHOGE-HEADER': 'xxxxxxx', //APIごとのヘッダーなど
        ...form.getHeaders(),
    }
}

axios.post(url, form, config)
    .then(res => console.log(res.data)) //成功時
    .catch(err => console.log(err)); //失敗時
```

## LINE OCRのNode.js向け非公式SDK

ここまでの実装内容などをまとめて、すごく雑な非公式SDKを作ってみました。

> 参考: [@n0bisuke/lineocr](https://www.npmjs.com/package/@n0bisuke/lineocr)

使い方も中身もシンプルです。

```bash
$ npm i @n0bisuke/lineocr
```

```js
'use strict';
 
const Brain = require('@n0bisuke/lineocr');
const ocr = new Brain(`サービスID`); 
 
const IMAGE_PATH = `./sample.png`; // 画像パス
 
//RECOGNITION - 文字認識のみを行います。もしくは、文字領域の検出と認識を順に行います。
const options = {
    entrance: 'detection', //optional: Default value: recognition
};
ocr.recognition(IMAGE_PATH, options)
    .then(res => console.log(res.data)) //成功時
    .catch(err => console.log(err.response)); //失敗時
```

こんな雰囲気で使えるので、試せる方（β版にアクセス出来る方は限られてそうですが）は使ってみてください。

## 斎場御嶽というテキストは解釈されたが……

テキストは解釈されました。 **ただ、読み方は分からない**ですよね。

このテキストをGoogleに投げるくらいでもだいぶ良さそうですが、せっかくなら読み方も教えてくれるBOTを作ってみたいなと思い、APIやWebサイトに投げて回答を得るチャレンジです。

### ちなみに読み方答え合わせ

だいぶ読み方を引っ張ってしまいましたが、それぞれ`斎場御嶽（せいふぁーうたき）、勢理客（じっちゃく）、保栄茂（びん）`と読みます。

初見だとほぼ無理ゲーですね笑

脱線しますが、他にも調べてて出てきて気になったのは`金武（きん）`とかは武の字の読みどこに消えたんだ感ありましたね（笑）

うちのスクールの1階に[北谷食堂（ちゃたん食堂）](https://r.gnavi.co.jp/b051005/)っていう居酒屋が入ってて良く授業終わりに行くんですけど、いつも北谷（ちゃたん）って何だ？って思ってた謎が解けました。
沖縄の地名だったんですね（笑）読めない訳ですね。

……とまぁ。脱線しましたが、この読み方や地名の解釈は出来るのでしょうか。

### 1人目の挑戦者: Gooラボ ひらがな化 API

> ![スクリーンショット 2019-12-17 12.51.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/d4a5a48e-65ac-fc10-1e00-d8b86ba08e09.png "スクリーンショット 2019-12-17 12.51.46.png")
> https://labs.goo.ne.jp/api/jp/hiragana-translation/
 
Gooが提供しているAPIです。 漢字を入れるとひらがな化して返してくれます。

さて彼は沖縄の地名を読めるのでしょうか……




> ![スクリーンショット 2019-12-17 12.53.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/4528dce4-c94a-457a-5862-d328a27b7257.png "スクリーンショット 2019-12-17 12.53.56.png")

**斎場御嶽=さいじょうみたけ** まぁ確かにそうなのかもしれないですが……

> ![スクリーンショット 2019-12-17 12.52.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/4997ec47-48cb-fcb5-8fb5-a832a9819eab.png "スクリーンショット 2019-12-17 12.52.40.png")

**勢理客=せりきゃく** まぁ確かに……

沖縄の地名の読みにGooラボは勝てませんでした。

### 2人目の挑戦者: Wikipedia （MediaWiki API）

有名な地名であればWikipediaにも載っているということで、この箇所を抜き出してみます。

> 参考: [MediaWiki APIを使ってWikipediaの情報を取得](https://qiita.com/yubessy/items/16d2a074be84ee67c01f)

Wikipediaのページにはだいたいひらながの読み方が書いてあるので、この箇所を抜き出します。

> ![スクリーンショット 2019-12-17 13.56.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/b80081bc-3f3a-a8af-f2ca-31d3fbc9a14c.png "スクリーンショット 2019-12-17 13.56.06.png")

WikipediaのAPIにリクエストを投げて帰ってきた本文から正規表現で抜き出します。

```js
'use strict';

const axios = require('axios');
// const KEYWORD = `斎場御嶽`;
const KEYWORD = `豊見城`;
// const KEYWORD = `勢理客`;
// const KEYWORD = `保栄茂`;
const WIKIPEDIA_API_URL = `http://ja.wikipedia.org/w/api.php?format=json&action=query&prop=revisions&rvprop=content&titles=${encodeURIComponent(KEYWORD)}`

axios.get(WIKIPEDIA_API_URL)
    .then(res => {
        const pages = res.data.query.pages;
        const page_id = Object.keys(pages)[0]; //Wikipedia PageID
        const page_content = pages[page_id].revisions; //Wikipedia 
        const body = page_content[0]['*'];
        // console.log(body);
        const regexp = new RegExp(`'''${KEYWORD}'''（(.*?)）は`);
        const yomigana_all = body.match(regexp)[1]; //  みぐすく・とみしろ など
        const yomigana = yomigana_all.match(/[ぁ-ゞー]+/)[0]; //ひらがな以外が出てきたら区切り文字として最初の読みを返す
        console.log(yomigana)
    }) //成功時
    .catch(err => console.log(err)); //失敗時
```

```bash
$ node wikipedia.js
とみぐすく
```

結果はこんな感じで、 **豊見城=とみぐすく**、 **斎場御嶽=せいふぁーうたき**などいい感じに読み仮名を取得できました。

ただ、当たり前なのですが、 **Wikipediaにページが存在しない地名には対応できず、今回試した中だと保栄茂の読み方は分かりません**でした。

> ![スクリーンショット 2019-12-17 14.01.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/439969a3-bc40-a09a-485e-e6b6ce9c6736.png "スクリーンショット 2019-12-17 14.01.03.png")

### 3人目の挑戦者: Weblio

Weblioは優秀で、結構多くの地名に対応してくれています。

例えば斎場御嶽だと[こんな感じ](https://www.weblio.jp/content/%E6%96%8E%E5%A0%B4%E5%BE%A1%E5%B6%BD)のページになります。

> ![スクリーンショット 2019-12-17 14.07.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/2e168ff8-be64-2fe8-5e3f-65fb2c1283ec.png "スクリーンショット 2019-12-17 14.07.19.png")

WeblioはAPIが見当たらなかったのでスクレイピングしてみます。

```js
'use strict';

const axios = require('axios');
// const KEYWORD = `斎場御獄`;
// const KEYWORD = `斎場御嶽`;
// const KEYWORD = `豊見城`;
// const KEYWORD = `勢理客`;
// const KEYWORD = `保栄茂`;
const KEYWORD = `世富慶`;

const WEBLIO_URL = `https://www.weblio.jp/content/${encodeURIComponent(KEYWORD)}`

axios.get(WEBLIO_URL)
    .then(res => {
        const pages = res.data;
        let body = pages.match(/<title>(.*?)<\/title>/)[1];
        let yomigana = (body.match(/\((.*?)\)/)) ? body.match(/\((.*?)\)/)[1] : '';
        if(yomigana === ''){
            body = pages.match(/name="description" content="(.*?)"/)[1];
            yomigana = body.match(/ふりがな：(.*?)種別/)[1];            
        }
        console.log(body);
        console.log(`[${yomigana}]`);
    }) //成功時
    .catch(err => console.log(err)); //失敗時
```

Wikipedia同様に、情報があればうまくいきました。

世富慶で調べるとWeblioにも検索結果が存在しなく。

> ![スクリーンショット 2019-12-17 14.10.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/ce8c0fb8-b184-cfec-475b-956ea07b86de.png "スクリーンショット 2019-12-17 14.10.16.png")

世冨慶で調べると結果が出てくるという惜しい結果に。 ググると`世富慶`も`世冨慶`も両方出てきますが、Weblio内でエイリアスが貼られている訳では無さそうなので惜しいところまで来てるけど結果は読み込め無さそうです。

> ![スクリーンショット 2019-12-17 14.12.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/e4fb205c-b537-6ab2-9b7a-80aef888a204.png "スクリーンショット 2019-12-17 14.12.11.png")

## 結果出来上がったBOT

ということで結構惜しいですよね。一つのAPIやサイトの情報だけでは厳しく、複数のWebサイトなどの情報を駆使して団体戦でやっと戦えるといったところでしょうか。 __沖縄強い__。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/8a7d245f-f54c-820a-3eb9-c72b5fb88866.png" width="400px">

とりあえずは`Wikipediaに投げる -> Weblioに投げる -> Gooに投げる`みたいな順番で処理を書いてBOTにしてみています。

ということであくまでも現時点版ですが、こんな感じの挙動になっています。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/6baa3958-b86b-ac53-46d4-66aa80a09eb6.png" width="300px">
> Wikipediaのみ

↓

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/e3186c5d-bb93-5e9e-9146-b1b3a033d18c.png" width="300px">
> Wikipedia+Weblioも導入

けっこう読み込んでくれるのも増えてきて実際に使えそうな雰囲気も出てきました。

ただやはりOCRの時点で読み込みが出来ないとそもそもWikipediaなどに投げても違う結果になりますよね。

> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/550387be-dfab-1c2a-c249-ca464fd19ab2.png" width="300px">

**僕が書く豊見城（とみぐすく）は、何回やってもキビしい**ですね……

## 結果とまとめ

**沖縄強い**。

**沖縄の地名の難しさに勝つのは難易度がとても高い……** という結果です。

複数の辞書やWebサイトなどの情報を元に団体戦で挑まないといけないですね……

あと、僕が豊見城を書くと毎回（何回か書いて試した）「曲見」と認識されてしまうんですけど、これは`僕の字が汚いの問題なのかLINE BRAINの精度の問題なのか気になる`ところですね。

とりあえず、まだまだ課題があることが分かったので、今後も挑んで行きたいと思います。（たぶん気持ちだけ）

沖縄の皆さんからのコメントお待ちしております。それでは！