眼精疲労がひどくなってめまいになってしまい、なんとか改善できる方法をないか探しています。

Hackerの皆様はお元気でお過ごしでしょうか...

本記事のゴール

前回の記事で、FCP/LCPの問題に対処し、少しずつ状況が改善しているようではありますが、サイト内の各ページの状況を網羅的に確認したいと思いました。

参考記事:

モルドスプーンアイコン

1つ1つ、ここからテストにかけるのはどう見ても面倒臭すぎます...。

そこで、PageSpeed API を使用して、一気に複数ページの分析結果をCSV形式で出力できるシステムを作ります。

技術調査

公式ドキュメントを読む

こちらの公式ドキュメントにPageSpeed APIの使い方が まとまっています。

PageSpeed Insights API を試すだけであれば、API キーは不要です。

とあるように、APIキーなしでも、curlコマンドから結果を試しに確認することができるようです。

まずこちらで試します。

curlでテスト実行してみる

以下のコマンドを実行し、本ブログのトップのスコアを確認して見ます。

結果がとても長いので、出力が消化されないよう、テキストファイルに出力します。

# 末尾の-oオプションが出力。上書きでなく「追加」なので注意
curl 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://moldspoon.jp/blog' -o output.txt

以下のように出力されます。

{
  "captchaResult": "CAPTCHA_NOT_NEEDED",
  "kind": "pagespeedonline#result",
  "id": "https://moldspoon.jp/blog",
  "loadingExperience": {
    "id": "https://moldspoon.jp",
    "metrics": {
      "CUMULATIVE_LAYOUT_SHIFT_SCORE": {
        "percentile": 0,
        "distributions": [
          {
            "min": 0,
            "max": 10,
            "proportion": 0.95499999999999952
          },
          {
            "min": 10,
            "max": 25,
            "proportion": 0.029999999999999988
          },
          {
            "min": 25,
            "proportion": 0.014999999999999998
          }
        ],
        "category": "FAST"
      },
      // 以下省略

とても長くてわかりにくいです...

ブラウザに投げ込むとJSON構造が細かく見れる

ドキュメントを掘り進めてパラメーターの詳細を...と思いましたが、ブラウザのアドレスバーにURLを入れるだけで、構造が見れることに気づきました。

https://www.googleapis.com/pagespeedonline/v5/runPagespeed?locale=ja&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&url=https://moldspoon.jp/blog

をブラウザに入れると見事に表示されます。

▶️を押すと、そのグループを省略できたりするので意外と見やすい

▶️を押すと、そのグループを省略できたりするので意外と見やすい

取得するAPIのオプションの紹介

ここで、オプションを超簡単に紹介しておきます。詳しくはこちらをご覧ください

大まかに「どのページが悪いか」が、わかる項目だけ取る

かなり細かく項目が存在するので、CSVに書き出すにあたってキリ がなくなりそうです。

設計方針としては。「どのページが悪いか」を目視でざっくりと確認できるようにし、スコアが悪いページから、優先的に個別に掘り下げて改善していくという対応が取れるような思想で実装を進めたいと思います。

以下のスクリーンショットに写っているスコア・指標が取れれば最低限は充足すると私は感じたのでそのようにします。

pagespeed4.png

実装

Laravelプロジェクトの初期設定については割愛します。

ことを前提として進めていきます。

よろしければ、Mac+Docker(仮想開発環境)で管理画面を開発できるLaravel Sail + Filamentの記事も書いているのでご覧ください。

参考記事:

モルドスプーンアイコン

APIキーの取得

まず、さきほどのPageSpeed APIの公式ドキュメントからAPIキーを取得します

赤いところをクリック

赤いところをクリック

分析したいプロジェクトを選択し、しばらく待つとキーが表示されるのでコピーしておきます。

キーをコピーしておく

キーをコピーしておく

Laravelの.envに以下のように書いておきます。

PAGESPEED_API_KEY='ここに書く'

つづいて、config/gcp.phpを新たに作り(ある人は適宜合流してください)下記のように書きます。

<?php
return [
  'pagespeed' => [
    'key' => env('PAGESPEED_API_KEY')
  ]
];

以下のコマンドを忘れず実行しておきます。.envを読み込みし直しています。

sail artisan config:cache

前提となる、URLリストの設置

Google Console上の警告リストから、そのままサクッとコピペで貼り付けられるように、以下のようなテキストファイルをLaravelのstorage/app/speedtest-urls.txtとして用意することにしました。

https://moldspoon.jp/blog/posts/how-to-start-laravel-filament
https://moldspoon.jp/blog/posts/authjs-with-prisma
https://moldspoon.jp/blog/before_posts/add-zod-to-myblog
https://moldspoon.jp/blog/posts/add-supabase-access-counter-to-blog
https://moldspoon.jp/blog/posts/add-php-batch-with-mysql-in-shortest-time
https://moldspoon.jp/blog/posts/laravel-filament-csv-download
https://moldspoon.jp/blog/posts/react-hook-form-and-zod
https://moldspoon.jp/blog/posts/route53-add-txt-record
https://moldspoon.jp/blog/posts/stream-download-from-s3-by-laravel
https://moldspoon.jp/blog/posts/add-toc-to-blog
...

Commandの実装

Laravel Sail環境の場合 ですが、ローカルから下記をプロジェクトのルートで実行します。

sail artisan make:command EvaluteConsoleUrlAndSaveData

app/Console/Commands以下にファイルを生成した後、以下のように実装します

app/Console/Commands/EvaluteConsoleUrlAndSaveData.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Storage;

class EvaluteConsoleUrlAndSaveData extends Command
{

    protected $baseUrl = "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?locale=ja&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO&url=";

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'evaluate-console-url-and-save-data';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'speedtest-urls.txtに従って画像を一斉ダウンロード';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {

        echo "実行開始 ...";

        // ストレージから読み込み
        $consoleUrlStr = Storage::disk('local')->get('speedtest-urls.txt');

        // 配列に分割
        $consoleUrls = explode("\n", $consoleUrlStr);

        $resultLists = [];
        foreach ($consoleUrls as $consoleUrl) {
            // 空白はスキップ
            if (!$consoleUrl) {
                continue;
            }

            $analyzeUrl = $this->baseUrl . $consoleUrl;
            $resultJson = file_get_contents($analyzeUrl);
            $obj = json_decode($resultJson);

            // スコアを出力
            $performanceScore = $obj->lighthouseResult->categories->performance->score;
            $accessibilityScore = $obj->lighthouseResult->categories->accessibility->score;
            $bestPracticesScore = $obj->lighthouseResult->categories->{'best-practices'}->score; // キーにハイフンが含まれるので一部違う
            $seoScore = $obj->lighthouseResult->categories->seo->score;

            // 指標の目印となるスコアを出力
            $firstContentfulPaintScore = $obj->lighthouseResult->audits->{'first-contentful-paint'}->score;
            $largestContentfulPaintScore = $obj->lighthouseResult->audits->{'largest-contentful-paint'}->score;
            $totalBlockingTimeScore = $obj->lighthouseResult->audits->{'total-blocking-time'}->score;
            $cumulativeLayoutShiftScore = $obj->lighthouseResult->audits->{'cumulative-layout-shift'}->score;
            $speedIndexScore = $obj->lighthouseResult->audits->{'speed-index'}->score;

            // 指標を出力
            $firstContentfulPaint = $obj->lighthouseResult->audits->{'first-contentful-paint'}->displayValue;
            $largestContentfulPaint = $obj->lighthouseResult->audits->{'largest-contentful-paint'}->displayValue;
            $totalBlockingTime = $obj->lighthouseResult->audits->{'total-blocking-time'}->displayValue;
            $cumulativeLayoutShift = $obj->lighthouseResult->audits->{'cumulative-layout-shift'}->displayValue;
            $speedIndex = $obj->lighthouseResult->audits->{'speed-index'}->displayValue;

            $list = [
                $consoleUrl, // URLを冒頭に入れる
                $performanceScore,
                $accessibilityScore,
                $bestPracticesScore,
                $seoScore,
                $firstContentfulPaintScore,
                $largestContentfulPaintScore,
                $totalBlockingTimeScore,
                $cumulativeLayoutShiftScore,
                $speedIndexScore,
                $firstContentfulPaint,
                $largestContentfulPaint,
                $totalBlockingTime,
                $cumulativeLayoutShift,
                $speedIndex
            ];

            $resultLists[] = $list;

            // 2秒ほどwait
            sleep(2);
        }

        // CSV出力処理

        $header = [
            'URL',
            'パフォーマンス',
            'アクセシビリティ',
            'ベストパフォーマンス',
            'SEO',
            'FCPスコア',
            'LCPスコア',
            'TBTスコア',
            'CLSスコア',
            'SIスコア',
            'FCP',
            'LCP',
            'TBT',
            'CLS',
            'SI',
        ];

        $csvFile = fopen('speedtest-results.csv', 'w');

        fputcsv($csvFile, $header);

        foreach ($resultLists as $row) {
            fputcsv($csvFile, $row);
        }

        fclose($csvFile);

        // 最終的にcsvとして、元のフォルダに出力
        Storage::disk('local')->put('speedtest-results.csv', file_get_contents('speedtest-results.csv'));

        echo "実行完了 ... csvファイルを出力しました。";

        return Command::SUCCESS;
    }
}


Commandを実行

下記で処理を実行します。

sail artisan evaluate-console-url-and-save-data

ファイルは、テキストファイルと同じstorage/app/以下に出力されます。

実行時の注意事項

URLの数によっては当然時間がかかります。気長に待ちましょう。また、数百行を超えるサイズの検証は当方では行っていないので、実行時の責任は負いかねますためあらかじめご了承ください。

大量に検証を行いたい場合はGoogle側に負荷をかけないかなど、適切かつ慎重にご利用いただくのが望ましい姿 と考えております。

結果の確認

storage/app/speedtest-result.csvに出力されたcsvを開いて確認します。

想定通り出力

想定通り出力

いい感じですね。(ドヤ顔)

あっ、余裕があれば、スコアの表示を「0~100点」に改修しよう...(小声)
補足

私はLibreOfficeで開きましたが、エクセルで開くと文字化けしそう。UTF-8で出力しているためです。メモ帳などで開くとひとまず結果はみれると思いますので念のためお伝えいたします。

まとめ

結果ですが、構造体のキーの捜索に時間がかかってしまって大変でした。 「audits」キーの下にさまざまな分析結果が、並列で164個程度 ぶら下がっていましたしね。

意外に面倒な作業となったわけでその辺りが、この件に関するエントリーがググっても少なかった理由かも... ただ、こういう誰もやりたがらないようなスクリプトを作ることにこそ、仕事の本質があるはずなので、引き続き地道にエントリー書いていけたらなと思っています。

よければシェアお願いいたします!


この記事が何かのお役に立てれば幸いです。
最後までお読みいただきありがとうございました!