Daten aus der API für den Datenexport im CSV-Format ausgeben

Alexander Lucas, Google Analytics-API-Team – August 2010


Einleitung

In diesem Artikel erfahren Sie, wie Sie Daten aus jeder beliebigen Abfrage an die Google Analytics-API für den Datenexport abrufen und die Ergebnisse im gängigen CSV-Format ausgeben können. Dies ist eine der häufigsten Aufgaben, die Nutzer mit Analytics-Daten ausführen, die aus der Datenexport API abgerufen werden. Die Automatisierung des Prozesses ist also eine einfache Möglichkeit, regelmäßig eine Menge Zeit zu sparen. Außerdem können Sie Code zum Drucken von CSV-Dokumenten aus Abfragen in größere Projekte wie automatische Berichtsgeneratoren, Mailer und Exportfunktionen für Ihre benutzerdefinierten Dashboards einbinden.

Vorbereitung

Der Artikel ist optimal, wenn Folgendes auf Sie zutrifft:

Programmübersicht

Mit dem Code in diesem Artikel wird Folgendes ausgeführt:

  1. Legen Sie während der Laufzeit fest, ob der Code in der Console oder in einem Dateistream ausgegeben werden soll.
  2. Wenn ein DataFeed-Objekt als Parameter vorhanden ist, geben Sie die Daten im CSV-Format aus:
    • Zeilenüberschriften drucken.
    • Datenzeilen ausgeben, wobei jede DataEntry eine Zeile in der resultierenden Ausgabe ausmacht.
    • Führen Sie jeden Wert mit einer Bereinigungsmethode aus, um eine CSV-sichere Ausgabe zu ermöglichen.
  3. Schreiben Sie eine „Sanitizer“-Methode, die alle Eingaben CSV-sicher macht.
  4. Sie erhalten eine Java-Klasse, die beliebige Datenexport-API-Abfragen in eine CSV-Datei umwandeln kann.

Zurück nach oben

Konfigurierbare Ausgabestreams zulassen

Als Erstes richten Sie einen konfigurierbaren Ausgabestream für Ihre Klasse ein, auf dem sie drucken kann. Auf diese Weise kann jeder Code, der Ihre Klasse verwendet, entscheiden, ob die Ausgabe standardmäßig oder direkt in einer Datei erfolgen soll. Hier müssen Sie lediglich die Getter-/Setter-Methode für ein PrintStream-Objekt einrichten. Das ist das Ziel für alle Druckvorgänge der Klasse.

private PrintStream printStream = System.out;

public PrintStream getPrintStream() {
  return printStream;
}

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

Das Festlegen der Ausgabe auf eine Datei ist ebenfalls sehr einfach. Zum Erstellen eines PrintStream-Objekts für diese Datei wird nur der Dateiname benötigt.

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

Zurück nach oben

Durch die Daten iterieren

Die erste Zeile der CSV-Datei ist die Zeile mit den Spaltennamen. Jede Spalte stellt eine Dimension oder einen Messwert aus dem Datenfeed dar. Gehen Sie daher folgendermaßen vor, um die erste Zeile auszudrucken.

  1. Rufen Sie den ersten Eintrag aus dem Feed ab.
  2. Mit der Methode getDimensions des Eintrags wird eine Liste von Dimensionen durchlaufen.
  3. Geben Sie den Namen jeder Dimension mit der Methode Dimension.getName() aus, gefolgt von einem Komma.
  4. Dasselbe gilt für Messwerte mit der Methode getMetrics(). Geben Sie Kommas bis auf den letzten Messwert aus.

Im Folgenden sehen Sie eine Implementierung der Methode zum Drucken von Zeilenüberschriften. Dieser Code gibt keinen String zurück, der die vollständige Zeile darstellt. Er wird während der Verarbeitung von Werten in einen Ausgabestream ausgegeben.

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();
  }

Das Drucken des „Textes“ der CSV-Datei (alles unter der Zeile mit Spaltennamen) ist sehr ähnlich. Es gibt nur zwei wesentliche Unterschiede. Erstens: Es wird nicht nur der erste Eintrag bewertet. Der Code muss alle Einträge im Feedobjekt als Schleife durchlaufen. Verwenden Sie dann getValue(), statt die Methode getName() zum Abrufen des zu bereinigten und zu druckenden Werts zu verwenden.

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();
  }

Durch diesen Code wird der Feed in Einträge aufgeteilt. Die Einträge werden in Werte aufgeteilt, die ausgegeben werden. Aber wie machen wir diese Werte CSV-freundlich? Was passiert, wenn ein Wert in der Datei „comma-separated-values“ ein Komma enthält? Diese Werte müssen bereinigt werden.

Zurück nach oben

Daten im Hinblick auf die CSV-Kompatibilität bereinigen

CSV ist ein einfaches Format. Eine CSV-Datei stellt eine Datentabelle dar, und jede Zeile stellt eine Zeile in dieser Tabelle dar. Die Werte in dieser Zeile sind durch Kommas getrennt. Eine neue Zeile bedeutet eine neue Datenzeile.

