複数ページの PageSpeed Insights の結果を取得したい
これまで、
- LOLIPOP! レンタルサーバーの LiteSpeed サーバー
- WordPress & Cocoon & LiteSpeed cache プラグイン
- QUIC.cloud CDN
を使用して、PageSpeed Insights のパフォーマンスの数値を向上させてきました。
トップページ等、単独のページの PageSpeed Insights のパフォーマンスの結果を取得するのであれば問題ないのですが、複数のページを一括で比較しようとすると、
- 対象のページを開く
- WordPress & Cocoon でログインしている場合には、下部にある PageSpeed Insights へのリンクをクリック
- 結果をコピー & ペーストで集計する
という煩雑な手順で作業する必要があります。
この先、QUIC.cloud の設定を変更しながら各サイトの PageSpeed Insights の結果を比較するとか、特定のページの結果の推移を確認するのに、繰り返し同じ作業をするのはあまりにも煩雑すぎます。
そこで、複数ページの PageSpeed Insights の結果を一括で取得する方法について調べてみました。
20230319 追記 : psi-score-collector を使用して、QUIC.cloud CDN の無料枠を使い切った時の PageSpeed Insights への影響を調べました。
20230329 追記 : 複数ページの Chrome で測定した Lighthouse の結果を取得する方法について記載しました。lh-score-collector を作ってみました。
PageSpeed Insights の結果をコマンドラインで取得する方法
curl で googleapis.com を読み込む
PageSpeed Insights には、ウェブブラウザで調べる以外にもコマンドラインで結果を取得する方法があります。
PageSpeed Insights API のページでは、以下の方法が記載されています。(連続で測定しない場合は API key を取得する必要はありません)
$ curl https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://developers.google.com
curl で googleapis.com を読み込む方法です。'?url=' 以降に記載された URL の結果を取得できます。結果は JSON 形式で標準出力に出力されますので、リダイレクトしてファイルに保存する必要がります。
私のサイトのホームページの場合には、次のように実行すると JSON 形式の結果が results.json に記録できます。
$ curl https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://hiro20180901.com > results.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 556k 0 556k 0 0 44578 0 --:--:-- 0:00:12 --:--:-- 156k
$ head results.json
{
"captchaResult": "CAPTCHA_NOT_NEEDED",
"kind": "pagespeedonline#result",
"id": "https://hiro20180901.com/",
"loadingExperience": {
"initial_url": "https://hiro20180901.com/"
},
"lighthouseResult": {
"requestedUrl": "https://hiro20180901.com/",
"finalUrl": "https://hiro20180901.com/",
$ wc -l results.json
3279 results.json
$ ls -l results.json
-rw-r--r-- 1 hiro hiro 569726 Mar 17 20:37 results.json
3279 行、約570kB の JSON 形式ファイルが取得できます。その中から主要な項目を抜き出したのが以下の JSON になります。適宜省略 & 順番を入れ替えています。
{
"lighthouseResult": {
"audits": {
"first-contentful-paint": {
"title": "First Contentful Paint",
"displayValue": "0.3 s",
"numericValue": 317,
"numericUnit": "millisecond"
},
"total-blocking-time": {
"title": "Total Blocking Time",
"displayValue": "0 ms",
"numericValue": 0,
"numericUnit": "millisecond"
},
"speed-index": {
"title": "Speed Index",
"displayValue": "1.0 s",
"numericValue": 978.21366366807433,
"numericUnit": "millisecond"
},
"largest-contentful-paint": {
"title": "Largest Contentful Paint",
"displayValue": "0.3 s",
"numericValue": 333.5,
"numericUnit": "millisecond"
},
"cumulative-layout-shift": {
"title": "Cumulative Layout Shift",
"displayValue": "0",
"numericValue": 1.9782856142194205e-05,
"numericUnit": "unitless"
},
},
"categories": {
"performance": {
"title": "Performance",
"score": 1,
}
},
},
}
私のサイトは設置してから間もない事とアクセス数が少ないので、実際のユーザーの環境で評価した結果 ("loadingExperience") は取得できません。直接測定した結果 ("lighthouseResult") のみです。
パフォーマンスの数値が "categories" 内の "performance" の "score" に、パフォーマンス指標の数値がそれぞれの項目の "numericValue" に記載されています。
数値の桁合わせは必要ですが、ウェブブラウザでアクセスしなくても、curl でパフォーマンスの数値を取得する事が出来ます。
指定可能なパラメータ
curl で googleapis を読み込む方法で指定できるパラメータは以下のページに記載されています。
よく使うパラメータは次の4項目でしょう。
- URL は必須
- category は指定しなければ performance のみ測定
- locale は必要なら日本語を指定 (ja)
- strategy には "mobile" か "desktop" を指定。デフォルトは "desktop"
例えば、日本語、mobile の結果を取得するのであれば、'&' で区切って続けて指定します。
curl "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://hiro20180901.com&locale=ja&strategy=mobile" > results_mobile.json
以下、参考にした記事です。
Python で複数のページの PageSpeed Insights の結果を取得する
curl と googleapis を使用する事で、コマンドラインで単一 URL の結果を取得する事ができました。少し時間を空けて測定すれば、複数サイトの結果も取得できます。
とはいえ、複数回の結果の平均値を求めたい場合等、そのまま JSON 形式のファイルだと扱いにくい所があります。Python を使用して何とかできないか調べてみました。
以下、Windows11 の WSL2 Ubuntu 22.04 LTS で実行しています。
psi-score-collector について
こちらの Zenn のページに、そのものズバリの Python スクリプトが紹介されていました。複数回の PageSpeed Insights の結果を平均して、複数ページの結果を表示する Python スクリプトです。
使ってみようかと思いましたが、上記のページの方法そのままでは実行できませんでした。
少し手直しすれば使用できましたので、手順を追って記載します。
PageSpeed Insights API Key の取得
psi-score-collector は PageSpeed Insights の結果の複数回の平均を計算するので、PageSpeed Insights API の Key を取得します。
「キーを取得する」ボタンを押すと次の画面が表示されます。project name を入力し、下のラジオボタンで「Yes」を選択して「NEXT」を押します。
「SHOW KEY」を押すと API Key が表示されますので、保管しておきます。(私は KeypassXC に入れて置きました)
これで PageSpeed Insights API Key の取得は終了です。
psi-score-collector のインストール
psi-score-collector は Poetry という Python のパッケージマネージャーを使用します。初めは psi-score-collector のページの手順通りに進めたのですが、どうしても Poetry のインストールに失敗しました。
Poetry のインストール方法について調べてみると、以下のページを見つけました。
こちらのページも参考にしながら、psi-score-collector をインストールしました。
# psi-score-collector を github から clone
$ git clone https://github.com/kotahashihama/psi-score-collector.git
# Poerty のインストール
$ curl -sSL https://install.python-poetry.org | python3 -
# ~/.local/bin/poetry がインストールされる。WSL2 bash の ~/.profile では PATH が通っていた
# 一旦 WSL2 を終了して再起動 (~/.profile の読み込み)
$ cd psi-score-collector/
$ rm poetry.lock # lock ファイルを削除
$ poetry config virtualenvs.in-project true
$ poetry install
Creating virtualenv psi-score-collector in /home/hiro/tmp/psi-score-collector/.venv
Updating dependencies
Resolving dependencies... (9.5s)
Writing lock file
Package operations: 7 installs, 0 updates, 0 removals
• Installing certifi (2022.12.7)
• Installing charset-normalizer (3.1.0)
• Installing idna (3.4)
• Installing urllib3 (1.26.15)
• Installing numpy (1.24.2)
• Installing python-dotenv (0.13.0)
• Installing requests (2.28.2)
$ cp .env.example .env
$ vim .env # PageSpeed Insights API Key を記入する
$ cat .env
API_KEY=XXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZZZ
Poetry のインストール元が変更されていましたので修正しました。また、clone した中に含まれる poetry.lock が存在しているとエラーが出ます。lock ファイルは自動生成されるので、削除してから poetry install を実行すれば Poetry をインストールできました。
実行結果は次の通りです。
$ poetry run python3 main.py
https://www.google.com/
https://www.yahoo.co.jp/
https://www.bing.com/
を測定中...(3回計測)
(1/3) https://www.google.com/
[ モバイル ]
60 60 60
平均 60.0 点(最低 60 点、最高 60 点)
(フィールドデータ) FCP: 0.8 s, LCP: 1.0 s, FID: 31.0 ms, CLS: 3.0
(ラボデータ) 累積レイアウト変更: 平均 0.0
[ パソコン ]
99 99 99
平均 99.0 点(最低 99 点、最高 99 点)
(フィールドデータ) FCP: 1.5 s, LCP: 1.6 s, FID: 9.0 ms, CLS: 0.0
(ラボデータ) 累積レイアウト変更: 平均 0.0
============================================================
(2/3) https://www.yahoo.co.jp/
[ モバイル ]
57 57 57
平均 57.0 点(最低 57 点、最高 57 点)
(フィールドデータ) FCP: 1.0 s, LCP: 1.8 s, FID: 31.0 ms, CLS: 2.0
(ラボデータ) 累積レイアウト変更: 平均 0.1
[ パソコン ]
92 92 92
平均 92.0 点(最低 92 点、最高 92 点)
(フィールドデータ) FCP: 0.9 s, LCP: 1.9 s, FID: 4.0 ms, CLS: 4.0
(ラボデータ) 累積レイアウト変更: 平均 0.0
============================================================
(3/3) https://www.bing.com/
[ モバイル ]
75 75 75
平均 75.0 点(最低 75 点、最高 75 点)
(フィールドデータ) FCP: 1.0 s, LCP: 1.6 s, FID: 20.0 ms, CLS: 0.0
(ラボデータ) 累積レイアウト変更: 平均 0.0
[ パソコン ]
75 75 75
平均 75.0 点(最低 75 点、最高 75 点)
(フィールドデータ) FCP: 1.9 s, LCP: 2.2 s, FID: 26.0 ms, CLS: 2.0
(ラボデータ) 累積レイアウト変更: 平均 0.1
============================================================
測定完了!
google と yahoo と bing の結果が表示されればインストールは成功です。
psi-score-collector スクリプトの修正
psi-score-collector のインストール直後の状態では、実際のユーザーの環境で評価した結果 ("loadingExperience") を表示します。しかし、前述のように私のサイトでは直接測定した結果 ("lighthouseResult") しか取得できません。
直接測定した結果 ("lighthouseResult") を表示するように main.py を修正しました。PageSpeed Insights のパフォーマンスで測定する数値を表示するように変更しています。
import config
import requests
import math
import numpy
from decimal import Decimal, ROUND_HALF_UP
# 測定回数
measurement_count = 3;
# 測定対象 URL
url_list = [
'https://hiro20180901.com/',
'https://hiro20180901.com/2023/01/30/use-litespeed-cache-on-wordpress/',
'https://hiro20180901.com/2023/02/25/use-quic-cloud-cdn-wordpress-lite-speed-cache-plugin/',
'https://hiro20180901.com/2023/03/02/wordpress-litespeed-cache-plugin-optimization/',
'https://hiro20180901.com/2023/03/04/wordpress-litespeed-cache-plugin-image-optimization/',
'https://hiro20180901.com/2023/03/08/wordpress-litespeed-cache-optimization-guest-mode-etc/'
]
api_url = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed'
api_key = config.API_KEY
payload = { 'key': api_key }
def calculate_average(scores):
score_average_raw = numpy.average(scores)
average_scores = {}
average_scores['main'] = Decimal(str(score_average_raw)).quantize(Decimal('0.001'), rounding=ROUND_HALF_UP)
average_scores['max'] = numpy.amax(scores)
average_scores['min'] = numpy.amin(scores)
return average_scores
def measure(device):
device_name = {
'mobile': 'Mobile',
'desktop': 'Desktop'
}
print(f'[ {device_name[device]} ]')
payload['strategy'] = device
url_name = api_url + "?url=" + url
scores = []
lab_fcp_scores = []
lab_tbt_scores = []
lab_si_scores = []
lab_lcp_scores = []
lab_cls_scores = []
for i in range(measurement_count):
result = requests.get(url_name, params = payload)
result_json = result.json()
result_score = result_json['lighthouseResult']['categories']['performance']['score']
displayed_score = math.floor(result_score * 100)
lab_data_scores = {}
lab_data_scores['fcp'] = result_json['lighthouseResult']['audits']['first-contentful-paint']['numericValue'] / 1000
lab_data_scores['tbt'] = result_json['lighthouseResult']['audits']['total-blocking-time']['numericValue'] / 1000
lab_data_scores['si'] = result_json['lighthouseResult']['audits']['speed-index']['numericValue'] / 1000
lab_data_scores['lcp'] = result_json['lighthouseResult']['audits']['largest-contentful-paint']['numericValue'] /1000
lab_data_scores['cls'] = result_json['lighthouseResult']['audits']['cumulative-layout-shift']['numericValue'] / 1000
lab_fcp_scores.append(lab_data_scores['fcp'])
lab_tbt_scores.append(lab_data_scores['tbt'])
lab_si_scores.append(lab_data_scores['si'])
lab_lcp_scores.append(lab_data_scores['lcp'])
lab_cls_scores.append(lab_data_scores['cls'])
scores.append(displayed_score)
print(displayed_score, end=' ')
average_scores = calculate_average(scores)
average_lab_fcp_scores = calculate_average(lab_fcp_scores)
average_lab_tbt_scores = calculate_average(lab_tbt_scores)
average_lab_si_scores = calculate_average(lab_si_scores)
average_lab_lcp_scores = calculate_average(lab_lcp_scores)
average_lab_cls_scores = calculate_average(lab_cls_scores)
print('\nAvg. {main} (min {min} max {max} )' \
.format(
main=average_scores['main'],
min=average_scores['min'],
max=average_scores['max']
))
print('(Labo Data)', end=' ')
print('FCP: {main} s'.format(main=average_lab_fcp_scores['main']), end=', ')
print('TBT: {main} s'.format(main=average_lab_tbt_scores['main']), end=', ')
print('SI : {main} s'.format(main=average_lab_si_scores['main']), end=', ')
print('LCP: {main} s'.format(main=average_lab_lcp_scores['main']), end=', ')
print('CLS: {main}'.format(main=average_lab_cls_scores['main']))
print('\n'.join(map(str, url_list)))
print(f' measuring...({measurement_count} times)')
for i, url in enumerate(url_list):
print(f'\n({i + 1}/{len(url_list)}) {url}')
measure('mobile')
measure('desktop')
print('\n' + '=' * 60)
print('\nFinish!!')
実行した結果は次の通りです。トップページ + LiteSpeed cache 関連のページをまとめて測定しました。
$ poetry run python3 main.py
https://hiro20180901.com/
https://hiro20180901.com/2023/01/30/use-litespeed-cache-on-wordpress/
https://hiro20180901.com/2023/02/25/use-quic-cloud-cdn-wordpress-lite-speed-cache-plugin/
https://hiro20180901.com/2023/03/02/wordpress-litespeed-cache-plugin-optimization/
https://hiro20180901.com/2023/03/04/wordpress-litespeed-cache-plugin-image-optimization/
https://hiro20180901.com/2023/03/08/wordpress-litespeed-cache-optimization-guest-mode-etc/
measuring...(3 times)
(1/6) https://hiro20180901.com/
[ Mobile ]
100 100 100
Avg. 100.000 (min 100 max 100 )
(Labo Data) FCP: 1.048 s, TBT: 0.000 s, SI : 2.615 s, LCP: 1.530 s, CLS: 0.000
[ Desktop ]
100 100 100
Avg. 100.000 (min 100 max 100 )
(Labo Data) FCP: 0.482 s, TBT: 0.006 s, SI : 0.787 s, LCP: 0.482 s, CLS: 0.000
============================================================
(2/6) https://hiro20180901.com/2023/01/30/use-litespeed-cache-on-wordpress/
[ Mobile ]
99 99 99
Avg. 99.000 (min 99 max 99 )
(Labo Data) FCP: 1.243 s, TBT: 0.000 s, SI : 1.943 s, LCP: 2.131 s, CLS: 0.000
[ Desktop ]
100 100 100
Avg. 100.000 (min 100 max 100 )
(Labo Data) FCP: 0.320 s, TBT: 0.000 s, SI : 0.875 s, LCP: 0.511 s, CLS: 0.000
============================================================
(3/6) https://hiro20180901.com/2023/02/25/use-quic-cloud-cdn-wordpress-lite-speed-cache-plugin/
[ Mobile ]
97 97 97
Avg. 97.000 (min 97 max 97 )
(Labo Data) FCP: 1.379 s, TBT: 0.093 s, SI : 2.014 s, LCP: 2.281 s, CLS: 0.000
[ Desktop ]
100 100 100
Avg. 100.000 (min 100 max 100 )
(Labo Data) FCP: 0.357 s, TBT: 0.000 s, SI : 0.833 s, LCP: 0.551 s, CLS: 0.000
============================================================
(4/6) https://hiro20180901.com/2023/03/02/wordpress-litespeed-cache-plugin-optimization/
[ Mobile ]
98 98 98
Avg. 98.000 (min 98 max 98 )
(Labo Data) FCP: 1.134 s, TBT: 0.010 s, SI : 1.576 s, LCP: 2.281 s, CLS: 0.000
[ Desktop ]
100 100 100
Avg. 100.000 (min 100 max 100 )
(Labo Data) FCP: 0.387 s, TBT: 0.000 s, SI : 0.696 s, LCP: 0.611 s, CLS: 0.000
============================================================
(5/6) https://hiro20180901.com/2023/03/04/wordpress-litespeed-cache-plugin-image-optimization/
[ Mobile ]
98 98 98
Avg. 98.000 (min 98 max 98 )
(Labo Data) FCP: 1.261 s, TBT: 0.010 s, SI : 1.370 s, LCP: 2.280 s, CLS: 0.000
[ Desktop ]
100 100 100
Avg. 100.000 (min 100 max 100 )
(Labo Data) FCP: 0.320 s, TBT: 0.000 s, SI : 0.735 s, LCP: 0.590 s, CLS: 0.000
============================================================
(6/6) https://hiro20180901.com/2023/03/08/wordpress-litespeed-cache-optimization-guest-mode-etc/
[ Mobile ]
98 98 98
Avg. 98.000 (min 98 max 98 )
(Labo Data) FCP: 1.123 s, TBT: 0.008 s, SI : 1.558 s, LCP: 2.431 s, CLS: 0.000
[ Desktop ]
100 100 100
Avg. 100.000 (min 100 max 100 )
(Labo Data) FCP: 0.324 s, TBT: 0.000 s, SI : 0.773 s, LCP: 0.631 s, CLS: 0.000
============================================================
Finish!!
これで複数 URL の複数回の PageSpeed Insights の結果の平均値を求める事が出来るようになりました。
QUIC.cloud CDN が使用できる状態で、PageSpeed Insights の結果は次の通りでした。
Page No. | 1 | 2 | 3 | 4 | 5 | 6 | Green Range |
Performance | 100 | 99 | 97 | 98 | 98 | 98 | 90-100 |
FCP | 1.048 | 1.243 | 1.379 | 1.134 | 1.261 | 1.123 | 0 - 1.800 |
TBT | 0.000 | 0.000 | 0.093 | 0.010 | 0.010 | 0.008 | 0 - 0.200 |
SI | 2.615 | 1.943 | 2.014 | 1.576 | 1.370 | 1.558 | 0 - 3.400 |
LCP | 1.530 | 2.131 | 2.281 | 2.281 | 2.280 | 2.431 | 0 - 2.500 |
CLS | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0 - 0.100 |
Page No. | 1 | 2 | 3 | 4 | 5 | 6 | Green Range |
Performance | 100 | 100 | 100 | 100 | 100 | 100 | 90-100 |
FCP | 0.482 | 0.320 | 0.357 | 0.387 | 0.320 | 0.324 | 0 - 1.800 |
TBT | 0.006 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0 - 0.200 |
SI | 0.787 | 0.875 | 0.833 | 0.696 | 0.735 | 0.773 | 0 - 3.400 |
LCP | 0.482 | 0.511 | 0.551 | 0.611 | 0.590 | 0.631 | 0 - 2.500 |
CLS | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0 - 0.100 |
- 2023年3月17日夜の結果です。QUIC.cloud CDN は有効で、残量が (ギリギリですが) 残っている状態です。
- Desktop は全て100、Mobile は 90後半で良好でした。
- トップページだけではなく、通常ページもパフォーマンスが高いレベルを維持できています。
- CLS が 0 なのは、個人的には好ましい所です。(無料版はてなブログで苦労した所ですので)
- PageSpeed Insights と QUIC.cloud CDN の間であれば、3回共に同じ結果となりましたので、複数回測定する意味はないかもしれません。
まとめ
複数の PageSpeed Insights の結果を取得する為に、PageSpeed Insights API を使用して Python スクリプトから一括で取得できるようにしました。JSON 形式の結果を受け取り、必要な項目のみを抜き出して集計する psi-score-collector を少し手直しして、直接測定した結果 ("lighthouseResult") を取得するようにしました。
Python で扱えるようにしておけば、Excel や Google SpreadSheed に記録させる事も出来ますし、別のサービスと連携させる事も可能でしょう。とりあえずはローカルの Windows11 WSL2 で実行していますが、そのうち LOLIPOP! レンタルサーバーの cron で定期実行させる事も考えています。
3月17日時点で、QUIC.cloud CDN の無料枠は 97% を使いました。3月後半は QUIC.cloud CDN の無料枠を使い切り、CDN を使用できなくなります。その際に、どの位のパフォーマンス低下が生じるか確認します。
また、来月は QUIC.cloud CDN の使用するリージョンを絞ってみようと考えています。Asia / Tokyo は North America の4倍のコストがかかります。North America <-> Asia / Tokyo の間の回線は CDN を使用しなくても速度が速いと期待できますし、日本語の記事の需要は日本国内が多いでしょうから、単価の高いリージョンは無効として1カ月様子を見たいと考えています。
今回のアイキャッチ画像
流氷を Stable Diffusion WebUI で生成しました。実物は見たことがありませんが、冬の北海道は一度は訪問してみたいです。北国育ちなので雪や寒さへの耐性は高めですが、関東での生活が長く鈍っています。
私のキーボードは Keychron K8 茶軸ですが、Keychron K8 Pro も良さそうです。QMK/VIA 対応なので、キーボード内にレイアウト変更を記憶できます。私も今購入するなら K8 Pro を選択します。
コメント