Générer des données de l'API d'exportation de données au format CSV

Alexander Lucas, équipe API Google Analytics – août 2010


Introduction

Cet article vous explique comment transférer les données de n'importe quelle requête vers l'API d'exportation de données Google Analytics et générer les résultats au format CSV courant. Il s'agit de l'une des tâches les plus courantes que les utilisateurs effectuent avec les données Analytics extraites de l'API d'exportation de données. L'automatisation du processus est donc un moyen simple de gagner régulièrement beaucoup de temps. De plus, une fois que vous aurez du code pour imprimer des documents CSV à partir de requêtes, vous pourrez l'intégrer à des projets plus importants, tels que des générateurs automatiques de rapports, des envois d'e-mails et des fonctions d'exportation pour les tableaux de bord personnalisés que vous avez écrits.

Avant de commencer

Pour tirer pleinement parti de cet article:

Présentation du programme

Le code traité dans cet article permet d'effectuer les opérations suivantes:

  1. Permet de choisir, au moment de l'exécution, le code à imprimer dans la console ou dans un flux de fichiers.
  2. À partir d'un objet DataFeed comme paramètre, imprimez les données au format CSV :
    • Imprimer les en-têtes de ligne
    • Imprimez les lignes de données, où chaque DataEntry représente une ligne dans la sortie obtenue.
    • Exécutez chaque valeur à l'aide d'une méthode de nettoyage pour obtenir une sortie compatible avec le format CSV.
  3. Écrivez une méthode "Sanitizer" qui rend toutes les entrées valides au format CSV.
  4. vous fournir une classe Java pouvant prendre n'importe quelle requête de l'API d'exportation de données et la transformer en fichier CSV ;

Haut de page

Autoriser les flux de sortie configurables

La première chose à faire est de configurer un flux de sortie configurable sur lequel votre classe doit lancer l'impression. De cette façon, tout code utilisant votre classe peut décider si la sortie doit être dirigée vers la sortie standard ou directement vers un fichier. Il vous suffit ici de configurer la méthode getter/setter pour un objet PrintStream. Ce sera la cible de toutes les impressions effectuées par la classe.

private PrintStream printStream = System.out;

public PrintStream getPrintStream() {
  return printStream;
}

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

Définir la sortie dans un fichier est également très facile. Pour créer un objet PrintStream pour ce fichier, vous n'avez besoin que du nom de fichier.

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

Haut de page

Itérer les données

La première ligne du fichier CSV contient les noms de colonnes. Chaque colonne représente une dimension ou une métrique du flux de données. Pour imprimer cette première ligne, procédez comme suit :

  1. Récupérez la première entrée du flux.
  2. Itérez une liste de dimensions à l'aide de la méthode getDimensions de cette entrée.
  3. Imprimez le nom de chaque dimension à l'aide de la méthode Dimension.getName(), suivi d'une virgule.
  4. Procédez de la même manière pour les métriques à l'aide de la méthode getMetrics(). Imprimez les virgules après toutes les métriques, à l'exception de la dernière.

Voici une implémentation de la méthode permettant d'imprimer des en-têtes de ligne. Notez que ce code ne renvoie pas de chaîne représentant la ligne complète: il est imprimé dans un flux de sortie lors du traitement des valeurs.

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

L'impression du "corps" du fichier CSV (tout ce qui se trouve sous la ligne de noms de colonnes) est très similaire. Il n'y a que deux différences essentielles. Tout d'abord, il ne s'agit pas seulement de la première entrée évaluée. Le code doit parcourir toutes les entrées de l'objet "feed". Ensuite, au lieu d'utiliser la méthode getName() pour extraire la valeur à nettoyer et à imprimer, utilisez plutôt 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();
  }

Ce code divise votre flux en entrées, et celles-ci en valeurs à imprimer en sortie. Mais comment rendre ces valeurs compatibles avec le format CSV ? Que se passe-t-il si une valeur du fichier "valeurs séparées par une virgule" contient une virgule ? Ces valeurs doivent être nettoyées.

Haut de page

Nettoyer les données pour assurer leur compatibilité avec le format CSV

Le format CSV est simple. Un fichier CSV représente un tableau de données et chaque ligne représente une ligne de cette table. Les valeurs de cette ligne sont séparées par des virgules. Une nouvelle ligne signifie une nouvelle ligne de données.

