GASとPageSpeed Insights APIで作る-スコア自動収集と可視化

## この記事について

### 説明すること
- PageSpeed Insights APIでスコアを自動で取得する方法
- Google Apps Scriptを使った実装方法（プログラムコードあり）

### 説明しないこと
- PageSpeed Insights自体の使い方
- パフォーマンス改善方法
- スプレッドシートの基本操作
- Google Apps Scriptの基本的な使い方

## きっかけ

Webサイトを運用していると、サイトパフォーマンスやSEO対策状況が気になります。
それら自体はPageSpeed Insightsで取得可能ですが、
私の場合、以下のような課題を抱えていました。

- リリースによるパフォーマンスやSEOへの影響が気になる
- 毎回PageSpeed Insightsをチェックするのが面倒
- 過去のスコアと比較し、改善の効果を分析したい

これらの課題を解決するため、スコアを自動で取得・可視化する仕組みを作ってみました。

### PageSpeed Insightsとは

Googleが提供するWebパフォーマンス計測ツールです。
URLを入力するだけで、そのページの読み込み速度やユーザー体験に関する様々なメトリクスが確認できます。

[PageSpeed Insights](https://pagespeed.web.dev/)

## やりたいこと
やりたいことをまとめると、以下が要件になってきました。

- PageSpeed Insightsのスコアを定期的に自動取得
- 結果をグラフで簡単に確認
- 運用コストは最小限に

## 技術選定

定期的に自動取得したいので、Googleが提供しているPageSpeed Insights APIを使用します。
[PageSpeed Insights API 公式ドキュメント](https://developers.google.com/speed/docs/insights/v5/get-started?hl=ja)

また、呼び出し方法を検討した結果、Googleスプレッドシート + GAS (Google Apps Script) を選択しました。

### 選定ポイント

- 無料で利用可能
- 設定が比較的簡単
- スプレッドシートに出力した後は自由にグラフ作成が可能

### (参考) 代替案
以下が候補だった時期がありました。

- [PageSpeed Insights CLI (npm)](https://www.npmjs.com/package/pagespeed-insights)
- [Lighthouse CI (npm)](https://www.npmjs.com/package/lighthouse-ci)

当初はNode (JavaScript) で実装するのがスムーズと考えていましたが、
処理を定期実行する環境やデータを蓄積するストレージ、そしてその後の見える化の手法を考えていくと、GASで処理してスプレッドシートに出力するのが最もメリットが高い結論となりました。

なお、Node以外にも多数の言語用ライブラリが提供されているようです。
[ライブラリとサンプル](https://developers.google.com/speed/docs/insights/libraries?hl=ja)

## 実装方針

1. GASのトリガーで毎日定刻に実行
2. GASで呼び出すPageSpeed Insights APIでスコアを取得
3. スプレッドシートにAPI取得結果を出力
4. 行（その日の出力結果）が追加されるたびにスプレッドシートのグラフが自動更新

## 実装のポイント

### 1. APIキーの準備 (スキップ可)
PageSpeed Insights APIは、APIキーありとなしの2つの方法で利用可能です。

#### APIキーなしで利用する場合
- 特別な準備は不要
    
ドキュメントのAPIキーの説明に「1秒間に複数回」と書かれているので、並列実行しないのであればAPIキーは不要でいいのかもしれません。
なお、本記事のサンプルコードは直列実行で作成しています。

#### APIキーを使用する場合
- [公式ドキュメント](https://developers.google.com/speed/docs/insights/v5/get-started?hl=ja)を参考にAPIキーを取得
- GoogleCloudの画面が出て戸惑うかもしれませんが、手順はあまり難しくないはず
- 利用量の確認は[GoogleCloudのコンソール](https://console.cloud.google.com/apis/api/pagespeedonline.googleapis.com/)から確認可能

### 2. GASスクリプトの作成準備
スプレッドシートから「拡張機能」→「Apps Script」を選択し、新規GASスクリプトを作成します。

![2.GASスクリプトの作成準備の選択手順.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231426/4c9dabce-6c24-9dee-ef26-1ebbc49b8b2a.png)

### 3. GASスクリプトの作成

以下は実行可能なサンプルコードです。
必要な情報が取得できるように、適宜書き換えて使用してください。
ソースコードの内容は、コード中のコメントを参照してください。

```js
// APIキーがある場合は設定
const API_KEY = "";

// 対象URL + 出力先シート名
const TARGET_URL_AND_SHEET_NAMES = [
  ["https://example.com/", "結果-TOP"],
  ["https://example.com/about", "結果-about"]
];

// 取得したいカテゴリ
// const CATEGORIES = ['performance', 'accessibility', 'best-practices', 'seo', 'pwa'];
const CATEGORIES = ["performance", "seo"];

// メイン処理
function run() {
  const currentTime = new Date().toISOString();

  TARGET_URL_AND_SHEET_NAMES.forEach(([url, sheetName]) => {
    const sheet = getOrInitSheet(sheetName);
    const columns = [currentTime, url];

    try {
      const json = fetchPageSpeed(url);

      CATEGORIES.forEach((category) => {
        const score = json.lighthouseResult.categories[category].score * 100;
        columns.push(score);
      });

      writeToSheet(sheet, columns);
    } catch (error) {
      console.error(`Error: ${error}`);

      columns.push("Error");
      writeToSheet(sheet, columns);
    }
  });
}

// PageSpeed Insights APIを呼び出す
function fetchPageSpeed(url) {
  // PageSpeed Insights APIのリクエスト/レスポンスの詳細
  // https://developers.google.com/speed/docs/insights/v5/reference/pagespeedapi/runpagespeed?hl=ja

  const apiUrl = buildPageSpeedUrl(url);
  console.info(apiUrl);

  const response = UrlFetchApp.fetch(apiUrl);
  const json = JSON.parse(response.getContentText());
  console.info(json);

  return json;
}

// PageSpeed Insights APIのURLを生成する
function buildPageSpeedUrl(targetUrl) {
  const params = [
    `url=${targetUrl}`,
    'strategy=mobile',
    ...CATEGORIES.map(category => `category=${category}`),
  ];

  if (API_KEY) {
    params.push(`key=${API_KEY}`);
  }

  return `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?${params.join("&")}`;
}

// シートを取得して返却する。存在しなければ作成し、ヘッダー行を追加して返却する
function getOrInitSheet(sheetName) {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  let sheet = spreadsheet.getSheetByName(sheetName);

  // シートがある場合は終了
  if (sheet) {
    return sheet;
  }

  // シートがない場合は作成
  sheet = spreadsheet.insertSheet(sheetName);
  sheet.appendRow(["Date", "URL", ...CATEGORIES]);
  return sheet;
}

// スプレッドシートに書き込む
function writeToSheet(sheet, rowData) {
  sheet.appendRow(rowData);
}
```

#### 実行後の出力結果
![出力結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231426/f1020466-7653-3064-e42e-033e9db2e645.png)


### 4. 定期実行の設定
スクリプトの定期実行には、GASのトリガー機能を使用します。

1. スクリプトエディタの「トリガー」メニューを開く
2. 「トリガーを追加」をクリックする
3. 以下の設定を行う：
    - 実行する関数：「run」
    - 実行するイベントのソース：「時間主導型」
    - 時間ベースのトリガーのタイプ：「日付ベースのタイマー」
    - 時刻：任意（深夜など負荷の少ない時間帯を推奨）

![3.定期実行の設定の設定例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231426/74cd0ab8-8b87-1d81-3fa2-0890501feadc.png)

### 4. グラフの作成
データが揃えば、スプレッドシート上で欲しいグラフを作るのは思いのままだと思います。

### 注意点
- PageSpeed Insights API関連
    - APIキーなしの場合、1秒間に複数アクセスができない
    - APIキーありの場合、無料で1日あたり25,000件までアクセスできる
- Google Apps Script関連
    - スクリプトの実行時間は6分以内に収める必要がある

## まとめ

GASを使うことで、思った以上に簡単に実装できました。
特に良かった点は以下の通りです。

- コードは100行程度でシンプル
- 毎日自動でデータが蓄積される
- チームメンバーとの共有も容易

この仕組みのおかげで、パフォーマンスやSEOの推移を継続的に把握できるようになり、改善施策の効果測定もスムーズになりました。
継続的な計測があってこそ、効果的な改善が可能になります。