Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@rickt
Last active April 8, 2016 02:48
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rickt/0c701ea850f6fc4a3301 to your computer and use it in GitHub Desktop.
Save rickt/0c701ea850f6fc4a3301 to your computer and use it in GitHub Desktop.
example Go code showing how to download reporting data from Google Analytics using the Core Reporting API (updated 2015)
//
// EDIT: this code is now old
// i have recently (april 2016) updated it to use the new golang.org/x/oauth2 lib
// you can get the new analytics dumper at the below url:
// https://gist.github.com/rickt/d839564155cac15d59b6027668d8cb64
//
package main
import (
"code.google.com/p/goauth2/oauth"
"code.google.com/p/goauth2/oauth/jwt"
"code.google.com/p/google-api-go-client/analytics/v3"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"time"
)
// constants
const (
// don't change these
dateLayout string = "2006-01-02" // date format that Core Reporting API requires
)
// your globals (change these)
var (
gaServiceAcctEmail string = "your-applications-email-address@developer.gserviceaccount.com" // email address of registered application
gaServiceAcctSecretsFile string = "/path/to/client_secret_your-application.apps.googleusercontent.com.json" // your application's JSON file downloaded from Google Cloud Console
gaServiceAcctPEMKey string = "/path/to/your-applications-converted-privatekey.pem" // private key (PEM format) of registered application
gaScope string = "https://www.googleapis.com/auth/analytics.readonly" // scope information for Core Reporting API
gaTableID string = "ga:12345678" // namespaced profile (table) ID of your analytics account/property/profile
)
// analytics-dumper globals (don't change these)
var (
// vars used for the runtime flags
gaDimensions string
gaEndDate string
gaFilter string
gaMetrics string
gaMaxResults int64
gaSortOrder string
help bool
gaStartDate string
)
// types
// struct to read the registered application's JSON secretsfile into
type GAServiceAcctSecretsConfig struct {
ClientEmail string `json:"client_email"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectURIs []string `json:"redirect_uris"`
Scope string
AuthURI string `json:"auth_uri"`
TokenURI string `json:"token_uri"`
}
// func: init()
// initialisation function for the command line flags/options.
func init() {
flag.BoolVar(&help, "h", false, "show all command line arguments.")
flag.StringVar(&gaDimensions, "d", "", "GA query dimensions")
flag.StringVar(&gaEndDate, "ed", "", "GA query end date")
flag.StringVar(&gaFilter, "f", "", "GA query filter")
flag.Int64Var(&gaMaxResults, "x", 10000, "GA maximum # of results to return (default: 10000)")
flag.StringVar(&gaMetrics, "m", "ga:pageviews", "GA metrics you want to query (default: ga:pageviews)")
flag.StringVar(&gaSortOrder, "s", "", "GA query reply sort order")
flag.StringVar(&gaStartDate, "sd", "2013-10-01", "GA query start date (default: 2013-10-01)")
}
// func: readGAServiceAcctSecretsConfig()
// reads in the registered application's JSON secretsfile and crafts an oauth.Config which
// it returns to the calling func. exits hard if either the JSON secretsfile load fails,
// or if the unmarshal fails.
func readGAServiceAcctSecretsConfig() *oauth.Config {
configfile := new(GAServiceAcctSecretsConfig)
data, err := ioutil.ReadFile(gaServiceAcctSecretsFile)
if err != nil {
log.Fatal("error reading GA Service Account secret JSON file -", err)
}
// unmarshal the JSON into our configfile struct
err = json.Unmarshal(data, &configfile)
if err != nil {
log.Fatal("error unmarshalling GA Service Account secret JSON file -", err)
}
// ok, we've made it this far. build & return the address to the filled oauth.Config struct
return &oauth.Config{
ClientId: configfile.ClientId,
ClientSecret: configfile.ClientSecret,
Scope: gaScope,
AuthURL: configfile.AuthURI,
TokenURL: configfile.TokenURI,
}
}
// func: main()
// the main function.
func main() {
// grok the command line args
flag.Usage = usage
flag.Parse()
if help {
flag.Usage()
}
// read in the registered application's JSON secretsfile and get a ptr to our oauth.Config in return.
oauthConfig := readGAServiceAcctSecretsConfig()
// load up the registered applications private PEM key.
pemKey, err := ioutil.ReadFile(gaServiceAcctPEMKey)
if err != nil {
log.Fatal("error reading GA Service Account PEM key -", err)
}
// create a JWT (JSON Web Token) by sending jwt.NewToken the service account email address, the Scope element of our
// oauthConfig struct, and the PEM key.
jsonwebToken := jwt.NewToken(gaServiceAcctEmail, oauthConfig.Scope, pemKey)
// create an authenticated HTTP client (expired tokens get refreshed automatically)
transport, err := jwt.NewTransport(jsonwebToken)
if err != nil {
log.Fatal("failed to create authenticated transport -", err)
}
// create a new analytics service by passing in the authenticated http client
analyticsService, err := analytics.New(transport.Client())
if err != nil {
log.Fatal("error creating Analytics Service -", err)
}
// create a new analytics data service by passing in the analytics service we just created
dataGaService := analytics.NewDataGaService(analyticsService)
// w00t! now we're talking to the core reporting API. the hard stuff is over, lets setup a simple query.
var dimensions, filter, sortorder string
// if no gaEndDate specified via command line args, set it to today's date.
if gaEndDate == "" {
t := time.Now()
gaEndDate = t.Format(dateLayout)
}
// setup the query, call the Analytics API via our analytics data service's Get func with the table ID, dates & metric variables
dataGaGetCall := dataGaService.Get(gaTableID, gaStartDate, gaEndDate, gaMetrics)
// now we can build the analytics query!
// setup the dimensions (if none specified at runtime)
if gaDimensions != "" {
dimensions = fmt.Sprintf("ga:dimensions=%s", gaDimensions)
dataGaGetCall.Dimensions(dimensions)
}
// setup the sort order (if none specified at runtime)
if gaSortOrder != "" {
sortorder = fmt.Sprintf("ga:sort=%s", gaSortOrder)
dataGaGetCall.Sort(sortorder)
}
// setup the filter (if none specified at runtime)
if gaFilter != "" {
filter = fmt.Sprintf("%s", gaFilter)
dataGaGetCall.Filters(filter)
}
// setup the max results we want returned.
dataGaGetCall.MaxResults(gaMaxResults)
// send the query to the API, get a big fat gaData back.
gaData, err := dataGaGetCall.Do()
if err != nil {
log.Fatal("API error -", err)
}
// we've made it this far, so lets print info about the query
fmt.Printf("gaStartDate=%s, gaEndDate=%s\n", gaStartDate, gaEndDate)
fmt.Printf("gaDimensions=%s\n", dimensions)
fmt.Printf("gaFilter=%s\n", filter)
fmt.Printf("gaMetrics=%s\n", gaMetrics)
fmt.Printf("gaSortOrder=%s\n", sortorder)
// how much data did we get back?
fmt.Printf("len(gaData.Rows)=%d, TotalResults=%d\n", len(gaData.Rows), gaData.TotalResults)
// lets print out the returned data via a lazy loop through the returned rows
for row := 0; row <= len(gaData.Rows)-1; row++ {
fmt.Printf("row=%d %v\n", row, gaData.Rows[row])
}
}
// func: usage()
// prints out all possible flags/options when "-h" or an unknown option is used at runtime.
// exits back to shell when complete.
func usage() {
fmt.Printf("usage: %s [OPTION] \n", os.Args[0])
flag.PrintDefaults()
os.Exit(2)
}
// example runs:
// 1. shows total pageview count for all URLs (starting at default date october 1st through today).
// $ ./analytics-dumper
// gaStartDate=2013-10-01, gaEndDate=2013-10-18
// gaMetrics=ga:pageviews
// gaFilter=ga:pagePath=~^/
// len(gaData.Rows)=1, TotalResults=1
// row=0 [25020179]
//
// 2. shows unique pageview count per URL for top 5 URLs starting in "/blog" (starting at default date october 1st through today),
// in descending order.
// $ ./analytics-dumper -m=ga:uniquePageviews -d=ga:pagePath -s=-ga:uniquePageviews -f=ga:pagePath=~^/blog -x=5
// gaStartDate=2013-10-01, gaEndDate=2013-10-18
// gaMetrics=ga:uniquePageviews
// gaDimensions=ga:dimensions=ga:pagePath
// gaSortOrder=ga:sort=-ga:uniquePageviews
// gaFilter=ga:pagePath=~^/movie
// len(gaData.Rows)=5, TotalResults=10553
// row=0 [/blog/foo 10056]
// row=1 [/blog/bar 3721]
// row=2 [/blog/baz 3129]
// row=3 [/blog/qux 2487]
// row=4 [/blog/xyzzy 857]
//
// 3. shows unique pageview count per URL for top 5 URLs from Aug 1 --> Sep 1, in descending order.
// $ ./analytics-dumper -sd=2013-08-01 -ed=2013-09-01 -m=ga:uniquePageviews -d=ga:pagePath -s=-ga:uniquePageviews -x=5
// gaStartDate=2013-08-01, gaEndDate=2013-09-01
// gaMetrics=ga:uniquePageviews
// gaDimensions=ga:dimensions=ga:pagePath
// gaSortOrder=ga:sort=-ga:uniquePageviews
// len(gaData.Rows)=5, TotalResults=159023
// row=0 [/ 42801]
// row=1 [/product/foo 28763]
// row=2 [/product/bar 23505]
// row=3 [/blog/foo 10056]
// row=4 [/blog/bar 3721]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment