Data Export API から CSV 形式へのデータの出力

Alexander Lucas、Google アナリティクス API チーム - 2010 年 8 月


はじめに

この記事では、Google アナリティクス Data Export API に対するクエリからデータを取り出して、その結果を一般的な CSV 形式に出力する方法について説明します。これは、Data Export API から取り出したアナリティクス データで最も頻繁に行われる作業の 1 つのため、このプロセスを自動化することで多くの時間を節約できます。さらに、クエリから CSV ドキュメントを出力するコードを作成しておくと、これをカスタム ダッシュボードの自動レポート生成、メール、エクスポート機能などの大きなプロジェクトに統合できるようになります。

始める前に

この記事を最大限に活用するには、次の要件を満たしている必要があります。

  • Java の実践的な知識。
  • ディメンションと指標の理解をはじめとする Google アナリティクスの実践的な知識。
  • 実際のデータがある有効な Google アナリティクス アカウントへのアクセス権。
  • Data Export API Java スタートガイドの知識。この記事では、スタートガイドで説明されている内容をすべて理解していることを前提としています。
  • ソースコード全体のローカルコピー。AnalyticsCvsPrinter.java で入手できます。このコードを使用したサンプル アプリケーションは AnalyticsCsvDemo.java にあります。

プログラムの概要

この記事で扱うコードでは、次を実行します。

  1. コードをコンソールに出力するかファイル ストリームに出力するかを実行時に選択できるようにする。
  2. DataFeed オブジェクトをパラメータとして指定し、データを CSV 形式で出力します。
    • 行見出しを出力する。
    • データ行を出力します。各 DataEntry が結果の出力で 1 行を構成します。
    • CSV 互換の出力を得るために各値に対してサニタイズ メソッドを実行する。
  3. すべての入力を CSV 互換にするサニタイズ メソッドを記述する。
  4. どのような Data Export API クエリでも CSV ファイルに変換できる Java クラスを提供する。

トップへ戻る

カスタマイズ可能な出力ストリームの準備

まず、クラスの出力先となるカスタマイズ可能な出力ストリームを設定します。これにより、このクラスを使用しているコードで、標準出力に出力するかファイルに直接出力するかを決定できます。ここで必要なのは、PrintStream オブジェクトのゲッター/セッター メソッドを設定することだけです。それが、このクラスによって行われるすべての出力のターゲットになります。

private PrintStream printStream = System.out;

public PrintStream getPrintStream() {
  return printStream;
}

public void setPrintStream(PrintStream printStream) {
  this.printStream = printStream;
}

出力をファイルに設定するのも簡単です。そのファイルの PrintStream オブジェクトを作成する際に必要なのはファイル名だけです。

FileOutputStream fstream = new FileOutputStream(filename);
PrintStream stream = new PrintStream(fstream);
csvprinter.setPrintStream(stream);

トップへ戻る

データのイテレーション

CSV ファイルの 1 行目には列名が並んでいます。各列はデータフィードのディメンションまたは指標を表します。この最初の行を出力する手順は次のとおりです。

  1. フィードから最初のエントリを取り込みます。
  2. そのエントリの getDimensions メソッドを使用して、ディメンションのリストを反復処理します。
  3. Dimension.getName() メソッドを使用して、各ディメンションの名前を出力し、その後にカンマを付けます。
  4. getMetrics() メソッドを使用して、指標について同じことを行います。最後を除いて、すべての統計情報の後ろにカンマを出力します。

次に示すのは、行見出しを出力する方法の一例です。このコードでは、完全な行を表す文字列は返されません。これは値の処理時に出力ストリームに出力されます。

public void printRowHeaders(DataFeed feed) {
    if(feed.getEntries().size() == 0) {
      return;
    }

    DataEntry firstEntry = feed.getEntries().get(0);

    Iterator<Dimension> dimensions = firstEntry.getDimensions().iterator();
    while (dimensions.hasNext()) {
      printStream.print(sanitizeForCsv(dimensions.next().getName()));
      printStream.print(",");
    }

    Iterator<Metric> metrics = firstEntry.getMetrics().iterator();
    while (metrics.hasNext()) {
      printStream.print(sanitizeForCsv(metrics.next().getName()));
      if (metrics.hasNext()) {
        printStream.print(",");
      }
    }
    printStream.println();
  }

CSV ファイルの本文(列名の行の下にあるすべて)の出力は上記のものによく似ています。主な違いは 2 つだけです。第一に、評価されるのは最初のエントリだけではありません。コードはフィード オブジェクトの全エントリをループする必要があります。次に、サニタイズして出力する値を getName() メソッドを使用して取得するのではなく、getValue() を使用します。

public void printBody(DataFeed feed) {
    if(feed.getEntries().size() == 0) {
      return;
    }

    for (DataEntry entry : feed.getEntries()) {
      printEntry(entry);
    }
  }

  public void printEntry(DataEntry entry) {
    Iterator<Dimension> dimensions = entry.getDimensions().iterator();
    while (dimensions.hasNext()) {
      printStream.print(sanitizeForCsv(dimensions.next().getValue()));
      printStream.print(",");
    }

    Iterator<Metric> metrics = entry.getMetrics().iterator();
    while (metrics.hasNext()) {
      printStream.print(sanitizeForCsv(metrics.next().getValue()));
      if (metrics.hasNext()) {
        printStream.print(",");
      }
    }
    printStream.println();
  }

