Reporting API v4を使用して、UAのバックアップを対応する

# はじめに
この記事を読んでいる皆さんは、UAのバックアップは終わりましたでしょうか？
UAは`2024/07/01`以降には、完全に削除されてしまいます。

なので、それまでにデータが必要であれば、バックアップする必要があります。
今回は以下APIを使用して、データをcsvに出力することをしたので、そのアウトプットとなります。

最終的には、出力したcsvをBigQueryにインポートするまで対応しますが、アウトプットはAPIの実装のみとなります。

https://developers.google.com/analytics/devguides/reporting/core/v4?hl=ja

:::note warn
この記事では、プロジェクトの作成方法や認証情報の取得に関しては、解説を行いません。
あくまで、APIの実装方法がメインとなります。
:::

# 前提
- サービスアカウント認証を利用
- 認証情報をJSONファイルは取得済み

Reporting API を利用するための準備は、以下記事を参照してください。

https://qiita.com/ryota-sasabe/items/a5efd2aac244cfcce5c7

# 他サービスの検討
- BigQueryエクスポート
- trocco
- スプレッドシートのアドオン

## BigQueryエクスポート
有料のGAを使用していれば、直接GAのデータをBigQueryにエクスポートしてくれる機能があります。
しかも、APIなどは加工されたデータしか取得できないですが、これは生データを取得することができます。

ただ使用しなかったのは、直近13ヶ月分のデータしか取得することができず、過去のデータ全てに対応することができなかったため、早々に検討から外しました。

https://support.google.com/analytics/answer/3416092?sjid=15184722854025583315-AP#zippy=%2C%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%AE%E5%86%85%E5%AE%B9

## trocco
ETLツールで、UAのデータを直接BigQueryに入れることができるので、非常に便利だと思います。
ただ、実行時間で課金がされるので、他事業で過去〇年分まで対応する...。となると、すごいことになってしまう予想をしました。

また、後ほど注意点にも記載しますが、サンプリングもあるため、troccoも検討から外しました。

## スプレッドシートのアドオン
アドオンも便利ですが、これもサンプリングが問題になるのと、サンプリングがかからない期間で取得しようとすると、すごい期間になったり、実行時間が長過ぎてしまうので、これも検討から外しました。

なので、

- サンプリングがかからない期間（1日単位）で、データを取得することができる
- 過去のデータも取得できる

主に、この2点でAPIしかないと判断して、実装を進めました。



# 実装

