Generowanie danych z interfejsu Data Export API do formatu CSV

Alexander Lucas, zespół Google Analytics API – sierpień 2010 r.


Wstęp

Z tego artykułu dowiesz się, jak pobrać dane z dowolnego zapytania wysłanego do interfejsu Google Analytics Data Export API i uzyskać wyniki w popularnym formacie CSV. Jest to jedno z najczęstszych zadań związanych z danymi Analytics pobranymi z interfejsu Data Export API, dlatego automatyzacja tego procesu to łatwy sposób na regularne zaoszczędzenie mnóstwo czasu. Mając już kod do drukowania dokumentów CSV z zapytań, możesz zintegrować go z większymi projektami, takimi jak automatyczne generatory raportów, wiadomości pocztowe i funkcje eksportowania napisanych przez siebie niestandardowych paneli.

Zanim rozpoczniesz

Aby w pełni wykorzystać możliwości tego artykułu, musisz mieć:

Omówienie programu

Kod omówiony w tym artykule:

  1. Włącz określanie w czasie działania, czy kod ma być drukowany w konsoli czy w strumieniu plików.
  2. Mając obiekt DataFeed jako parametr, wydrukuj dane w formacie CSV:
    • Drukuj nagłówki wierszy.
    • Drukuj wiersze danych, w których każdy element DataEntry tworzy 1 wiersz wynikowych danych wyjściowych.
    • Uruchom każdą wartość za pomocą metody oczyszczania, aby uzyskać dane wyjściowe bezpieczne w formacie CSV.
  3. Utwórz metodę „Sanitizer”, która sprawi, że wszystkie wejściowe dane CSV będą bezpieczne.
  4. Udostępnij klasę Javy, która może przekształcić dowolne zapytanie interfejsu API eksportu danych i przekształcić je w plik CSV.

Powrót do góry

Zezwalaj na konfigurowalne strumienie wyjściowe

Najpierw musisz skonfigurować konfigurowalny strumień danych wyjściowych, w którym Twoja klasa będzie drukować. Dzięki temu każdy kod korzystający z klasy decyduje, czy dane wyjściowe powinny zostać udostępnione w standardzie czy bezpośrednio do pliku. Wystarczy skonfigurować metodę getter/setter dla obiektu PrintStream. Będzie to cel wszystkich drukowania wykonywanych przez klasę.

private PrintStream printStream = System.out;

public PrintStream getPrintStream() {
  return printStream;
}

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

Ustawienie danych wyjściowych na plik również jest bardzo łatwe. Wystarczy nazwa pliku, aby utworzyć dla niego obiekt PrintStream.

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

Powrót do góry

Przechodzenie między danymi

Pierwszy wiersz pliku CSV zawiera nazwy kolumn. Każda kolumna reprezentuje wymiar lub dane z pliku danych, więc aby wydrukować pierwszy wiersz, wykonaj te czynności.

  1. Pobierz pierwszy wpis z kanału.
  2. Przejrzyj listę wymiarów przy użyciu metody getDimensions tego wpisu.
  3. Wydrukuj nazwę każdego wymiaru za pomocą metody Dimension.getName(), po których następuje przecinek.
  4. To samo zrób z danymi, korzystając z metody getMetrics(). Drukuj przecinki oprócz ostatniego wskaźnika.

Oto jedna z implementacji metody drukowania nagłówków wierszy. Pamiętaj, że ten kod nie zwraca ciągu znaków reprezentującego cały wiersz. Jest drukowany do strumienia danych wyjściowych podczas przetwarzania wartości.

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

Drukowanie „treści” pliku CSV (wszystkiego, co znajduje się pod wierszem nazw kolumn) jest bardzo podobne. Występują tylko 2 główne różnice. Po pierwsze, nie jest to tylko pierwsza ocena poddana ocenie. Kod musi przejść przez wszystkie wpisy w obiekcie pliku danych. Po drugie, zamiast metody getName() do pobierania wartości przeznaczonej do oczyszczenia i wydrukowania użyj metody 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();
  }

Ten kod dzieli plik danych na wpisy, a wpisy na wartości są wydrukowane na wyjściu. Jak jednak dostosować je do formatu CSV? Co zrobić, jeśli wartość w pliku z wartościami rozdzielonymi przecinkami zawiera przecinek? Wartości te muszą zostać oczyszczone.

Powrót do góry

Jak Sanityzacja danych w celu zapewnienia zgodności z CSV

Prostym formatem jest plik CSV. Plik CSV odpowiada tabeli danych, a każdy wiersz odpowiada jej wierszowi. Wartości w tym wierszu są rozdzielone przecinkami. Nowy wiersz oznacza nowy wiersz danych.

