【GAS】GASでPSIスコアの半自動計測をやってみた

## はじめに
CoreWebVitalsが重要視される昨今、担当サイトの全ページ1つずつ計測を行うのは面倒、、。
そんな思いからCWVを半自動で全ページ測定できるGASを作成してみました。
その方法について備忘録程度に記載します。

## できるもの
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/0cf5aa4c-6c39-28b6-0519-ecdf8972f287.png)


## 用意するもの
+ Google SpreadSheet（以下スプシ）
+ 測定したいリンク

## 作成手順
### 1, スプシにリンク入力用のシートを作成
シートに計測対象を記入する表を作成します。
見出しは`No` `URL` `device`の3項。

`device`には今後`desktop`、`mobile`のどちらかが入力されますのでプルダウンリストにしてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/a7cfc1d7-fc7b-12f9-2d8d-82e44368ec34.png)
シート名を「計測対象」に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/403311a5-400d-3637-3b98-dae953a84e52.png)

### 2, 計測結果を出力するテンプレートを作成
PSIスコアの計測結果を出力する表を作成します。
見出しは`計測日時` + `各計測を行う項目` + `URL` + `device`とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/c4593ec6-34a9-2954-3105-49978359396e.png)
シート名を「temp」に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/4efe0ee2-f3e4-673e-2f65-ae102016b434.png)

### 3, GASにてPSIの計測処理を作成
PSIのAPI KEYを取得します。
下記にアクセスして`キーを取得`をクリックし、必要に応じてプロジェクトを作成します。
https://developers.google.com/speed/docs/insights/v5/get-started?hl=ja
取得したAPI_KEYはGASで使用しますのでどこかに保存しておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/418d057a-27d6-093e-a680-8c81a01523a2.png)

スプシの拡張機能から`AppScript`を選択し、GASの編集画面へと遷移します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/ed9f22ef-d8aa-7fe4-f1b2-b2868195afad.png)
画面が開いたらデフォルトで記載されている内容は削除して下記のコードを張り付けます。
1行目のAPI_KEYは取得したものに変更してください。
```javascript:GAS.gs
// 取得したAPIキーを設定
const API_KEY = '■■■■■';

const SS = SpreadsheetApp.getActiveSpreadsheet();
const COPY_SHEET = SS.getSheetByName('temp');

function onOpen(){
  let ui = SpreadsheetApp.getUi();
  let menu = ui.createMenu('GAS');
  menu.addItem('自動計測','startTimeSpan');
  menu.addToUi();
}
 
function startTimeSpan() {
  let date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd');
  let tmpSheet = null;
  if(SS.getSheetByName(date)){
    tmpSheet = SS.getSheetByName(date);
  }else{
    tmpSheet = COPY_SHEET.copyTo(SS);
    tmpSheet.setName(date);
  }
 
  exec(tmpSheet);
}
 
function exec(sheet) {
  let array = getURL();
  let count = array.length;
  for(let i = 0; i < count; i++){
    let request = setQuery(array[i]);
    let response = [
      UrlFetchApp.fetch(request, {
        muteHttpExceptions: true
      })
    ];

    let row = sheet.getLastRow();
    sheet.insertRowAfter(row);
  
    let date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'HH:mm');
    sheet.getRange(row+1, 1).setValue(date);
  
    let result = JSON.parse(response[0].getContentText());
    let convertedResult = convertToResult(result,array[i]);
  
    sheet.getRange(row+1, 2, 1, convertedResult[0].length).setValues(convertedResult);
  }
}
 
// 取得データの変換
function convertToResult(content,url) {
  let result_array = [[]];

  // ラボデータ
  let labo_data = content.lighthouseResult.audits;
  result_array[0].push(labo_data['first-contentful-paint'].displayValue);
  result_array[0].push(labo_data['interactive'].displayValue);
  result_array[0].push(labo_data['speed-index'].displayValue);
  result_array[0].push(labo_data['total-blocking-time'].displayValue);
  result_array[0].push(labo_data['largest-contentful-paint'].displayValue);
  result_array[0].push(labo_data['cumulative-layout-shift'].displayValue);

  // フィールドデータ
  let field_data = content.loadingExperience.metrics;
  result_array[0].push(field_data.FIRST_CONTENTFUL_PAINT_MS.percentile / 1000);
  result_array[0].push(field_data.FIRST_INPUT_DELAY_MS.percentile);
  result_array[0].push(field_data.LARGEST_CONTENTFUL_PAINT_MS.percentile / 1000);
  result_array[0].push(field_data.CUMULATIVE_LAYOUT_SHIFT_SCORE.percentile / 100);
  result_array[0].push(url[0]);
  result_array[0].push(url[1]);

  return result_array;
}
 
function setQuery(url) {
  const API_URL = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed';
 
  let parameters = {
    url: encodeURIComponent(url[0]),
    key: API_KEY,
    strategy: url[1]
  };
 
  let query = API_URL + '?';
  for (key in parameters) {
    query += '&' + key + '=' + parameters[key];
  }
 
  return query;
}

function getURL(){
  let urlSheet = SS.getSheetByName('計測対象');
  
  let returnArray = [];
  let row = urlSheet.getLastRow() -1;
  for(let i = 0; i < row; i++){
    if(urlSheet.getRange(i + 2, 2).getValue() != ""){
      let tmp = [urlSheet.getRange(i + 2, 2).getValue(),urlSheet.getRange(i + 2, 3).getValue()];
      returnArray.push(tmp);
    }
  }
  return returnArray;
}
```

### 4, GASの実行をする
スプシに戻り、ページリロードをするとタブに`GAS`が表示されます。
計測対象シートに計測したいページのURLを`URL`に、`device`にはdesktopまたはmobileを入力してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/014dd9d6-6136-7948-a7b2-0af7678d8116.png)

上記が完了したら`GAS`>`自動計測`を選択しGASを実行します。
※初回実行時はアカウントの許可が表示されますが全て許可して下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/c573f63d-eb13-757a-cc93-f9d4262725da.png)

実行日付のシートが作成され、各スコアが書き込まれたら完了になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2348088/0cf5aa4c-6c39-28b6-0519-ecdf8972f287.png)

## おわりに
やはり手間が減るというのは魅力的ですね。
ただ使ってみた問題として、GASでは1回でのスクリプトの最大実行時間が決められている為、一度に大量の画面の測定を行おうと思うとスクリプトが停止してしまいます。キャッシュとかを使うことで実行行数の記憶、2回目実行時に続きの行から実行させるとかで回避できる気がするので、今後時間があればアップデートしてみようかと思います。