```rb
require 'googleauth'
require 'google/apis/analyticsreporting_v4'
require 'csv'
require 'time'

VIEW_ID = '111111111'
START_DATE = Date.parse('2018-01-01')
END_DATE = Date.parse('2018-12-31')
FORMAT_NAME = 'test'
PROPATY_NAME = 'propaty_test'
TRACKING_ID = 'UA-111111111-11'

def get_analytics_data(client, analytics, date_str, dimension, page_token = nil)
  date_range = analytics::DateRange.new(start_date: date_str, end_date: date_str)
  metric = [
    analytics::Metric.new(expression: 'ga:users', alias: 'users'),
    analytics::Metric.new(expression: 'ga:pageviews', alias: 'pageviews'),
    analytics::Metric.new(expression: 'ga:totalEvents', alias: 'total_events'),
    analytics::Metric.new(expression: 'ga:sessions', alias: 'sessions'),
    analytics::Metric.new(expression: 'ga:bounceRate', alias: 'bounce_rate'),
    analytics::Metric.new(expression: 'ga:exitRate', alias: 'exit_rate'),
    analytics::Metric.new(expression: 'ga:timeOnPage', alias: 'time_on_page'),
    analytics::Metric.new(expression: 'ga:newUsers', alias: 'new_users')
  ]
  report_request = analytics::ReportRequest.new(
    view_id: VIEW_ID, metrics: metric, dimensions: dimension, date_ranges: [date_range], page_size: 100000
  )
  report_request.page_token = page_token if page_token

  request = analytics::GetReportsRequest.new(report_requests: [report_request])
  client.batch_get_reports(request)
end

# CSV output
def save_to_csv(all_rows, response, date_str)
  filename = "#{VIEW_ID}/analytics_data_#{date_str}_#{FORMAT_NAME}.csv"
  dimension_header = response.reports.first.column_header.dimensions

  CSV.open(filename, 'w', force_quotes: true) do |csv|
    header = ['property_name', 'tracking_id', 'view_id'] + dimension_header + response.reports.first.column_header.metric_header.metric_header_entries.map { |entry| entry.name }
    csv << header

    all_rows.each do |row|
      dimensions = row.dimensions
      metrics = row.metrics.first.values

      csv << [PROPATY_NAME, TRACKING_ID, VIEW_ID] + dimensions + metrics
    end
  end

  puts "データが #{filename} に保存されました。"
end

# 実行開始時刻を記録
start_time = Time.now

scope = ['https://www.googleapis.com/auth/analytics.readonly']
analytics = Google::Apis::AnalyticsreportingV4

client = analytics::AnalyticsReportingService.new
client.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: File.open('test-analysis-abc123456789.json'),
  scope: scope
)

# 実行回数をカウントする変数
total_execution_count = 0
execution_count = 0

sleep_time = 30
execution_limit = 95
# 各日付ごとにデータを取得しCSVに保存
(START_DATE..END_DATE).each do |date|
  # 日付の文字列変換
  date_str = date.strftime('%Y-%m-%d')

  original_dimension = [
    analytics::Dimension.new(name: 'ga:date'),
    analytics::Dimension.new(name: 'ga:deviceCategory'),
    analytics::Dimension.new(name: 'ga:pagePath'),
    analytics::Dimension.new(name: 'ga:sourceMedium'),
    analytics::Dimension.new(name: 'ga:landingPagePath')
  ]

  client_dimension = [
    analytics::Dimension.new(name: 'ga:dimension27')
  ]

  dimension = original_dimension + client_dimension

  # データ取得
  response = get_analytics_data(client, analytics, date_str, dimension)

  all_rows = response.reports.first.data.rows
  puts response.reports.first.data.row_count

  while response.reports.first.next_page_token
    next_page_token = response.reports.first.next_page_token
    next_response = get_analytics_data(client, analytics, next_page_token)
    all_rows.concat(next_response.reports.first.data.rows)
    response = next_response
  end

  # データをCSVに保存
  save_to_csv(all_rows, response, date_str) unless all_rows.nil?
end

# 実行終了時刻を記録
end_time = Time.now
execution_time = end_time - start_time
puts "プログラムの実行時間: #{execution_time} 秒"

```

**ディレクトリ構成**
```
- VIEW_ID（フォルダ）
    - この配下にcsvが出力される
- script.rb
- test-analysis-abc123456789.json
```

## 環境
- ruby：3.2.2
- Gem：google-api-client
    - 3.4.10

```sh
gem install google-api-client
```

https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet?hl=ja

# 解説
複数のプロパティ・ビューを取得したい場合、主に定数を変更するだけで良いようにしています。

```rb
client = analytics::AnalyticsReportingService.new
client.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
  json_key_io: File.open('test-analysis-abc123456789.json'),
  scope: scope
)
```

上記で認証を行っています。
このrubyファイルと同じディレクトリに、事前に作成したjsonファイルを置いていただければ、問題ございません。

```rb
report_request = analytics::ReportRequest.new(
  view_id: VIEW_ID, metrics: metric, dimensions: dimension, date_ranges: [date_range], page_size: 100000
)
report_request.page_token = page_token if page_token
```

APIは、最大行数を超えると、別ページとなり、再度リクエストを送らないと取得することができません。
今回は、`page_size: 100000`としているので、この行よりも多く返ってきてしまった場合も、もう一度リクエストを走らせて取得できるよう実装しています。

あとは、利用制限を気をつけつつ、メトリクス・ディメンションを設定していただければ、csvに出力できるかなと思います。


# 注意点
## 1日の利用制限
https://developers.google.com/analytics/devguides/reporting/core/v4/limits-quotas?hl=ja

利用制限を超えてしまうと、データが取得できなかったり、エラーになってしまいます。
ここはしっかり読んで、把握しておきましょう。

## サンプリングがかかってしまう
https://support.google.com/analytics/answer/2637192?hl=ja#zippy=%2C%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%AE%E5%86%85%E5%AE%B9

長い期間を設定して、一気にデータを取得しようとすると、GA側で全てのデータの中からある程度のデータで計算されてしまう仕様がサンプリングになります。
そのため、1年間を一気に取得しようとすると、1日ずつ取得したデータと差異を生じることがあります。

なので、1日ずつ取得するよう、APIは実装しています。


# まとめ
**データを取得するだけ**ですが、考慮しないといけない点が多く、とても時間がかかる作業です。
今回アウトプットした実装を例に、皆様がやりやすいように修正して、使っていただけたら幸いです。

最後までご覧いただき、ありがとうございました。