Вывод данных из API экспорта данных в формат CSV

Александр Лукас, команда Google Analytics API – август 2010 г.


Введение

В этой статье показано, как получить данные из любого запроса к API экспорта данных Google Analytics и вывести результаты в популярный формат CSV. Это одна из наиболее распространенных задач, которые люди выполняют с данными Analytics, полученными из API экспорта данных, поэтому автоматизация процесса — это простой способ регулярно экономить массу времени. Кроме того, как только у вас появится код для распечатки документов CSV из запросов, вы сможете интегрировать его в более крупные проекты, такие как автоматические генераторы отчетов, почтовые программы и функции «экспорта» для написанных вами пользовательских панелей мониторинга.

Прежде чем ты начнешь

Вы получите максимальную пользу от этой статьи, если у вас есть следующее:

обзор программы

Код, описанный в этой статье, будет выполнять следующие действия:

  1. Включите выбор во время выполнения, будет ли код выводиться на консоль или в файловый поток.
  2. Учитывая объект DataFeed в качестве параметра, распечатайте данные в формате CSV:
    • Распечатать заголовки строк.
    • Выведите строки данных, где каждая DataEntry составляет одну строку в результирующем выводе.
    • Пропустите каждое значение с помощью метода очистки для безопасного вывода в формате CSV.
  3. Напишите метод «Sanitizer», который сделает все входные данные CSV безопасными.
  4. Предоставить вам класс Java, который может принять любой запрос API экспорта данных и преобразовать его в файл CSV.

Вернуться к вершине

Разрешить настраиваемые выходные потоки

Первое, что нужно сделать, — это настроить настраиваемый поток вывода, в который ваш класс будет печатать. Таким образом, любой код, использующий ваш класс, может решить, следует ли отправлять вывод на стандартный вывод или непосредственно в файл. Все, что вам нужно сделать, это настроить метод получения/установки для объекта 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. Возьмите первую запись из ленты.
  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 (все, что находится под строкой имен столбцов) очень похоже. Есть только два ключевых различия. Во-первых, оценивается не только первая запись. Коду необходимо просмотреть все записи в объекте канала. Во-вторых, вместо использования метода 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 представляет таблицу данных, и каждая строка представляет строку в этой таблице. Значения в этой строке разделяются запятыми. Новая строка означает новую строку данных.

К сожалению, из-за этого простого формата обманчиво легко испортить ситуацию с плохими данными. Что, если в вашем значении есть запятая? Что, если одно из ваших значений содержит разрывы строк? Что должно произойти с пробелом между запятыми и значениями? Все эти ситуации можно объяснить, используя несколько простых правил.

  • Если строка содержит символ двойной кавычки, экранируйте ее вторым символом двойной кавычки.
  • Если в строке есть запятая, заключите всю строку в двойные кавычки (если это еще не сделано).
  • Если в строке есть разрыв строки, заключите всю строку в двойные кавычки (если это еще не сделано).
  • Если строка начинается или заканчивается каким-либо пробелом, заключите всю строку в двойные кавычки (если это еще не сделано).

На данном этапе может быть немного сложно представить себе, как должны выглядеть ваши значения, поэтому вот несколько примеров. Помните, что каждый пример представляет одно значение и экранируется как таковое. Для ясности пробелы будут обозначаться символом _.

До После
неизменный неизменный
случайная " двойная кавычка случайная "" двойная кавычка
разделенные запятой "разделенные запятой"
Два
линии
"Два
линии"
_ведущий пробел и запятая "_ведущий пробел и запятая"
"начальная цитата, запятая """начальная цитата, запятая"
_пробел, запятая
вторая строка и двойная кавычка"
"_пробел, запятая
вторая строка и двойная кавычка """

Самый простой способ справиться со всеми этими условиями — написать метод очистки. Поступают сомнительные данные, а выходят хорошие, чистые значения в формате 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 значений в строке x Изменения стоимости = Всего создано новых строк
10 000 10 3 300 000

Вернуться к вершине

Что дальше?

Теперь, когда вам дали золотой молоток, вполне естественно отправиться на поиски гвоздей. Вот несколько идей, с которых можно начать.

  • Взгляните на пример исходного кода приложения , в котором этот класс используется для распечатки файла CSV на основе примера запроса. Он принимает имя выходного файла в качестве параметра командной строки и по умолчанию печатает в стандартном формате. Используйте его как отправную точку и создайте что-то потрясающее!
  • CSV — лишь один из многих популярных форматов. Настройте класс для вывода в другой формат, например TSV, YAML, JSON или XML.
  • Напишите приложение, которое генерирует CSV-файлы и отправляет их по почте. Легкая автоматизированная ежемесячная отчетность!
  • Напишите приложение, которое позволит вам вводить запросы в интерактивном режиме и получить мощный интерфейс для поиска внутри ваших данных.