Leider macht dieses einfache Format es trügerisch leicht, Dinge mit schlechten Daten durcheinanderzuwerfen. Was passiert, wenn der Wert ein Komma enthält? Was passiert, wenn einer Ihrer Werte Zeilenumbrüche enthält? Was sollte mit Leerzeichen zwischen Komma und Werten passieren? All diese Situationen können mit ein paar einfachen Regeln berücksichtigt werden.

  • Wenn der String ein doppeltes Anführungszeichen enthält, maskieren Sie es mit einem zweiten doppelten Anführungszeichen.
  • Wenn der String ein Komma enthält, setzen Sie den gesamten String in doppelte Anführungszeichen (es sei denn, Sie haben das bereits getan).
  • Wenn der String einen Zeilenumbruch enthält, setzen Sie den gesamten String in doppelte Anführungszeichen, sofern Sie das nicht bereits getan haben.
  • Wenn die Zeichenfolge mit einem Leerraum beginnt oder endet, setzen Sie die gesamte Zeichenfolge in doppelte Anführungszeichen (sofern Sie dies nicht bereits getan haben).

Es kann etwas schwierig sein, sich vorzustellen, wie Ihre Werte zu diesem Zeitpunkt aussehen sollten. Hier sind einige Beispiele. Jedes Beispiel stellt einen einzelnen Wert dar und wird entsprechend maskiert. Zur Verdeutlichung werden Leerzeichen als „_“ angezeigt.

Vorher Nachher
nicht geändert nicht geändert
zufälliges Anführungszeichen zufälliges doppeltes Anführungszeichen ""
kommagetrennt "Komma,getrennt"
Zwei
Zeilen
„Zwei
Zeilen“
_leading Leerzeichen und ein Komma „_leading Leerzeichen und ein Komma“
"führendes Anführungszeichen, Komma """führendes Zitat, Komma"
_Leerzeichen, Komma
zweite Zeile und doppeltes Anführungszeichen
"_Leerzeichen, Komma
zweite Zeile und doppeltes Anführungszeichen"""

Am einfachsten lassen sich alle diese Bedingungen mit einer Bereinigungsmethode bewältigen. Es gehen fragwürdige Daten ein und gute, saubere CSV-Werte werden ausgegeben. Hier ist ein gutes Implementierungsbeispiel für eine solche Methode.

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();
}

Die Methode beginnt mit der Überprüfung auf vorhandene doppelte Anführungszeichen. Dies sollte vor allen anderen Prüfungen erfolgen, da hierbei ein String in doppelte Anführungszeichen gesetzt wird. Außerdem wäre es mühsam, den Unterschied zwischen doppelten Anführungszeichen, die Teil des Werts waren, und doppelten Anführungszeichen zu ermitteln, die zuvor durch diese Methode hinzugefügt wurden. Sie sind leicht zu entkommen – sie müssen nur verdoppelt werden. Jedes " wird zu "", jedes "" wird zu einem """" usw.

Sobald diese Bedingung erfüllt ist, können alle anderen Bedingungen (ohne Leerraum, Kommas und Zeilenumbrüche) geprüft werden. Sind solche vorhanden, setzen Sie den Wert einfach in doppelte Anführungszeichen.

Im obigen Beispiel wird ein StringBuilder-Objekt verwendet, das niemals direkt einen Rohstring bearbeitet. Das liegt daran, dass Sie mit StringBuilder den String frei bearbeiten können, ohne vorläufige Kopien im Speicher zu erstellen. Da Zeichenfolgen in Java unveränderlich sind, würde jede kleine Optimierung, die Sie vornehmen, eine ganz neue Zeichenfolge erstellen. Beim Durchblättern von Tabellenkalkulationsdaten kann sich das sehr schnell summieren.

Zeilenanzahl x Werte pro Zeile x Wertänderungen = Gesamtzahl der neuen Strings, die erstellt wurden
10.000 10 3 300.000

Zurück nach oben

Weitere Informationen

Nachdem Sie nun einen goldenen Hammer erhalten haben, geht es nur noch auf die Nägeljagd. Hier sind ein paar Ideen für den Einstieg.

  • Sehen Sie sich den Beispielanwendungsquellcode an, in dem diese Klasse verwendet wird, um eine CSV-Datei basierend auf einer Beispielabfrage auszugeben. Der Ausgabedateiname wird als Befehlszeilenparameter verwendet und standardmäßig ausgegeben. Nutzen Sie sie als Ausgangspunkt, um etwas Tolles zu schaffen!
  • CSV ist nur eines von vielen beliebten Formaten. Sie können die Klasse so anpassen, dass sie in einem anderen Format wie TSV, YAML, JSON oder XML ausgegeben wird.
  • Schreiben Sie eine Anwendung, die CSV-Dateien generiert und nach Abschluss des Vorgangs per E-Mail versendet. Einfache automatisierte monatliche Berichterstellung!
  • Schreiben Sie eine Anwendung, mit der Sie Abfragen interaktiv eingeben können und mit der Sie eine leistungsstarke Schnittstelle zum Durchsuchen Ihrer Daten erhalten.