🇯🇵 日本語/🇺🇸 English

GA4のAPIとGitHub Actionsを使って人気記事を自動更新する

  • 開発

このブログに人気記事一覧を表示させるようにしてみました。
毎朝9時に自動更新される仕組みです。

本記事ではその方法を詳細に説明していきます。

前提

  • コンテンツ管理にはmicroCMSを利用している
  • アナリティクスにはGoogle Analytics 4を利用している
  • ソースコードをGitHubで管理している
  • フロントエンドはSSGしている

全体の流れ

  1. Analytics Data APIを用いて記事のPV情報を取得する
  2. PV上位10件をmicroCMSに保存する
  3. サイトをビルド&デプロイし、人気記事一覧を更新する
  4. 1〜3の処理をGitHub Actionsのcronを用いて毎朝9時に自動実行する

Google Analyticsから情報を取得する

GA4から情報を取得するためには、Analytics Data APIを用います。

そのためにGoogle Cloud Platform(GCP)のアカウントが必要です。これは各自用意してください。

次にGCPのプロジェクトを作成します。

その後、「APIとサービス > 有効なAPIとサービス」からGoogle Analytics Data APIを見つけ、有効化しましょう。

そして、「認証情報を作成」ボタンをクリックします。

認証情報としてサービスアカウントを作成します。次のように進めていきます。

以上でサービスアカウントが作成されます。

次に、サービスアカウントキーをJSON形式でダウンロードします。

以上でGCP側の準備は完了です。

今度はGoogle Analyticsに移動し、「設定 > プロパティのアクセス管理 > ユーザーを追加」から先ほど作成したサービスアカウントのメールアドレスを追加します。

これにより、サービスアカウントの認証を使って自身のGoogle Analyticsのデータを読み取ることができるようになりました。

PV上位10件を取得し、microCMSに保存する

本ブログはCloudflareにホスティングしているので、データの保存先としてWorkers KVやD1などを使ってみたかったんですが、エッジ上でAnalytics Data APIが動かず断念しました・・・。
(NodeのAPIを使わず素で呼び出そうと頑張ったんですが、認証周りが厳しそうだったので諦めました)

というわけで、元々コンテンツ管理としても利用しているmicroCMSにデータを保存することにしました。

microCMS側の設定

PVランキングを保存するための箱をmicroCMSに用意します。

今回はpopularというAPIを作成しました。

リスト形式で作成します。

オブジェクト形式で作成しても良いのですが、実は裏技的なテクニックがありまして。
本ブログにはRanking(自動)とPickup(手動)という2種類の枠を用意しており、その両方をpopularという一つのAPIで管理してしまおうという作戦です。

APIスキーマは、別APIとして管理しているブログAPIを複数コンテンツ参照します。

以上でAPIの準備は完了です。

次にコンテンツIDをrankingとして、ランキング管理用のコンテンツを作成して公開しておきます。

最後にこのコンテンツに対してAPIから書き換えを行うためにAPIキー設定にてPATCHを許可します。

本来、GETPATCHのキーは分けるべきですが、HobbyプランでAPIキーを1つしか作れないのでやむなし・・・。

SSGかSSRでAPIキーが秘匿されるようにしましょう。

これでmicroCMS側の準備は完了です。

コード側

次はコード側を進めていきます。

まずは下記2つのパッケージをインストールします。

npm install @google-analytics/data microcms-js-sdk

そして、実際にPVを取得してmicroCMSに書き込む処理はこちら。
後のGitHub Actionsでの処理も考慮して、.github/actions/ranking/index.jsというファイルを作成します。

import { BetaAnalyticsDataClient } from '@google-analytics/data';
import { createClient } from 'microcms-js-sdk';
const client = createClient({
  serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
  apiKey: process.env.MICROCMS_API_KEY
});

const serviceAccountKey = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_KEY || '');
const analyticsDataClient = new BetaAnalyticsDataClient({
  credentials: {
    client_email: serviceAccountKey.client_email,
    private_key: serviceAccountKey.private_key,
  },
});

export const getPopularArticles = async () => {
  const [response] = await analyticsDataClient.runReport({
    property: `properties/${process.env.GA_PROPERTY_ID}`,
    dateRanges: [
      {
        startDate: '8daysAgo',
        endDate: '1daysAgo',
      },
    ],
    dimensions: [
      {
        name: 'pagePath',
      },
    ],
    metrics: [
      {
        name: 'screenPageViews'
      }
    ],
    // トップページ(/)を除く
    dimensionFilter: {
      filter: {
        fieldName: 'pagePath',
        stringFilter: {
          matchType: 'FULL_REGEXP',
          value: '^/[^/]+$',
        },
      },
    },
    limit: '10',
  });
  const data = response.rows?.map(row => {
    return {
      path: row.dimensionValues?.[0].value,
      views: row.metricValues?.[0].value
    }
  });
  return data;
};

