マイナビエンジニアブログの人気記事ランキングをSlack通知してみた【GAS+GA+スプレッドシートアドオン】

この記事は、【[マイナビ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/mynavi)】の11日目（12/11分）の記事となります。
後ほど別サイトへ移行する可能性があります。

記事ネタは色々と迷ったのですが、開発系のネタが良いとリクエストを頂いたので表題の件を書きます。

# 今回使った技術など

まずは今回使用した技術などを箇条書きでご紹介します。

- Google Analytics
- スプレッドシート（アドオン）
- Google App Script
- Slack API（Incoming Webhook）
- スクレイピング

# そもそも何やっている人なのか

この記事ネタだけ見に来た人からしたら興味ないと思うんですが、そもそもお前は誰なんだというのを簡単にご紹介しようかなと思います。

自分は新卒でマイナビに入社し、現在4年目になります。
主に社内向け共用サービスの管理やサイト開発、エンジニアブログの編集長、システム向けインターンシップの企画運営など、割と幅広い領域を担当しています。

宣伝になって申し訳ないのですが、マイナビエンジニアブログのリンクをご紹介します。もし興味があればのぞいてみて下さい。

- https://engineerblog.mynavi.jp/

大学では、ITとは全く関係の無い化学系の専攻でした。
趣味でサイト開発などを行っていたのですが、途中からそちらが化学系より面白く感じたのでIT系に進んだという形です。
なのでかなり独学が多く、記法などが一般的なセオリーに沿っていないかもしれませんがご了承下さい。

# 今回の案件背景

先ほどご紹介した通り、自分はエンジニアブログの編集長をやっています。
完全内製で構築＆記事も自分らで企画しているのですが、その中でサイトのKPI達成状況を確認すべく、分析を行いたいという話になりました。

そんなSEO解析をしている過程で、編集部内で「GAに詳しくない人でも見れるような人気記事ランキングを出したい」といった要望が発生。
色々話しましたが「まずはシンプルなものが良いよね」ということで、「Slackに今週の人気記事ランキングを通知する」といった、比較的シンプルな要件に落ち着きました。

今回は自分の勉強も兼ねていたので、そのアウトプットとして記載しています。
割と粗雑に書いているので、その辺りはご了承下さい。

# GAのデータをSlack通知までもっていく戦略

GAのデータを使うということで、全てGoogle内で完結できるなと考え、以下戦略で実装することにしました。

- GASを使って無料でサーバレスに
- スプレッドシートのアドオンでGAと連動
- Slack APIのIncoming WebhookでPOST

# 実装工程

## Google Analyticsの設定

まずはエンジニアブログ内に、GAで計測するためのgtagを発行し、サイトに埋め込みます。
この時、GAで発行したビューのラベル名を覚えておくと良いです。

![20201210img1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/07e38c57-463f-7898-20e8-2771ec7e974c.jpeg)

<figcaption>▲この画像でいう「社外からのアクセス」です</figcaption>

## スプレッドシートとGoogle Analyticsとの連動

### アドオンの追加

次にスプレッドシートを開き、ページ上部のタブにある「アドオン > アドオンを取得」を選択します。
すると図のような連携できるアドオン一覧が表示されるので、「Google Analytics」を選択してインストールします。

![20201210img2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/5f744e31-2bc4-e80e-c462-37bfc78cf45e.jpeg)

### レポートの設定

インストールが完了すると、先ほど追加時にクリックした「アドオン」の中に「Google Analytics」が追加されているので、「Create new report」を選択してシート内に書き込みを行うレポートを作成します。
レポート作成時に以下項目を記入する必要があるので、それぞれ設定して下さい。

参考までに、エンジニアブログでは「過去7日間のページビュー数」を「記事ページ」で算出することにしており、以下のようにしています。

