Alexander Lucas, Team API di Google Analytics – Agosto 2010
Introduzione
Questo articolo spiega come recuperare i dati di qualsiasi query effettuata all'API di esportazione dei dati di Google Analytics e generare i risultati nel formato CSV più comune. Questa è una delle attività più comuni eseguite dagli utenti con i dati di Analytics estratti dall'API di esportazione dei dati. Automatizzare il processo è un modo semplice per risparmiare tempo e denaro. Inoltre, se disponi del codice per stampare i documenti CSV dalle query, potrai integrarlo in progetti più grandi, come generatori automatici di report, mailer ed funzioni di "esportazione" per le dashboard personalizzate che hai scritto.
Prima di iniziare
Otterrai il massimo da questo articolo se disponi di quanto segue:
- Una conoscenza pratica di Java.
- Conoscenza pratica di Google Analytics, inclusa la comprensione di dimensioni e metriche.
- Accesso a un account Google Analytics attivo con dati reali.
- Dimestichezza con la guida introduttiva a Java dell'API di esportazione dei dati. Questo articolo presuppone che tu conosca già tutti gli argomenti trattati nella guida.
- Una copia locale del codice sorgente completo, disponibile all'indirizzo AnalyticsCvsPrinter.java. Un'applicazione di esempio che utilizza questo codice è disponibile in AnalyticsCsvDemo.java.
Panoramica del programma
Il codice descritto in questo articolo ha le seguenti caratteristiche:
- Abilita la scelta in fase di runtime se il codice viene stampato sulla console o in uno stream di file.
- Dato un oggetto
DataFeed
come parametro, stampa i dati in formato CSV:- Stampa intestazioni di riga.
- Stampa righe di dati, dove ogni
DataEntry
costituisce una riga nell'output risultante. - Esegui ogni valore tramite un metodo di sanitizzazione per un output compatibile con CSV.
- Scrivi un metodo "Sanitizer" che renda tutti gli input sicuri per i file CSV.
- Fornirti una classe Java che possa elaborare qualsiasi query dell'API di esportazione dei dati e trasformarla in un file CSV.
Consenti flussi di output configurabili
La prima cosa da fare è configurare uno stream di output configurabile
per la stampa. In questo modo qualsiasi codice che utilizza la tua classe può decidere se l'output deve passare allo standard o direttamente a un file. Devi solo configurare il metodo getter/setter per un oggetto PrintStream
. che sarà il target di tutte le
stampe eseguite dalla classe.
private PrintStream printStream = System.out; public PrintStream getPrintStream() { return printStream; } public void setPrintStream(PrintStream printStream) { this.printStream = printStream; }
Anche impostare l'output su un file è molto semplice. È sufficiente il nome del file per creare un oggetto PrintStream
per il file.
FileOutputStream fstream = new FileOutputStream(filename); PrintStream stream = new PrintStream(fstream); csvprinter.setPrintStream(stream);
Ripetizione dei dati
La prima riga del file CSV è la riga dei nomi delle colonne. Ogni colonna rappresenta una dimensione o una metrica del feed di dati; pertanto, per stampare questa prima riga, procedi nel seguente modo.
- Prendi la prima voce dal feed.
- Esegui l'iterazione dell'elenco di dimensioni utilizzando il metodo
getDimensions
di tale voce. - Stampa il nome di ogni dimensione utilizzando il metodo
Dimension.getName()
, seguito da una virgola. - Ripeti l'operazione per le metriche utilizzando il metodo
getMetrics()
. Stampa le virgole dopo tutte le metriche tranne l'ultima.
Di seguito è riportata un'implementazione del metodo per stampare le intestazioni di riga. Tieni presente che questo codice non restituisce una stringa che rappresenta la riga completa, ma viene stampato in un flusso di output durante l'elaborazione dei valori.
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(); }
La stampa del "corpo" del file CSV (tutto ciò che si trova sotto la riga dei nomi delle colonne) è molto simile. Ci sono solo due differenze fondamentali. Innanzitutto, non riguarda solo
la prima voce oggetto di valutazione. Il codice deve scorrere tutte le voci
nell'oggetto del feed. In secondo luogo, invece di utilizzare il metodo getName()
per estrarre il valore da sanitizzare e stampare, usa
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(); }
Questo codice suddivide il feed in voci, mentre le voci in valori da stampare per l'output. Ma come possiamo rendere questi valori compatibili con i file CSV? Cosa succede se un valore nel file "valori separati da virgole" contiene una virgola? Questi valori devono essere bonificati.
Come eliminare i dati per garantirne la compatibilità con i file CSV
CSV è un formato semplice. Un file CSV rappresenta una tabella di dati e ogni riga rappresenta una riga in quella tabella. I valori nella riga sono separati da virgole. Una nuova riga indica una nuova riga di dati.
Sfortunatamente, questo formato semplice consente ingannevolmente di eliminare i dati con dati errati. Che cosa succede se il valore contiene una virgola? Che cosa succede se uno dei valori include interruzioni di riga al suo interno? Cosa dovrebbe succedere con lo spazio tra virgole e valori? Tutte queste situazioni possono essere prese in considerazione con poche semplici regole.
- Se la stringa contiene un carattere apice, inserisci l'escape di una seconda virgoletta.
- Se la stringa contiene una virgola, racchiudi l'intera stringa tra virgolette (a meno che tu non la abbia già specificata).
- Se la stringa contiene un'interruzione di riga, racchiudi l'intera stringa tra virgolette (a meno che non l'hai già fatto).
- Se la stringa inizia o termina con uno spazio vuoto, racchiudi l'intera stringa tra virgolette (a meno che tu non l'abbia già fatto).
Può essere un po' difficile visualizzare i valori a questo punto, ecco alcuni esempi. Ricorda che ogni esempio rappresenta un singolo valore e come tale contiene i caratteri di escape. Per chiarezza, gli spazi saranno rappresentati come un carattere _.
Prima | Dopo |
---|---|
invariata | invariata |
virgolette " casuali | virgolette "" casuali |
virgole,separati | "virgola,separato" |
Due righe |
"Due righe" |
spazio _principale e una virgola | "spazio_principale e una virgola" |
"citazione iniziale, virgola | """citazione iniziale, virgola" |
_space, virgola seconda riga e virgolette doppie" |
"_spazio, virgola seconda riga e virgolette doppie"" |
Il modo più semplice per gestire tutte queste condizioni è scrivere un metodo di sanitizzazione. Vengono inseriti dati discutibili e valori CSV validi e puliti. Ecco un esempio di implementazione di questo metodo.
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(); }
Il metodo inizia controllando la presenza di virgolette doppie esistenti. Questa operazione deve essere eseguita prima di tutti gli altri controlli, poiché prevede l'avvolgimento di una stringa tra virgolette doppie e sarebbe fastidioso determinare la differenza tra le virgolette doppie che facevano parte del valore e le virgolette doppie aggiunte in precedenza con questo metodo. Sono facili da evitare: devono solo essere raddoppiati. Ogni " diventa "", ogni "" diventa """" e così via.
Una volta soddisfatta questa condizione, puoi controllare tutte le altre condizioni (spazio vuoto non tagliato, virgole e interruzioni di riga). Se sono presenti anche altri valori, racchiudi il valore tra virgolette doppie.
Tieni presente che quanto riportato sopra utilizza un oggetto StringBuilder
e non manipola mai direttamente una stringa non elaborata. Questo perché StringBuilder
ti consente di manipolare liberamente la stringa senza creare copie intermedie in memoria. Poiché le stringhe in Java sono immutabili, ogni piccolo ritocco creerà una stringa completamente nuova. Quando si scorrono i dati dei fogli di lavoro, la somma può essere aggiunta molto rapidamente.
Numero di righe | x valori per riga | x Il valore cambia | = Totale nuove stringhe create |
---|---|---|---|
10.000 | 10 | 3 | 300.000 |
Cosa devo fare adesso?
Ora che ti hanno dato un martello d'oro, è naturale andare a caccia di chiodi. Ecco alcune idee per iniziare.
- Dai un'occhiata al codice sorgente dell'applicazione di esempio che utilizza questa classe per stampare un file CSV sulla base di una query di esempio. Prende un nome file di output come parametro della riga di comando e lo visualizza come standard. Usalo come punto di partenza per creare qualcosa di fantastico.
- Il CSV è solo uno dei tanti formati più popolari. Modifica la classe in modo che generi un output in un formato diverso, come TSV, YAML, JSON o XML.
- Scrivi un'applicazione che generi file CSV e li invii per posta al termine dell'operazione. Creazione di report mensili automatici e facili da usare.
- Scrivi un'applicazione che consenta di inserire le query in modo interattivo, per un'interfaccia potente per analizzare i dati.