食べログを便利にするChrome拡張機能を作った話

食べログの店舗ページにGoogleMapの口コミ評価点数を表示するChrome拡張機能を作りました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240149/a001bef9-bc4e-c1b7-2d28-de4fcfe808ea.png)

こちらからどうぞ！
[食べログ GoogleMap評価追加](https://chrome.google.com/webstore/detail/%E9%A3%9F%E3%81%B9%E3%83%AD%E3%82%B0-googlemap%E8%A9%95%E4%BE%A1%E8%BF%BD%E5%8A%A0/chiajkmgndamdhdpfifldnkajpocgfng?hl=ja)

# 背景
多くの人はデートとか合コンとかの店を探す時に、食べログを使うと思います。食べログは店舗情報とか写真とかメニューとかデータが整理されているので便利なのですが、口コミがイマイチです。最近体感として、googlemapsの口コミが量も溜まってきていい感じなので、googlemapの口コミを食べログに表示しようと思います。ついでに、googlemapのリンクも貼って、口コミの詳細等を確認できるようにしました。

# 環境構築
Google Extension の開発環境を簡単にセットアップするライブラリが巷に転がってますが、導入するだけで半日かかってしまうので、今回は使いません。サクサク進めます。
もしテスト書きたい場合やファイルの変更を追従したい場合は下のようなライブラリを使うといいと思います。

- https://github.com/yeoman/generator-chrome-extension
- https://github.com/HaNdTriX/generator-chrome-extension-kickstart

さらに、拡張機能はすべてブラウザで動くのでローカルでは、nodeとかもなにもいりません。
なので、必要な環境はchromeとエディタだけです。

# 実装
### Chrome拡張機能
まずは、Chrome拡張機能を作るのに必ず必要な`manifest.json`から作っていきます。

```manifest.json
{
  "name": "食べログ GoogleMap評価追加",
  "version": "1.0.0",
  "manifest_version": 2,
  "description": "食べログの店舗ページにGoogleMapの評価を表示します。",
  "content_scripts": [{
    "matches": ["https://tabelog.com/*"],
    "js": [
      "content.js"
    ]
  }]
}
```
chrome拡張機能はcontent_scriptsとbackgroundとbrowser_actionの大きく3つに分かれています。

- content_scripts  
ブラウザのフロントで動く部分。DOMの操作とかができる。
- background  
バックグラウンドで動く部分。chromeの色々なAPIが使える。
- browser_action  
右上のアイコンを押して出てくる小さいページ。html,cssを使って作る。

今回はブラウザのフロントで食べログのデータを取得してそのままGoogle Maps APIを叩けばよいので、content_scriptsだけ使います。matchesはどのサイトでスクリプトを走らせるか。jsは実際に走らせるコードを指定します。

次にメインのコードの`content.js`です。

```content.js
window.onload = function() {
  var displayNameElement = document.getElementsByClassName('display-name')[0]
  if (!displayNameElement) {
    return
  }
  var query = "name="
  query += displayNameElement.children[0].innerText.trim();

  var address = document.getElementsByClassName('rstinfo-table__address')[0]
  if (address) {
    query += "&address="
    query += address.innerText
  }
  // XMLHttpRequestオブジェクトの作成
  var request = new XMLHttpRequest();

  // URLを開く
  var url = "https://xxxxxx.cloudfunctions.net/functions?" + query
  request.open('GET', url, true);

  // レスポンスが返ってきた時の処理を記述
  request.onload = function() {
    var res = JSON.parse(this.response)
    if (!res.candidates[0]) {
      return
    }
    var query = res.candidates[0].name
    var queryPlaceId = res.candidates[0].place_id
    var rating = res.candidates[0].rating
    var ratingElement = document.getElementById('js-header-rating').children[0]
    ratingElement.insertAdjacentHTML('beforeend', `
      <li class="rdheader-counts__item">
        <span class="rdheader-rating__review">
          <span class="rdheader-rating__review-target">
            <span class="unit">Googleによる評価</span>
            <a property="" href="https://www.google.com/maps/search/?api=1&query=${query}&query_place_id=${queryPlaceId}" target="_blank">
              <i>口コミ</i> <em class="num" property="v:count">${rating}</em>
            </a>
          </span>
        </span>
      </li>
      `);
  }

  // リクエストをURLに送信
  request.send();

};
```

食べログのページから店舗名と住所を拾ってきます。本来はこれをそのままGoogleMapsAPIに叩きに行けばいいのですが、セキュリティ上良くないので(CORSエラーが発生します)Cloud Functionsを噛ませます。
`https://xxxxxx.cloudfunctions.net/functions`はCloud Functionsのurlです。GoogleMapsAPIからのレスポンスとして名前とplace_idと評価をとってきます。評価を画面に表示し、名前とplace_idはgoogleにリンクを貼るために使います。

`https://www.google.com/maps/search/?api=1&query=${query}&query_place_id=${queryPlaceId}`みたいな感じのurlでお店ページへのリンクが貼れます。

### Cloud Functions
先程出てきた、カマセ役のCloud Functionsです。GCPのCloud Functionsを使用しています。受けたアクセスをそのままGoogleMapsAPIに送信しているだけです。Pythonで書いてますが、node.jsでも書けます。APIKEYはベタ書きですが、うまくやると環境変数で設定できた気がします。せっかくなので、GoogleMapsAPIは[こちら](https://github.com/googlemaps/google-maps-services-python)の公式ライブラリを使います。

```main.py
import googlemaps
import json
import flask
import requests

def place_api(request):
    request_json = request.get_json()
    if request.args and 'name' in request.args:
        key = 'xxxxxxxxxxxxxx'
        client = googlemaps.Client(key)

        input_string="Restaurant+"+request.args.get('name')
        if 'address' in request.args:
            input_string+="+"+request.args.get('address')
        place_result = client.find_place(
        input=input_string,
        input_type='textquery',
        fields=['name','rating','place_id']
        )


        response = flask.jsonify(place_result)
        response.headers.set('Access-Control-Allow-Origin', '*')
        response.headers.set('Access-Control-Allow-Methods', 'GET, POST')
        return response
    else:
        return f'Set args!'
```

```requirements.txt
googlemaps>=3.1.3
```
# 気になったこと
- GoogleMapsAPIはそこそこ高いので、破産しないように一日あたりの上限を設定します。月10000リクエストまで無料らしいので、一日333を上限にしました。
- 開発者向けにパッケージ化されてない拡張機能は無料で読み込めるのですが、ChromeWebStoreの登録は有料です。初回5$します。昼飯一回抜いたと思って支払いましょう。
- 最初名前だけでGoogleMapの情報をとってきたのですが、とくにアルファベットの名前のときに全然違う国の店がヒットすることがあるので、検索クエリに住所を付け足しました。また、GoogleMapは内部のデータの持ち方が微妙で、同じ店でも複数データが存在します。口コミとかが乗っているレストランのデータが欲しいので、クエリにRestaurantを付け足しました。



# まとめ
いい感じにサクッとできたので個人的には満足です。ぜひ皆さん使って下さい。