GA4のストリーム情報一覧をGASでスプレッドシートに出力する

自分のGoogleアカウントが権限を持っている全GA4プロパティが持つストリーム情報をGoogle Apps Scriptを使ってGoogleスプレッドシートに出力する方法について。

この方法でGA4計測タグ設定の測定IDから対応するプロパティを見つけることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/94726f6e-54b4-a578-20e1-e8747dd2322a.png)


## 背景
GA4の計測タグ設定はデータストリームの測定ID情報を持っていますがプロパティに関する情報を持っておらず、管理画面からも検索できないため、GA4計測タグがデータを送信しているプロパティが分からなくなってしまう場合があります。
GA4側の管理画面の機能アップデートが期待されますが、それまでは [Google Analytics Admin API](https://developers.google.com/analytics/devguides/config/admin/v1) を叩いて情報を抽出する必要があります。
本記事で紹介する方法ではこのAPIをGASから叩いて結果をGoogleスプレッドシートに出力します。


**GA4計測タグ**
データストリームの測定ID情報しか持たない。

```html:
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-XXXXXXXXXX');
</script>
```

執筆時点でGA4の管理画面から測定IDを使ったプロパティの検索は不可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/9d7b639f-5776-dc6e-fd70-318517944dd9.png)


## 使い方
### 1. 準備
まず、ブラウザに複数のGoogleアカウントでログインしている場合はGASを編集する前に予めGoogleアカウントからログアウト、GA4のストリーム情報出力を行いたいGoogleアカウントのみでログインしている状態にします。
これはマルチログイン状態だとGASを編集するページが以下のようなエラーにより正常に開けない場合があるためです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/49f8eb77-3f75-4fde-695d-68531780532c.png)


#### Googleスプレッドシートの準備
新しいスプレッドシートにGoogle Sheetを作成し、結果を出力したいシートに `result`というシート名を付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/850e396b-88a4-a978-4e06-7e31b261dcde.png)

あとで使うのでこのSheetのURLの `/d/` の後に続く英数字（シートID）をどこかにメモして下さい。

`https://docs.google.com/spreadsheets/u/1/d/[[ここの半角英数字]]/edit`

#### GASの準備
上部の「拡張機能」>「App Script」からGASの編集画面を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/a898f949-5c6b-2b49-2915-efd632f8fe9c.png)

左側メニューの「サービス」の隣のプラスボタンをクリックして、
リストから「Google Analytics Admin API」を選択して「追加」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/b146c4e5-60b7-879b-f5df-c8d3f7f10359.png)

既に入力されている `function myFunction() {   }`のコードをすべて消して、代わりに以下コードを貼り付けます。

