Menghasilkan Data dari Data Export API ke Format CSV

Alexander Lucas, Tim Google Analytics API – Agustus 2010


Pengantar

Artikel ini menunjukkan cara mengambil data dari kueri apa pun yang dibuat ke Data Export API Google Analytics dan menampilkan hasilnya dalam format CSV yang populer. Ini adalah salah satu tugas paling umum yang dilakukan pengguna dengan data Analytics yang diambil dari Data Export API, sehingga mengotomatiskan proses ini merupakan cara mudah untuk menghemat banyak waktu secara rutin. Selain itu, setelah Anda memiliki kode untuk mencetak dokumen CSV dari kueri, Anda dapat mengintegrasikannya ke dalam project yang lebih besar, seperti pembuat laporan otomatis, surat, dan fungsi "ekspor" untuk dasbor kustom yang telah Anda tulis.

Sebelum Memulai

Anda dapat mengoptimalkan artikel ini jika memiliki hal berikut:

Ringkasan Program

Kode yang dibahas dalam artikel ini akan berfungsi sebagai berikut:

  1. Mengaktifkan pemilihan pada saat runtime, apakah kode dicetak ke konsol atau ke aliran file.
  2. Dengan objek DataFeed sebagai parameter, cetak data tersebut dalam format CSV:
    • Cetak header baris.
    • Mencetak baris data, dengan setiap DataEntry membentuk satu baris dalam output yang dihasilkan.
    • Jalankan setiap nilai melalui metode sanitasi untuk output yang aman dari CSV.
  3. Tulis metode "Sanitizer" yang membuat semua input Aman untuk CSV.
  4. Memberi Anda class Java yang dapat mengambil kueri Data Export API apa pun dan mengubahnya menjadi file CSV.

Kembali ke Atas

Izinkan Aliran Output yang Dapat Dikonfigurasi

Hal pertama yang harus dilakukan adalah menyiapkan streaming output yang dapat dikonfigurasi untuk menjadi tujuan pencetakan class Anda. Dengan demikian, kode apa pun yang menggunakan class Anda dapat menentukan apakah output harus dikirim ke standard out atau langsung ke file. Yang perlu Anda lakukan di sini adalah menyiapkan metode pengambil/penyetel untuk objek PrintStream. Itu akan menjadi target dari semua pencetakan yang dilakukan oleh class tersebut.

private PrintStream printStream = System.out;

public PrintStream getPrintStream() {
  return printStream;
}

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

Menyetel {i>output<i} ke file juga sangat mudah. Kita hanya memerlukan nama file untuk membuat objek PrintStream bagi file tersebut.

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

Kembali ke Atas

Melakukan iterasi melalui data

Baris pertama {i>file<i} CSV adalah baris nama kolom. Setiap kolom mewakili dimensi atau metrik dari feed data, jadi untuk mencetak baris pertama ini, lakukan hal berikut.

  1. Ambil entri pertama dari feed.
  2. Lakukan iterasi melalui daftar dimensi menggunakan metode getDimensions entri tersebut.
  3. Cetak nama setiap dimensi menggunakan metode Dimension.getName(), diikuti dengan koma.
  4. Lakukan hal yang sama untuk metrik menggunakan metode getMetrics(). Setelah semua, cetak koma, kecuali metrik terakhir.

Berikut salah satu implementasi metode untuk mencetak header baris. Perlu diperhatikan bahwa kode ini tidak menampilkan string yang mewakili baris lengkap: kode ini dicetak ke aliran output saat memproses nilai.

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

Mencetak "isi" file CSV (semua yang ada di bawah baris nama kolom) sangat mirip. Hanya ada dua perbedaan utama. Pertama, itu bukan hanya entri pertama yang dievaluasi. Kode harus melakukan loop melalui semua entri dalam objek feed. Kedua, daripada menggunakan metode getName() untuk menarik nilai yang akan dibersihkan dan dicetak, gunakan getValue() sebagai gantinya.

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

Kode ini membagi feed Anda menjadi beberapa entri, dan entri Anda menjadi nilai yang akan dicetak menjadi output. Namun, bagaimana cara membuat nilai tersebut ramah untuk CSV? Bagaimana jika nilai dalam file "nilai yang dipisahkan koma" memiliki tanda koma di dalamnya? Nilai-nilai tersebut harus dibersihkan.

Kembali ke Atas

Cara Membersihkan Data untuk Kompatibilitas CSV

CSV adalah format yang sederhana. {i>File<i} CSV mewakili tabel data, dan setiap baris mewakili baris dalam tabel itu. Nilai-nilai di baris tersebut dipisahkan dengan tanda koma. Baris baru berarti baris data baru.

