Google アナリティクス API チーム Alexander Lucas – 2010 年 8 月
はじめに
この記事では、Google アナリティクス Data Export API に対するクエリからデータを取り出して、その結果を一般的な CSV 形式に出力する方法について説明します。これは、Data Export API から取り出したアナリティクス データで最も頻繁に行われる作業の 1 つのため、このプロセスを自動化することで多くの時間を節約できます。さらに、クエリから CSV ドキュメントを出力するコードを作成しておくと、これをカスタム ダッシュボードの自動レポート生成、メール、エクスポート機能などの大きなプロジェクトに統合できるようになります。
始める前に
以下のものが揃っていれば、この記事を最大限に活用できます。
- Java の実践的な知識。
- ディメンションと指標の理解をはじめとする Google アナリティクスの実践的な知識。
- 実際のデータがある有効な Google アナリティクス アカウントへのアクセス権。
- Data Export API Java スタートガイドの知識。この記事では、スタートガイドで説明されている内容をすべて理解していることを前提としています。
- 完全なソースコードのローカルコピー。AnalyticsCvsPrinter.java で入手できます。このコードを使用したサンプル アプリケーションは、AnalyticsCsvDemo.java にあります。
プログラムの概要
この記事で扱うコードでは、次を実行します。
- コードをコンソールに出力するかファイル ストリームに出力するかを実行時に選択できるようにする。
DataFeed
オブジェクトをパラメータとして与えると、データを CSV 形式で出力します。- 行見出しを出力する。
- データ行を出力します。各
DataEntry
は、結果の出力で 1 行になります。 - CSV 互換の出力を得るために各値に対してサニタイズ メソッドを実行する。
- すべての入力を CSV 互換にするサニタイズ メソッドを記述する。
- どのような 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 行目には列名が並んでいます。各列はデータフィードのディメンションまたは指標を表すため、この最初の行を表示するには、次の手順を行います。
- フィードから最初のエントリを取り込みます。
- そのエントリの
getDimensions
メソッドを使用して、ディメンションのリストを反復処理します。 Dimension.getName()
メソッドを使用して各ディメンションの名前を出力し、その後にカンマを付けます。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()
メソッドで pull するのではなく、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" |
_space、カンマ 2 行目、二重引用符 |
"_space、カンマ 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 を生成し、完了時にメールを送信するアプリケーションを作成してみましょう。月間レポートの自動化も簡単です。
- 対話形式でクエリを入力できるアプリケーションを作成すると、強力なインターフェースでデータ内を探索できます。