- 1) Name your report：任意の名前
- 2) Select a view：「アカウント > プロパティ > ビュー」の順で選択
- 3) Choose configuration options：メトリクス、ディメンション、セグメントを指定
 - 例：`「Metrics：ga:pageviews」「Dimensions：ga:pagePath」「Filters：ga:pagePath=@interview,ga:pagePath=@technology,…」`
 - どれを選べばいいかなど、これらの設定方法については【[公式リファレンス](https://ga-dev-tools.appspot.com/dimensions-metrics-explorer/)】を参照

※これらの設定は後で書き換えられます

必要事項の記入が終わったら、「Create Report」を選択して下さい。
自動的に、「Report Configuration」というシートが出来上がります。

![20201210img3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/79ef0e21-3571-6efe-6a95-2be05a2ffdb1.jpeg)

デフォルトでは「30日前～昨日」「データ上限1000」になっているので、必要に応じて変更して下さい。
シート上に直接書き込みができます。

### レポートの作成

シート上での設定が完了したら、もう一度ページ上部の「アドオン > Google Analytics」から、「Run report」をクリックして下さい。
設定が正しく行われていれば、その時点でシートが新たに生成され、GA上の指定したデータが入ります。

![20201210img4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/43518f14-c7b5-5a59-9839-ca309e894a68.jpeg)

なお、定期実行をしたい場合には「Schedule Report」から設定することができます。

![20201210img5.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/e0118900-8823-4d9d-42be-74bfa01a9cfc.jpeg)

<figcaption>▲これだと「毎週月曜9-10時の間に自動実行」になります</figcaption>

## GASでの必要情報スクレイピングとSlack通知の実装

ここからはGASによる必要情報のスクレイピング、およびSlack通知の実装を行います。
レポートを作成したスプレッドシートにて、ページ上部の「ツール > スクリプトエディタ」からGASを開いて下さい。

### 通知に必要な情報のスクレイピング

ここでは詳しくは解説しませんが、GASではスクレイピングを簡単に実行することができます。
今回はSlack通知時に「タイトル」「サムネイル」「公開日」の情報が追加で必要であったため、それらの情報をスクレイピングすることにしました。

手法としては、

1. アドオンにより取得したGAのpath情報を使用して、当該ページをスクレイピング
2. 正規表現を用いて「タイトル」「サムネイル」「公開日」をデータから抜き出し
3. スプレッドシートに、GAのデータと2のデータを併せたシートを作成

といった流れです。

なお、スクレイピングを行う際には対象サイト等の規約を必ず遵守して下さい。

### スクレイピング実行

```js
var rankingDataLink = mainURL() + rankingDataPath;

var titleRegexp = new RegExp(/<title>.*?<\/title>/);
var thumbnailRegexp = new RegExp(/<meta property="og:image" content=".*?" \/>/);
var postDateRegexp = new RegExp(/<time datetime=".*?<\/time>/);

try {
  var itemContent = UrlFetchApp.fetch(rankingDataLink).getContentText('UTF-8');
  var itemTitle = itemContent.match(titleRegexp)[0].replace(/<\/*title>/g,'').replace(' | マイナビエンジニアブログ','');  
  var itemThumbnailLink = itemContent.match(thumbnailRegexp)[0].replace(/<meta property="og:image" content="/g,'').replace(/" \/\>/g,'');
  var itemPostDate = itemContent.match(postDateRegexp)[0].replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'');

  mainSheet.getRange(mainInsertRowNum,1).setValue(itemTitle);
  ... // 以下適宜データをシート挿入
}
} catch(e) {
}
```

`mainURL()`はエンジニアブログのフロントページリンクが、`rankingDataPath`にはGAのpath情報が入っています。
タイトルは`<title>`から、サムネイルは`<og:image>`から、日付は`<time>`タグから取得しました。

上記をループさせて、各記事の必要データを取得しました。
取得後のデータは以下のようなシートに出力されます。

![20201210img6.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/f86142f0-675e-62c4-5b18-49d3f525b1ee.jpeg)

### Slack通知

これで全て必要な情報がそろったので、最後にSlack通知を実装します。

初めてSlack通知を実装する方は

- 「Slack API」をネットで検索
- 「Create New App」で該当ワークスペースへアプリを追加

を事前に行って下さい。

アプリ追加後は、追加したアプリを選択し、左カラムにある「Incoming Webhooks」を選択します。

![20201210img7.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/8d4d5d1d-e217-aedb-2021-917b52135385.jpeg)

ページに入ったら「Activate Incoming Webhooks」を「On」にし、ページ下の「Add New Webhook to Workspace」から通知したいチャンネルを選択して、webhook URLを払い出して下さい。

払い出されたものは先ほどのページに表示されます。

```
https://hooks.slack.com/services/1234567/ABCDEFG/test123456789
```

のようなURLが払い出されていると思いますので、それをコピーしておいてください。

### 通知用JSON生成

SlackへPOSTするJSONを作成します。
JSONは「Block kit Builder」を使用すると簡単に生成できます。

- https://api.slack.com/tools/block-kit-builder

### POST実行

スクレイピングのときと同じ`UrlFetchApp.fetch`を使用することでSlackへ投稿ができます。

```js
var payload = JSON.stringify(postData);
var options = {
  "method": "post",
  "contentType" : "application/json",
  "payload" : payload
}
UrlFetchApp.fetch(webhookURL(), options);
```

こちらも定期実行ができます。
対象の関数にトリガーを設定することで、Slack通知も自動実行成されます。
※GAのレポート生成とは別に必要なのでご注意下さい。

最終的な完成形として、Slackに以下のような形で通知がなされます。

![20201210img8.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912086/79f12295-71d5-3a37-9da8-b3b51a3d421d.jpeg)

黒い所にはPV数が入っています。

# 終わりに

1日あれば作れるような簡単なプログラムで、勉強目的には最適でした。
余裕があれば開発系の勉強記事以外にも、採用や共用サービス運用系など書いていきたいと思います。