从 Google Analytics 获取 Pageviews
人的欲望总是不断膨胀的,笔者是凡人,也难逃此劫。近来,它滋生成为对博客功能的一个新需求:获取 GA (Google Analytic) 的 Pageviews。
本站开建早期,就嵌入了 GA 的数据收集代码。它的功能仅限搜集跟踪记录并上传,没法同时返回统计信息。 于是调研 Google 相关开发手册,得知 GA 中一个称为 Reporting
的组件,内含几个 API 对外提供处理后的数据,其中 Core Reporting API
支持按特定的维度、指标查询记录。所以使用这部分 API 就能得到所需要的 Pageviews 了。
根据 Core Reporting API 的教程。通过 JS 获取的 GA 报告,需要在代码提供 Google APIs 项目的一个信任凭证、OAuth client ID
,以及 GA 的 Account Explorer 的 viewID。这三个都是站点拥有者登陆控制台才能看到的私人信息,即便可以在设置域名过滤来限定这几个值只能在自己名下的站点使用,但是直接暴露在 JS 中对外可见,似乎不太优雅。因此,从 Web 调用 Core Reporting API
读取 Pageviews 的可行性是直接否定了。
不久后,找到了一个对外安全获取 GA 数据的方案:GA Super Proxy。SuperProxy 涵盖了所有 GA Reporting
组件的 API,上述的凭证、ID 之类的敏感信息可以写入 SuperProxy 的配置,由它代为请求数据。将 SuperProxy 部署在 GAE (Google App Engine) 上,敏感信息对外不可见,这样就成了一个持续公开指定 GA 数据的广播站了,网站再从 GAE 读取数据并展示即可,美滋滋。
SuperProxy 配置与部署
第一步是攻克 SuperProxy。项目仓库 README 有介绍安装步骤以及解说 Live,强烈建议动手前看一下作者的 Live 视频,从项目架构原理到实际操作都有详细表述。请注意项目发布距今已有 5 年(2013 年发布),GCP (Google Cloud Platform) 布局已经不可同日而语,项目 README 以 Live 的部分操作内容已经过时,所以更新描述一下当前最新环境的操作。
Google APIs 创建项目
用 Google 账户登陆 Google APIs Dashboard,Create Project新建一个 Project,如起名cotes-blog-ga
,”Location” 项默认为 No organization
。
新建完毕后,为项目开启 API 和服务。+ ENABLE APIS AND SERVICES进入 API Library
,搜索栏中搜关键词 “analytic” 即可找到 Analytics API
,点击图标进去 Enable 此 API 服务即可。
开启 API 后页面会自动回到 Dashboard,根据 ⚠️ 信息提示点击 Create credentials 为 API 创建 credentials。创建页面作如下操作:
- Find out what kind of credentials you need
- Where will you be calling the API from? 下拉选择
Web brower(Javascript)
- What data will you be accessing? 选择
User data
- Create an OAuth 2.0 client ID
- Client ID 自定义命名,笔者为
blog-oauth
- Restrictions 两项暂时留空,往后将会写入 GAE 的项目地址。
- Set up the OAuth 2.0 consent screen
- Email 保持默认值
- 产品名称自定义命名,不与其他公司产品重名即可,例笔者为
cotes-blog-ga
- Download credentials
- 视个人需要决定下载与否,供 SuperProxy 使用的
Client ID
,Client secret
都可以在 Dashboard 直接查看。
完成后即可生成新 OAuth 2.0 client ID:
上述 API Dashboard 的操作就算过程中的填写信息有纰漏也不用担心,API Dashboard 相互之间的各个按钮开关总是可以跳转到所需页面,十分友好。
下载配置 SuperProxy
在本地满足环境依赖:
墙内的童鞋可能在本地 Cloud SDK 登陆 Google 账户会受限,可以考虑使用 Cloud SDK 网络代理 解决。
接着下载 SuperProxy 项目,SuperProxy 的配置基本按照作者的 README 介绍修改即可。
1. 修改 src/config.py
OAUTH_CLIENT_ID
- 填入上一步创建的Client ID
OAUTH_CLIENT_SECRET
- 填入Client secret
OAUTH_REDIRECT_URI
- 填入 GAE 派发的免费域名https://PROJECT_ID.appspot.com
,其中PROJECT_ID
在 Google APIs Dashboard 或者其他任一 GCP 页面中,点击顶栏项目名即可查看。OAUTH_REDIRECT_URI
- 配置内默认在地址尾部添加了/admin/auth
,所以 URI 全貌为:https://PROJECT_ID.appspot.com/admin/auth
。
此时再返回上一步的 Credentials,点击 OAuth 2.0 client IDs 中的 OAuth ID,在设置页面的 Authorized redirect URIs 填入 SuperProxy 中 OAUTH_REDIRECT_URI
的完整地址,例如,笔者是:https://cotes-blog-ga-214617.appspot.com/admin/auth
。
2. 修改 src/app.yaml
src/app.yaml
文件首部两行:application
与 version
,在 Cloud SDK 213.0.0 中已经标记为无效字段了,需要将其删除,否则部署时会出现警告而导致中断。
1
2
- application: your-project-id
- version: 1
上传 SuperProxy 至 GAE
进入 SuperProxy 源码 src/
目录,使用 Cloud SDK 命令上传:
1
$ gcloud app deploy app.yaml index.yaml --project PROJECT_ID
PROJECT_ID
替换成项目 ID,笔者的是:cotes-blog-ga-214617
GAE 上创建查询
登陆 https://PROJECT_ID.appspot.com/admin
,验证账户后创建查询。GA Core Reporting API 查询请求可以在 Query Explorer 创建。因为要查询的是 Pageviews,所以笔者的查询参数如下:
start-date
- 填写博客发布首日end-date
- 填today
(这是 GA Report 支持的参数,表示永远按当前查询日期为止)metrics
- 选择ga:pageviews
dimensions
- 选择ga:pagePath
为了减少返回结果,减轻网络带宽,所以增加自定义过滤规则1:
filters
- 填写ga:pagePath!@=;ga:pagePath!@(
其中
;
表示用 逻辑与 串联两条规则,!@=
表示不含=
,!@(
表示不含(
。
Run Query 之后在页面底部拷贝 API Query URI 生成内容,填至 GAE 上 SuperProxy 的 Encoded URI for the query 即可。
GAE 上保存查询后,会生成一个 Public Endpoint(公开的访问地址),用户访问它将返回 JSON 格式的查询结果。最后,在 Public Request Endpoint 点击 Enable Endpoint 使查询生效,Scheduling 中点击 Start Scheduling 开启定时任务。
Web 端处理 GA 数据
Web 中使用 AJAX 获取 pageviews 数据,动态刷新到页面指定位置。关键代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
$.ajax({
url: "PUBLIC_ENDPOINT", // Replace with SuperProxy Public endpoint
dataType: "jsonp", // for cross-origin access
timeout: 1000 * 10, // 10 secs
success: function (data) {
displayPageviews(data.rows); // Do what ever you want.
},
error: function () {
console.log("Failed to load Pageviews!");
},
});
展示 Pageviews 的逻辑函数 displayPageviews
可以根据个人的实际需求实现,笔者需要的是每篇 Post 首部展示 Pageviews,所以在页面指定位置添加 HTML 元素 <span id="pageviews"></span>
,JS 逻辑实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function displayPageviews(rows) {
if (rows === undefined) {
return;
}
var curPath = window.location.pathname;
var curFile = curPath.slice(curPath.lastIndexOf("/") + 1); // Sometimes posts will be moved.
var len = rows.length;
var cnt = 0;
for (var i = 0; i < len; ++i) {
var gaPath = rows[i][0];
var gaFile = gaPath.slice(gaPath.lastIndexOf("/") + 1);
if (gaPath === curPath || gaFile === curFile) {
cnt += parseInt(rows[i][1]);
}
}
$("#pageviews").text(cnt);
}
不出意外的话,现在 GitHub 上部署的博客可以顺利展示 Pageviews 了。
扩展:墙内访问 GAE
经过上文一通猛如虎的操作,GitHub 托管的站点是解决了 Pageviews 问题。但是如果访问者身处「全球最大的局域网」内呢?那么上述的 Web 端处理手段就要失效了,发出的请求,在光明降临这片土地之前,永远无法得到返回。破此困局,须借道海外 VPS,另外如果还有个私人域名的话就更佳了。下面说一下笔者的解决方案:
VPS 运行在海外,且能被国内正常访问。VPS 上通过 Nginx 对 GAE Application 域名作反向代理,墙内用户即可通过 VPS 访问到 GAE 上的数据。笔者的 VPS 公网地址绑定了私人域名 api.cotes.info
,并且申请了 SSL 证书。
那么,Nginx 的反代 GAE 的关键配置如下:
注:Nginx 监听 SSL 需要事先安装相应的模块。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 443 ssl;
ssl on;
server_name api.cotes.info;
ssl_certificate /etc/letsencrypt/live/api.cotes.info/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.cotes.info/privkey.pem;
location /ga/ {
proxy_redirect off;
proxy_set_header Host cotes-blog-ga-214617.appspot.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://cotes-blog-ga-214617.appspot.com;
rewrite ^/ga/(.*)$ /$1 break;
}
}
这份配置为域名 api.cotes.info
开启了 SSL,把笔者的 GAE Application 域名 https://cotes-blog-ga-214617.appspot.com
代理至墙内可见的地址 https://api.cotes.info/ga/
。另外,proxy_set_header Host
很关键,GAE 需要根据它寻找你的 Application。
Web 端的 AJAX 也要扩展:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$.ajax({
url: "https://cotes-blog-ga-214617.appspot.com/query?id=ahZifmNvdGVzLWJsb2ctZ2EtMjE0NjE3chULEghBcGlRdWVyeRiAgICA3pCBCgw",
dataType: "jsonp", // for cross-origin access
timeout: 1000 * 5, // 5 secs
success: function (data) {
displayPageviews(data.rows);
},
error: function () {
// Get GA-reports by proxy
$.ajax({
url: "https://api.cotes.info/ga/query?id=ahZifmNvdGVzLWJsb2ctZ2EtMjE0NjE3chULEghBcGlRdWVyeRiAgICA3pCBCgw",
dataType: "jsonp",
timeout: 1000 * 5,
success: function (data) {
console.log("Load gae from proxy.");
displayPageviews(data.rows);
},
error: function () {
console.log("Failed to get pageviews from proxy.");
},
});
},
});
上述代码(第 13 行)增加对 GAE 代理地址的请求,如此一来,墙内用户可以享受到无差别的数据服务了。