Data Export API에서 CSV 형식으로 데이터 출력

Alexander Lucas, Google 애널리틱스 API팀 - 2010년 8월


소개

이 도움말에서는 Google 애널리틱스 Data Export API에 대한 모든 쿼리에서 데이터를 가져와서 인기 있는 CSV 형식으로 결과를 출력하는 방법을 설명합니다. 이는 사용자가 Data Export API에서 가져온 애널리틱스 데이터로 수행하는 가장 일반적인 작업 중 하나이므로 프로세스를 자동화하면 정기적으로 많은 시간을 절약할 수 있습니다. 또한 쿼리에서 CSV 문서를 출력하는 코드를 만들면 이를 작성한 자동 대시보드 생성기, 메일러, 맞춤 대시보드용 내보내기 함수와 같은 더 큰 프로젝트에 통합할 수 있습니다.

시작하기 전에

다음과 같은 경우 이 도움말을 최대한 활용해 보세요.

  • 자바에 대한 실무 지식
  • 측정기준 및 측정항목에 대한 이해 등 Google 애널리틱스에 대한 실무 지식이 필요합니다.
  • 실제 데이터가 있는 활성 Google 애널리틱스 계정에 대한 액세스 권한.
  • Data Export API 자바 시작 가이드에 관한 기본 지식 이 도움말에서는 사용자가 이 가이드에서 다루는 모든 내용을 이미 알고 있다고 가정합니다.
  • 전체 소스 코드의 로컬 사본으로서 AnalyticsCvsPrinter.java에서 확인할 수 있습니다. 이 코드를 사용하는 샘플 애플리케이션은 AnalyticsCsvDemo.java에서 확인할 수 있습니다.

프로그램 개요

이 도움말에서 다루는 코드는 다음을 수행합니다.

  1. 코드가 콘솔에 출력되는지 아니면 파일 스트림으로 출력되는지를 런타임에 선택할 수 있습니다.
  2. DataFeed 객체가 매개변수로 주어지면 데이터를 CSV 형식으로 출력합니다.
    • 행 헤더를 출력합니다.
    • 데이터 행을 인쇄합니다. 여기서 각 DataEntry은 결과 출력에서 하나의 행을 구성합니다.
    • CSV 안전 출력을 위한 정리 메서드를 통해 각 값을 실행합니다.
  3. 모든 입력을 CSV로 안전하게 만드는 "Sanitizer" 메서드를 작성합니다.
  4. 데이터 내보내기 API 쿼리를 가져와서 CSV 파일로 변환할 수 있는 자바 클래스를 제공합니다.

맨 위로

구성 가능한 출력 스트림 허용

가장 먼저 할 일은 클래스가 출력할 구성 가능한 출력 스트림을 설정하는 것입니다. 이렇게 하면 클래스를 사용하는 모든 코드가 출력을 표준으로 출력할지 아니면 파일로 직접 이동할지 결정할 수 있습니다. 여기서는 PrintStream 객체의 getter/setter 메서드를 설정하기만 하면 됩니다. 이 클래스가 클래스가 실행하는 모든 인쇄의 타겟이 됩니다.

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 파일 (열 이름 행 아래의 모든 항목)의 'body' 인쇄는 매우 유사합니다. 두 가지 주요 차이점이 있습니다. 첫째, 평가되는 첫 번째 항목뿐만이 아닙니다. 코드는 피드 객체의 모든 항목을 순환해야 합니다. 둘째, 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 파일은 데이터 테이블을 나타내며 각 줄은 해당 테이블의 행을 나타냅니다. 이 행의 값은 쉼표로 구분됩니다. 새 줄은 새 데이터 행을 의미합니다.

안타깝게도 이러한 간단한 형식으로 인해 잘못된 데이터로 인해 사기성 문제가 쉽게 발생할 수 있습니다. 값에 쉼표가 있으면 어떻게 해야 할까요? 값 중 하나에 줄바꿈이 있으면 어떻게 해야 할까요? 쉼표와 값 사이의 공간은 어떻게 될까요? 몇 가지 간단한 규칙을 사용하면 이러한 상황을 모두 해결할 수 있습니다.

  • 문자열에 큰따옴표가 있는 경우 두 번째 큰따옴표 문자로 이스케이프 처리합니다.
  • 문자열에 쉼표가 있으면 전체 문자열을 큰 따옴표로 묶습니다 (이미 있는 경우 제외).
  • 문자열에 줄바꿈이 있으면 전체 문자열을 큰따옴표로 묶습니다 (이미 있는 경우 제외).
  • 문자열이 일종의 공백으로 시작하거나 끝나는 경우 전체 문자열을 큰따옴표로 묶습니다 (이미 있는 경우 제외).

이 시점의 값의 모양을 시각화하는 것은 다소 까다로울 수 있으므로 다음은 몇 가지 예입니다. 각 예시는 단일 값을 나타내며 그렇게 이스케이프 처리됩니다. 명확하게 하기 위해 공백은 _ 문자로 표시됩니다.

이전 이후
변경 안됨 변경 안됨
무작위 따옴표 무작위 따옴표
쉼표로 구분 쉼표,구분됨
두 줄
"2
_선호하는 공백과 쉼표 쉼표와 쉼표
"선명한 따옴표, 쉼표 "선두 따옴표, 쉼표
_space, 쉼표
두 번째 줄, 큰따옴표'
'_space', 쉼표
두 번째 줄, 큰따옴표'

이러한 조건을 모두 처리하는 가장 쉬운 방법은 정리 메서드를 작성하는 것입니다. 의심스러운 데이터가 들어오고, 깨끗하고 깔끔한 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를 사용하면 메모리에 중간 복사본을 만들지 않고도 문자열을 자유롭게 조작할 수 있기 때문입니다. 자바의 문자열은 변경할 수 없기 때문에 사소한 변경이 있을 때마다 완전히 새로운 문자열이 생성됩니다. 스프레드시트 데이터를 활용하는 경우 매우 빠르게 합쳐질 수 있습니다.

행 수 x 행당 값 x 값 변경 = 생성된 총 새 문자열
10,000 10 3 300,000

맨 위로

다음 단계

황금 망치를 받았기 때문에 손톱을 사냥하는 것은 당연합니다. 시작할 때 도움이 되는 몇 가지 아이디어입니다.

  • 이 클래스를 사용하여 샘플 쿼리를 기반으로 CSV 파일을 인쇄하는 샘플 애플리케이션 소스 코드를 살펴보세요. 출력 파일 이름을 명령줄 매개변수로 사용하고 기본적으로 표준 출력으로 인쇄합니다. 출발점으로 삼아 멋진 콘텐츠를 만들어 보세요.
  • CSV는 많이 사용되는 형식 중 하나일 뿐입니다. TSV, YAML, JSON 또는 XML과 같은 다른 형식으로 출력하도록 클래스를 조정합니다.
  • CSV를 생성하고 완료되면 메일을 전송하는 애플리케이션을 작성합니다. 간편한 월별 자동 보고 기능
  • 대화형으로 쿼리를 입력할 수 있는 애플리케이션을 작성하여 데이터 탐색에 유용한 강력한 인터페이스를 제공합니다.