const popularArticles = await getPopularArticles() || [];
const ids = popularArticles.map((article) => article.path.slice(1));

await client
  .update({
    endpoint: 'popular',
    contentId: 'ranking',
    content: {
      articles: ids,
    },
  });

本ブログの場合は/xxxxxxというURLパスのPVをカウントしたかったため、dimensionFilterではそのような正規表現を書いています。

limitの部分で上位何件分を取得するか決定しているので、適宜変更してください。

環境変数

Analytics Data API、Google Analytics、microCMS APIに必要な認証情報は環境変数で管理しています。

GOOGLE_SERVICE_ACCOUNT_KEY

先ほどダウンロードしてきたサービスアカウントキーのJSONデータをそのまま文字列として環境変数に突っ込んでいます。

GA_PROPERTY_ID

Google AnalyticsのプロパティIDです。「管理 > プロパティ」で確認できます。

MICROCMS_SERVICE_DOMAIN

microCMS管理画面URLのxxxxxx.microcms.ioxxxxxxの部分です。

MICROCMS_API_KEY

microCMSのAPIキーです。「サービス設定 > APIキー」で確認できます。

GitHub Actionsのcronを用いて毎朝9時に自動実行する

これまでの処理をGitHub Actionsで実行します。

先ほど作成した.github/actions/ranking/index.jsというファイルに加えて、.github/workflows/ranking.ymlを用意します。

name: Set Ranking

on:
  schedule:
    - cron: '0 0 * * *'
  workflow_dispatch:

jobs:
  set-ranking:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/[email protected]
        with:
          node-version: '18'
      - name: Install dependencies
        run: npm install
      - name: Set Ranking
        env:
          GOOGLE_SERVICE_ACCOUNT_KEY: ${{secrets.GOOGLE_SERVICE_ACCOUNT_KEY}}
          GA_PROPERTY_ID: ${{secrets.GA_PROPERTY_ID}}
          MICROCMS_API_KEY: ${{secrets.MICROCMS_API_KEY}}
          MICROCMS_SERVICE_DOMAIN: ${{secrets.MICROCMS_SERVICE_DOMAIN}}
        run: node ./.github/actions/ranking
      - name: Publish to Cloudflare Pages
        run: curl -X POST "https://api.cloudflare.com/client/v4/pages/webhooks/deploy_hooks/${{ secrets.CLOUDFLARE_DEPLOY_HOOK }}"

まずはトリガー部分ですが、cronを使って毎朝9時に定期的に発火されるようにします。
(UTCで0時0分なので、日本時間だと朝9時になります)

on:
  schedule:
    - cron: '0 0 * * *'
  workflow_dispatch:

ちなみに動かしてみると1時間くらい発火がズレて、朝10時くらいに動き出します。

workflow_dispatchと記述しておくと、GitHub Actionsの管理画面から手動でも動かせるので便利です。

そして、.github/actions/ranking/index.jsを動かす部分ですが、環境変数の設定が必要です。

GitHubの設定画面からSecretsの設定ができるので、そこで先ほどの4つの環境変数をセットします。

加えて、記事ランキングの更新を本番反映させるために、最後にCloudflareへのビルド&デプロイをしたいので、CloudflareのビルドフックのID部分も環境変数にしています。
(ここは記事ランキングを管理しているmicroCMS側からWebhookで発火しても良さそうです)

お疲れ様でした。以上で準備は完了です。

ソースコードをPUSHして、無事に動くかどうか試してみましょう。
(ちなみにcronはmainブランチでないと動かないらしいです)

下記のように動作していれば成功です。

おわりに

かなり長い記事になってしまいましたが、詳細に手順を紹介できました。

環境によって少しずつ変更点はあると思いますが、microCMSを利用している環境であれば、だいたい同じような感じで動かすことができると思います。

自動的にデータが更新されるのは気持ち良いので、ぜひお試しあれ!

柴田 和祈 X GitHub
株式会社microCMS 共同創業者 / デザイナー兼フロントエンドエンジニア / ex Yahoo / 2児の父 / 著書「React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで 」

Recommended