Last active
October 8, 2024 12:46
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 = "[email protected]" // 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