Java:Analytics APIを使う、その1

JavaプログラムからAnalytics APIを使い、Analyticsのデータにアクセスする。基本的には、はじめてのアナリティクス API: サービス アカウント向け Java クイック スタートに従うのだが、少々この文書は古く、2019/5時点では正しく動作しない。

この修正も含めて説明していく。

ただし、今回の内容としては非常にわずかなものだ。ほとんどJavaプログラムからAnalyticsに接続できるというレベルにすぎない。

やりたいこと

やりたいこととしては単純だ。普段はブラウザを使い目視で確認しているGoogle Analyticsの内容を何らかのソフトウェアによって自動処理したいということだ。

Analyticsの内容をデータとして取得して自動処理できれば、より便利になる(かもしれない)。これをJavaプログラムから行う。

プログラム以前に必要なこと

プログラムを動作させる以前に様々必要なことがある。まず最初に「このプログラムを閲覧者として認識させる」ために必要な措置があるのだ。それは、

  1. Google Cloud Platform(GCP)でプロジェクトを作成する。
  2. GCPプロジェクト内にサービスアカウントを作成し、そのメアドとキーを取得する。
  3. このメアドを対象とするAnalyticsに「閲覧者」として登録する。
  4. GCPプロジェクト内にてAnalytics APIを有効にする。

1、2、3については、Googleのサービスアカウントを作成するで説明しているので参照されたい。ここで、メアドとキーが取得されるのだが、これは特にAnalyticsに限ったことではなく、あらゆるGoogle APIで共通の話だ。

4については次で説明する。

GCPプロジェクト内にてAnalytics APIを有効にする

サービスアカウントを作成したプロジェクトにおいてAnalyticsのAPIを有効状態にする。なぜわざわざ「有効状態」にしなければならないのか(デフォルトで有効になってればいいじゃないか)と思うが、これはおそらくAPIによっては課金制のものもあるからだろう。それらもデフォルトで有効になっていたら、ユーザとしてはたまらない。

ともあれ、以下の手順で有効にする。

検索する。おそらくはAnalytics APIの方。

単純に有効にすればいい。それだけだ。

Javaプログラムを作成する

最初に説明したように、はじめてのアナリティクス API: サービス アカウント向け Java クイック スタートを参考にするのだが、しかし古いので2019/5時点では以下のようにする。

まずライブラリをダウンロードする必要などはない。Mavenリポジトリにある。Gradleであれば以下だ。

もちろん、「キーファイル」の部分はキーファイルのパスを指定しなければいけない。

compile group: 'com.google.apis', name: 'google-api-services-analytics', version: 'v3-rev161-1.25.0'

そして、改善したJavaプログラムソースは以下である。

import java.io.*;
import java.security.*;

import com.google.api.client.googleapis.auth.oauth2.*;
import com.google.api.client.googleapis.javanet.*;
import com.google.api.client.http.*;
import com.google.api.client.json.*;
import com.google.api.client.json.jackson2.*;
import com.google.api.services.analytics.*;
import com.google.api.services.analytics.model.*;

/**
 * A simple example of how to access the Google Analytics API using a service
 * account.
 */
public class HelloAnalytics {

  private static final String APPLICATION_NAME = "Hello Analytics";
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();
  private static final String KEY_FILE_LOCATION = "キーファイル";
  public static void main(String[] args) {
    try {
      Analytics analytics = initializeAnalytics();

      String profile = getFirstProfileId(analytics);
      System.out.println("First Profile Id: "+ profile);
      printResults(getResults(analytics, profile));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  /**
   * Initializes an Analytics service object.
   *
   * @return An authorized Analytics service object.
   * @throws IOException
   * @throws GeneralSecurityException
   */
  private static Analytics initializeAnalytics() throws GeneralSecurityException, IOException {

    HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    GoogleCredential credential = GoogleCredential
        .fromStream(new FileInputStream(KEY_FILE_LOCATION))
        .createScoped(AnalyticsScopes.all());

    // Construct the Analytics service object.
    return new Analytics.Builder(httpTransport, JSON_FACTORY, credential)
        .setApplicationName(APPLICATION_NAME).build();
  }

  private static String getFirstProfileId(Analytics analytics) throws IOException {
    // Get the first view (profile) ID for the authorized user.
    String profileId = null;

    // Query for the list of all accounts associated with the service account.
    Accounts accounts = analytics.management().accounts().list().execute();

    if (accounts.getItems().isEmpty()) {
      System.err.println("No accounts found");
    } else {
      String firstAccountId = accounts.getItems().get(0).getId();

      // Query for the list of properties associated with the first account.
      Webproperties properties = analytics.management().webproperties()
          .list(firstAccountId).execute();

      if (properties.getItems().isEmpty()) {
        System.err.println("No Webproperties found");
      } else {
        String firstWebpropertyId = properties.getItems().get(0).getId();

        // Query for the list views (profiles) associated with the property.
        Profiles profiles = analytics.management().profiles()
            .list(firstAccountId, firstWebpropertyId).execute();

        if (profiles.getItems().isEmpty()) {
          System.err.println("No views (profiles) found");
        } else {
          // Return the first (view) profile associated with the property.
          profileId = profiles.getItems().get(0).getId();
        }
      }
    }
    return profileId;
  }

  private static GaData getResults(Analytics analytics, String profileId) throws IOException {
    // Query the Core Reporting API for the number of sessions
    // in the past seven days.
    return analytics.data().ga()
        .get("ga:" + profileId, "7daysAgo", "today", "ga:sessions")
        .execute();
  }

  private static void printResults(GaData results) {
    // Parse the response from the Core Reporting API for
    // the profile name and number of sessions.
    if (results != null && !results.getRows().isEmpty()) {
      System.out.println("View (Profile) Name: "
        + results.getProfileInfo().getProfileName());
      System.out.println("Total Sessions: " + results.getRows().get(0).get(0));
    } else {
      System.out.println("No results found");
    }
  }
}

本プログラムで出力される内容は、わずかに以下のようなものだ。

First Profile Id: 15......
View (Profile) Name: すべてのウェブサイトのデータ
Total Sessions: 7422