このコードはフィードをエントリに分割し、エントリを出力値に分割します。しかし、それらの値をどのように CSV 互換性のあるものにできるでしょう。たとえば、「カンマ区切り値」ファイルの値の中にカンマが含まれている場合、そのような値はサニタイズする必要があります。

トップへ戻る

CSV 互換にするためにデータをサニタイズする方法

CSV はわかりやすい形式です。CSV ファイルはデータ表を表し、各行がその表内の行を指します。行の値はカンマで区切られ、新しい行は、データの新しい行を意味します。

しかし、このシンプルな形式では、不良データにより混乱を招きがちです。たとえば、値にカンマが含まれていたり、改行が入っていたり、カンマと値の間にスペースが入っていたりする場合などが考えられますが、これらは簡単なルールを使って解決できます。

  • 文字列に二重引用符が含まれている場合は、二重引用符をもう 1 つ付けてエスケープします。
  • 文字列にカンマがある場合は、文字列全体を二重引用符で囲みます(既に二重引用符がある場合を除く)。
  • 文字列に改行がある場合は、文字列全体を二重引用符で囲みます(既に二重引用符がある場合を除く)。
  • 文字列がスペースで始まるか終わる場合は、文字列全体を二重引用符で囲みます(既に二重引用符がある場合を除く)。

この時点で値がどのように見えるかを想像することは難しいかもしれないので、次に例を挙げます。例はそれぞれ 1 つの値を表し、その値に応じてエスケープしています。わかりやすいように、スペースは「_」で示します。

エスケープ前 エスケープ後
unchanged unchanged
random " doublequote random "" doublequote
comma,separated "comma,separated"
2
「2
行」
_leading space, and a comma "_leading space, and a comma"
"leading quote, comma """leading quote, comma"
_スペース、カンマ
2 行目、二重引用符"
"_スペース、カンマ
2 行目、二重引用符"""

これらすべての条件を処理する最も簡単な方法は、サニタイズ メソッドを作成することです。問題のあるデータが入力されても、適切でクリーンな CSV 値が出力されます。そうしたメソッドを実装する方法の一例を次に示します。

private String sanitizeForCsv(String cellData) {
  StringBuilder resultBuilder = new StringBuilder(cellData);

  // Look for doublequotes, escape as necessary.
  int lastIndex = 0;
  while (resultBuilder.indexOf("\"", lastIndex) >= 0) {
    int quoteIndex = resultBuilder.indexOf("\"", lastIndex);
    resultBuilder.replace(quoteIndex, quoteIndex + 1, "\"\"");
    lastIndex = quoteIndex + 2;
  }

  char firstChar = cellData.charAt(0);
  char lastChar = cellData.charAt(cellData.length() - 1);

  if (cellData.contains(",") || // Check for commas
      cellData.contains("\n") ||  // Check for line breaks
      Character.isWhitespace(firstChar) || // Check for leading whitespace.
      Character.isWhitespace(lastChar)) { // Check for trailing whitespace
      resultBuilder.insert(0, "\"").append("\""); // Wrap in doublequotes.
  }
    return resultBuilder.toString();
}

このメソッドはまず、既存の二重引用符をチェックします。これは、文字列を二重引用符で囲む必要があるため、他のすべてのチェックの前に行う必要があります。また、値の一部である二重引用符と、このメソッドで以前に追加された二重引用符の違いを特定するのが面倒になります。値に含まれる二重引用符は、倍にすることで簡単にエスケープできます。たとえば、""" に、"""""" になります。

既存の二重引用符のチェックが済んだら、残されているスペース、カンマ、改行をチェックできます。これらの場合は、値を二重引用符で囲むだけで処理は完了です。

上記では StringBuilder オブジェクトを使用していますが、未加工の文字列を直接操作していません。これは、StringBuilder を使用すると、メモリに一時コピーを作成せずに文字列を自由に操作できるためです。Java の文字列は変更できないため、少しでも調整すると新しい文字列が作成されます。そのため、スプレッドシートのデータを操作するうちに、たちまち膨大な量になってしまいます。

行数 x 1 行あたりの値 x 値の変更 = 作成される新しい文字列
10,000 10 3 300,000

トップへ戻る

次のステップ

あとは金色のハンマーを手にしたので、ネイルに挑戦するのはごく自然なことです。以下に、おすすめのアイデアをご紹介します。

  • このクラスを使用してサンプルクエリから CSV ファイルを出力するサンプル アプリケーションのソース コードをご覧ください。出力ファイル名をコマンド ライン パラメータとして使用し、デフォルトでは標準出力に出力されます。これをたたき台にして、斬新なアプリケーションを作成してください。
  • CSV は数多くある形式の 1 つにすぎません。クラスを調整して、TSV、YAML、JSON、XML などの別の形式に出力することも可能です。
  • CSV を生成し、完了時にメールを送信するアプリケーションを作成してみましょう。月間レポートの自動化も簡単です。
  • 対話形式でクエリを入力できるアプリケーションを作成すると、強力なインターフェースでデータ内を探索できます。