Malheureusement, ce format simple permet de créer facilement des erreurs avec des données de mauvaise qualité. Que se passe-t-il si votre valeur contient une virgule ? Et si l'une de vos valeurs comporte des sauts de ligne ? Que doit-il se passer avec l'espace entre les virgules et les valeurs ? Toutes ces situations peuvent être justifiées par l'utilisation de quelques règles simples.

  • Si la chaîne contient un guillemet double, échappez-le avec un deuxième guillemet double.
  • Si la chaîne contient une virgule, entourez la chaîne entière de guillemets doubles (sauf si vous en avez déjà fait).
  • Si la chaîne comporte un saut de ligne, entourez la chaîne entière de guillemets doubles (sauf si vous en avez déjà fait).
  • Si la chaîne commence ou se termine par un espace blanc, entourez toute la chaîne de guillemets doubles (sauf si vous en avez déjà fait).

Il peut être un peu difficile de visualiser vos valeurs à ce stade. Voici quelques exemples. N'oubliez pas que chaque exemple représente une valeur unique et est échappé en tant que tel. Pour plus de clarté, les espaces seront affichés sous la forme d'un caractère _.

Avant Après
aucune modification aucune modification
aléatoire " double guillemet guillemets doubles aléatoires ""
virgule,séparé "séparés par une virgule"
Deux
lignes
"Deux
lignes"
_de début et une virgule "_espace de début et une virgule"
« citation de début, virgule """guillemet de début, virgule"
_space, virgule
deuxième ligne et guillemet double"
"_espace, virgule
deuxième ligne et guillemets doubles"""

Le moyen le plus simple de gérer toutes ces conditions consiste à écrire une méthode de nettoyage. Des données contestables entrent en jeu, et des valeurs CSV correctes et propres apparaissent. Voici un bon exemple d'implémentation d'une telle méthode.

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

La méthode commence par rechercher les guillemets doubles existants. Cette opération doit être effectuée avant toutes les autres vérifications, car elles impliquent l'encapsulation d'une chaîne avec des guillemets doubles, et il serait gênant de déterminer la différence entre les guillemets doubles qui faisaient partie de la valeur et les guillemets doubles ajoutés précédemment par cette méthode. Il est facile de s'échapper ; il suffit juste de doubler ces éléments. Chaque " devient un "", chaque "" devient """", et ainsi de suite.

Une fois cette condition remplie, toutes les autres conditions (espaces blancs non tronqués, virgules et sauts de ligne) peuvent être vérifiées. Si l'un d'entre eux est présent, placez simplement la valeur entre guillemets doubles.

Notez que ce qui précède utilise un objet StringBuilder, qui ne manipule jamais directement une chaîne brute. En effet, StringBuilder vous permet de manipuler librement la chaîne sans créer de copies provisoires en mémoire. Étant donné que les chaînes en Java sont immuables, chaque ajustement mineur que vous effectuez créera une toute nouvelle chaîne. Lorsque vous parcourez les données d'une feuille de calcul, cela peut s’additionner très rapidement.

Nombre de lignes x Valeurs par ligne x Modifications de la valeur = Nombre total de nouvelles chaînes créées
10 000 10 3 300 000

Haut de page

Étapes suivantes

Maintenant que vous avez reçu un marteau doré, il est normal d'aller à la recherche de ongles. Voici quelques idées pour commencer.

  • Consultez l'exemple de code source d'application qui utilise cette classe pour imprimer un fichier CSV à partir d'un exemple de requête. Elle utilise un nom de fichier de sortie comme paramètre de ligne de commande et affiche la sortie standard par défaut. Utilisez-le comme point de départ, créez quelque chose de génial !
  • Le format CSV n'est qu'un des nombreux formats populaires. Modifiez la classe pour obtenir un format de sortie différent, tel que TSV, YAML, JSON ou XML.
  • Créez une application qui génère des fichiers CSV et les envoie par e-mail une fois l'opération terminée. Créez facilement des rapports mensuels automatisés.
  • Concevez une application qui vous permet de saisir des requêtes de manière interactive, et bénéficiez d'une interface puissante pour explorer vos données.