Ten prosty format sprawia, że łatwo pozbyć się niekorzystnych danych. Co zrobić, jeśli wartość zawiera przecinek? Co zrobić, jeśli jedna z wartości zawiera podziały wiersza? Co powinno się wydarzyć między przecinkami a wartościami? W każdym z tych przypadków można uwzględnić kilka prostych reguł.

  • Jeśli ciąg zawiera znak cudzysłowu, zastąp go drugim znakiem cudzysłowu.
  • Jeśli ciąg zawiera przecinek, umieść go w cudzysłowie prostym (chyba że już go masz).
  • Jeśli w ciągu znaków jest podział wiersza, umieść cały ciąg w cudzysłowie prostym (chyba że już go masz).
  • Jeśli ciąg znaków na początku lub na końcu jest spacją, umieść cały ciąg w cudzysłowie prostym (chyba że już go masz).

Wyobrażenie sobie, jak w tym momencie powinny wyglądać Twoje wartości, może być nieco trudne, dlatego podajemy kilka przykładów. Pamiętaj, że każdy przykład reprezentuje pojedynczą wartość, która jako taka ma znaczenie zmiany znaczenia. Aby uniknąć wątpliwości: spacje będą wyświetlane jako znak _.

Przed Po
bez zmian bez zmian
losowy " podwójny cudzysłów losowy cudzysłów podwójny ""
oddzielone przecinkami "rozdzielone przecinkami"
Dwie
wiersze
„Dwie
wiersze”
_leading space i przecinek "_najpierwsza spacja, przecinek"
"cytat początkowy, przecinek """cytat początkowy, przecinek"
_space, przecinek
drugi i cudzysłów podwójny"
"_space, przecinek
drugi i cudzysłów podwójny"""

Najłatwiejszym sposobem radzenia sobie ze wszystkimi tymi warunkami jest napisanie metody oczyszczania. Wprowadzane są budzące wątpliwości dane i otrzymywane są prawidłowe, przejrzyste wartości w formacie CSV. Oto przykładowa implementacja takiej metody.

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

Metoda rozpoczyna się od sprawdzenia, czy występują już podwójne cudzysłowy. Należy to zrobić przed wszystkimi innymi testami, ponieważ trzeba dodać ciąg znaków podwójnym cudzysłowem. Określanie różnic między cudzysłowami podwójnymi, które wchodzą w skład wartości, a cudzysłowami dodanymi wcześniej przy użyciu tej metody, byłoby uciążliwe. Łatwo uciekać od tych danych – wystarczy je podwoić. Każdy element " staje się "", każdy "" zmienia się w """" itd.

Gdy ten warunek zostanie spełniony, możesz sprawdzać wszystkie pozostałe warunki (nieusunięte odstępy, przecinki i znaki podziału wierszy). Jeśli występuje któraś z nich, po prostu umieść wartość w cudzysłowie.

Zwróć uwagę, że powyższe polecenie używa obiektu StringBuilder i nigdy nie modyfikuje bezpośrednio nieprzetworzonego ciągu znaków. Dzieje się tak, ponieważ obiekt StringBuilder umożliwia swobodną obsługę ciągu bez tworzenia tymczasowych kopii w pamięci. Ciągi w Javie są niezmienne, więc każda drobna zmiana powoduje utworzenie zupełnie nowego ciągu. Gdy przeglądasz dane w arkuszu kalkulacyjnym, szybko sumuje się z nich.

Liczba wierszy x wartości na wiersz x Zmiany wartości = Łączna liczba utworzonych nowych ciągów znaków
10 000 10 3 300 000

Powrót do góry

Co dalej?

Teraz, gdy dostajesz złoty młotek, poszukiwanie paznokci to zupełnie normalne. Oto kilka pomysłów na początek.

  • Przyjrzyj się przykładowemu kodowi źródłowemu aplikacji, który korzysta z tej klasy do wydrukowania pliku CSV na podstawie przykładowego zapytania. Wykorzystuje on nazwę pliku wyjściowego jako parametr wiersza poleceń i domyślnie drukuje w standardzie. Potraktuj go jako punkt wyjścia i stwórz coś niesamowitego.
  • CSV to tylko jeden z wielu popularnych formatów. Dostosuj klasę do innego formatu, np. TSV, YAML, JSON lub XML.
  • Napisz aplikację, która będzie generować pliki CSV i wysyłać je e-mailem po ich zakończeniu. Łatwe automatyczne raporty miesięczne
  • Napisz aplikację, która umożliwia interaktywne wprowadzanie zapytań, aby utworzyć zaawansowany interfejs do przeglądania danych.