```javascript:
function main() {
  const FILE_ID = 'INSERT_YOUR_SHEET_ID_HERE';
  const SHEET_NAME = 'result';
  const resultSheet = SpreadsheetApp.openById(FILE_ID).getSheetByName(SHEET_NAME);

  resultSheet.clear();

  const startTime = new Date();
  let results = [];
  let accountSummaryNextPageToken = undefined;
  while(true){
    let accounts = getAccountSummaries(100, accountSummaryNextPageToken);
    let result = [];
    if(accounts !== null){
      result = getGa4Streams(accounts) || [];
      results.push(...result);
    }
    console.log(`accountSummaries.list > accounts.nextPageToken: ${accounts.nextPageToken}`);
    let elapsedTime = parseInt((new Date() - startTime) / 1000); // GASは6分以上連続で動作できないため経過時間チェック
    if(elapsedTime >= 300){  // 5分経過したら処理を強制終了させる
      console.log('アカウントが多すぎるため、処理が中断されました');
      break;
    }
    if(result.length = 0 || typeof accounts.nextPageToken === 'undefined'){
      break;
    }
    accountSummaryNextPageToken = accounts.nextPageToken;
  }

  const resultHeaders = Object.keys(results[0]);
  const resultBody = results.map((row) => {
    return resultHeaders.map((key) => row[key] || '');
  });
  const resultTable = [resultHeaders].concat(resultBody);
  const tableRange = resultSheet.getRange(1, 1, resultTable.length, resultTable[0].length);
  tableRange.setValues(resultTable);
  tableRange.applyRowBanding();
  console.log('スプレッドシートへの出力が完了しました');
}

function getAccountSummaries(pageSize=200, nextPageToken) {
  try {
    const params = {
      pageSize: pageSize,
      pageToken: nextPageToken
    }
    const accounts = AnalyticsAdmin.AccountSummaries.list(params);
    if (!accounts.accountSummaries || !accounts.accountSummaries.length) {
      console.log('No accounts found.');
      return null;
    }
    return accounts;
  } catch (e) {
    console.log('getAccountSummaries failed with error: %s', e.error);
  }
}

function getGa4Streams(accounts) {
  let results = [];
  try {
    for(const account of accounts.accountSummaries){
      const accountId = account.name.split('/')[1];
      console.log(`${accountId} : ${account.displayName}`);
      if (account.propertySummaries) {
        properties = AnalyticsAdmin.Properties.list({filter: 'parent:' + account.account});
        if (properties.properties !== null) {
          for(const property of properties.properties){
            const propertyID = property.name.replace('properties/','');
            const streams = AnalyticsAdmin.Properties.DataStreams.list(property.name);
            console.log(`  └ ${propertyID} :  ${property.displayName}`);
            if(typeof streams.dataStreams !== 'undefined') {
              for(const dataStream of streams.dataStreams) {
                let result = {
                  'accountId': accountId,
                  'accountName': account.displayName,
                  'propertyId': propertyID,
                  'propertyName': property.displayName,
                  'streamId': dataStream.name,
                  'streamName': dataStream.displayName,
                  'streamType': dataStream.type,
                  'streamCreateTime': dataStream.createTime,
                  'streamAndroidFirebaseAppId': dataStream.androidAppStreamData && dataStream.androidAppStreamData.firebaseAppId,
                  'streamAndroidPackageName': dataStream.androidAppStreamData && dataStream.androidAppStreamData.packageName,
                  'streamIosBundleId': dataStream.iosAppStreamData && dataStream.iosAppStreamData.bundleId,
                  'streamWebMeasurementId': dataStream.webStreamData && dataStream.webStreamData.measurementId,
                  'streamWebDefaultUrl': dataStream.webStreamData && dataStream.webStreamData.defaultUri
                };
                console.log(`    └ ${result.streamId} : ${result.streamName}`);
                results.push(result);
              }
            }
          }
        }
      }
    }
    return results;
  } catch (e) {
    console.log('getGa4Streams failed with error: %s', e.error);
  }
}
```

コードを貼り付けたら、 `INSERT_YOUR_SHEET_ID_HERE`の文字列を先ほどメモしたスプレッドシートのURL内に登場した文字列で書き換えた上で、フロッピーディスクアイコンをクリックして「保存」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/32c05486-253f-f258-7c6b-a3bd899dc305.png)


#### GASの実行
実行する関数に「main」が選ばれていることを確認、選ばれていなければ「main」に変更の上、「実行」ボタンをクリックしてコードの実行を開始します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/67512c4f-4cc9-1897-8656-02787fd2710a.png)

初回実行時のみ、承認およびスクリプトを信用していいか尋ねられるので許可します。

「権限を確認」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/a4f72e0e-0718-ddab-52f2-c30424e8bb7b.png)
アカウントを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/6ee2cccd-b55d-b571-3629-e77a1bd9e6cb.png)
自作スクリプトに対して警告が出るので「詳細」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/ecbeb179-d936-12d6-9393-7d940a0469a8.png)
「…に移動」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/2a390a65-2e95-1ed4-a304-e7b95a35db9a.png)
「許可」をクリックしてコードを実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/78732af4-b6b4-d0a0-af9e-910d2659d696.png)

実行ログウィンドウが表示されてぐるぐる処理し始めるので待機。
アカウントの量によっては実行完了まで数分掛かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/a23ba765-374d-5ba3-17b3-f764b6adc7d3.png)

#### GASの実行結果

実行ログに「実行完了」と表示されたらスプレッドシートにストリームの情報が出力されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/94726f6e-54b4-a578-20e1-e8747dd2322a.png)

ウェブストリームの測定IDから対応するプロパティを調べたいときはstreamWebMeasurementId列から対応する測定IDを探せばOKです。

##### うまく動かない場合
シート名の書き換えやコード内の`INSERT_YOUR_SHEET_ID_HERE`の書き換えを行ったか確認して下さい。
また、GASの制限により本ツールは5分で処理を打ち切るように設定しているため、あまりに紐づいているアカウント数が多いと一部のアカウント情報を取得できない場合があります。

## メモ: GTMでGA4タグ設定を行うとき
対応するプロパティが分からなくなると上記のような面倒なことをしないといけなくなるので、予め設定時にメモにプロパティの情報を残すことをオススメします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/e4fa2894-e0c2-9177-a0a1-f621186e03ec.png)