Posts 从 Google Analytics 获取 Pageviews
Post
Cancel

从 Google Analytics 获取 Pageviews

人的欲望总是不断膨胀的,笔者是凡人,也难逃此劫。近来,它滋生成为对博客功能的一个新需求:获取 GA 的 Pageviews。

本站开建早期,就嵌入了 GA (Google Analytic) 的数据收集代码。它的功能仅限搜集跟踪记录并上传,没法同时返回统计信息。 于是调研 Google 相关开发手册,得知 GA 中一个称为Reporting的组件,内含几个 API 对外提供处理后的数据,其中Core Reporting API支持按特定的维度、指标查询记录。所以研究这部分 API 就能得到所需要的 Pageviews 了。

仔细学习了Core Reporting API 的教程。通过 Web JS 获取的 GA 报告,需要在代码提供 Google APIs 项目的一个信任凭证、OAuth client ID,以及 GA 的 Account Explorer 的 viewID。很不幸,这三个都是站点拥有者的私密信息,泄露在 JS 中对 GA 账户数据有着极大危险。因此,从信息安全角度来说,Web 内直接用Core Reporting API的可行性是直接否定了。

不久后,找到了一个对外安全获取 GA 数据的方案:GA superProxy。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 DashboardCreate 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。创建页面作如下操作:

  1. 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
  2. Create an OAuth 2.0 client ID
    • Client ID 自定义命名,笔者为blog-oauth
    • Restrictions 两项暂时留空,往后将会写入 GAE 的项目地址。
  3. Set up the OAuth 2.0 consent screen
    • Email 保持默认值
    • 产品名称自定义命名,不与其他公司产品重名即可,例笔者为cotes-blog-ga
  4. Download credentials
    • 视个人需要决定下载与否,供 superProxy 使用的Client IDClient 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_IDOAUTH_CLIENT_SECRET,分别填入上一步创建的Client IDClient secret

OAUTH_REDIRECT_URI填入 GAE 派发的免费域名,格式为https://PROJECT_ID.appspot.comPROJECT_IDGoogle 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文件首部两行:applicationversion,在 Cloud SDK 213.0.0 中已经标记为无效字段了,需要将其删除,否则部署时会出现警告而导致中断。

1
2
3
4
5
6
7
diff --git a/src/app.yaml b/src/app.yaml
index e4d3d44..c4fd4cd 100644
--- a/src/app.yaml
+++ b/src/app.yaml
@@ -1,5 +1,3 @@
-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-datetoday(这是 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.in,并且申请了 SSL 证书。

那么,Nginx 的反代 GAE 的关键配置如下:

:Nginx 监听 SSL 需要事先安装相应的模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server {

   listen   443 ssl;

   ssl on;
   server_name api.cotes.in;

   ssl_certificate /etc/letsencrypt/live/api.cotes.in/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/api.cotes.in/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.in开启了 SSL,把笔者的 GAE Application 域名https://cotes-blog-ga-214617.appspot.com代理至墙内可见的地址https://api.cotes.in/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.in/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 代理地址的请求,如此一来,墙内运行的站点可以轻松愉快的访问 GAE 数据了。

参考资料

引用

OLDER POST NEWER POST

Comments powered by Disqus.

Search Results