Sayangnya, format yang sederhana ini membuat pengguna dapat dengan mudah membuang data yang buruk. Bagaimana jika nilai Anda memiliki koma di dalamnya? Bagaimana jika salah satu nilai Anda memiliki jeda baris di dalamnya? Apa yang harus terjadi dengan spasi antara koma dan nilai? Semua situasi ini dapat diperhitungkan karena menggunakan beberapa aturan sederhana.

  • Jika string berisi karakter kutip ganda, konversikan dengan karakter kutip ganda kedua.
  • Jika ada koma dalam string, gabungkan seluruh string dengan tanda kutip ganda (kecuali jika Anda sudah memilikinya).
  • Jika ada jeda baris dalam string, gabungkan seluruh string dengan tanda kutip ganda (kecuali jika Anda sudah memilikinya).
  • Jika string dimulai atau diakhiri dengan spasi kosong, gabungkan seluruh string dalam tanda kutip ganda (kecuali jika Anda sudah memilikinya).

Mungkin sedikit sulit untuk memvisualisasikan tampilan nilai Anda pada tahap ini, jadi berikut beberapa contohnya. Ingat, setiap contoh mewakili nilai tunggal, dan di-escape seperti itu. Untuk kejelasan, spasi akan ditampilkan sebagai karakter _.

Sebelum Setelah
tidak diubah tidak diubah
kutipan " acak tanda kutip ganda "" acak
koma,terpisah "koma,dipisahkan"
Dua
baris
"Dua
baris"
_spasi di awal, dan koma "_Spasi di depan, dan koma"
"kutipan awal, koma """kutipan awal, koma"
_spasi, koma
baris kedua, dan tanda kutip ganda"
"_spasi, koma
baris kedua, dan tanda kutip ganda"""

Cara termudah untuk menangani semua kondisi ini adalah dengan menulis metode sanitasi. Data yang dipertanyakan masuk, dan nilai CSV yang bagus dan bersih akan keluar. Berikut ini contoh implementasi yang baik dari metode tersebut.

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

Metode dimulai dengan memeriksa tanda kutip ganda yang ada. Hal ini harus dilakukan sebelum semua pemeriksaan lainnya, karena melibatkan penggabungan string dengan tanda kutip ganda, dan akan mengganggu jika menentukan perbedaan antara tanda kutip ganda yang merupakan bagian dari nilai dan tanda kutip ganda yang ditambahkan sebelumnya oleh metode ini. Ini mudah dihindari—mereka hanya perlu diperbesar dua kali. Setiap " menjadi "", setiap "" menjadi """", dan seterusnya.

Setelah kondisi tersebut terpenuhi, semua kondisi lainnya (spasi kosong, koma, dan baris baru) dapat diperiksa. Jika ada, cukup kurung nilai dalam tanda kutip ganda.

Perlu diketahui bahwa cara di atas menggunakan objek StringBuilder, jangan pernah memanipulasi string mentah secara langsung. Hal ini karena StringBuilder memungkinkan Anda memanipulasi string dengan bebas tanpa membuat salinan sementara di memori. Karena string di Java tidak dapat diubah, setiap penyesuaian kecil yang Anda buat akan membuat string baru. Ketika menelusuri data {i>spreadsheet<i}, data ini akan bertambah dengan cepat.

Jumlah baris x Nilai per baris x Perubahan nilai = Total String Baru yang Dibuat
10.000 10 3 300.000

Kembali ke Atas

Apa Selanjutnya?

Karena sekarang Anda diberi palu emas, mencari paku adalah hal yang wajar. Berikut ini beberapa ide untuk membantu Anda memulai.

  • Lihat contoh kode sumber aplikasi yang menggunakan class ini untuk mencetak file CSV berdasarkan kueri contoh. Fungsi ini mengambil nama file output sebagai parameter command line, dan mencetaknya ke output standar secara default. Gunakan sebagai titik awal, buat sesuatu yang luar biasa!
  • CSV hanyalah salah satu dari banyak format yang populer. Sesuaikan class untuk menghasilkan output ke format yang berbeda, seperti TSV, YAML, JSON, atau XML.
  • Tulis aplikasi yang membuat CSV dan mengirimkannya melalui email jika sudah selesai. Pelaporan bulanan otomatis yang mudah.
  • Tulis aplikasi yang memungkinkan Anda memasukkan kueri secara interaktif, sebagai antarmuka yang andal untuk menggali data lebih dalam.