用 Google Analytics API 为 hexo 博客加上阅读量统计

前言

博客各个页面的 Page View (PV) 统计算是我一直都挺想要的一个功能了,但是现有的解决方案都有点缺点:

  • 不蒜子:这个是我一开始考虑的,但是问题是导入已有的数据有点麻烦而且有点担心跑路
  • LeanCloud:很多地方都推荐这个,但是它的 API 对加载速度影响大而且流程麻烦。

我需要的是一个尽量不影响加载速度,同时数据不易失而且最好避免导入旧数据的方案,思来想去只有从 Google Analytics 获取数据满足需求了,这时候我也发现了hexo-related-popular-posts,但是问题是点击量只是这个 repo 的附属功能,我并不想要相关主题的功能,然后点开 Document 一看全是日语也不方便二次开发,所以决定自己实现。

本文就是简单介绍如何手动实现用 Google Analytics API 为博客加上阅读量统计。

环境准备

hexo 毕竟是一个静态博客,如果想添加阅读量统计不可避免的要引入一个后端,由于我的博客正在使用的评论系统 hashover 是基于 php 的,所以我第一想法就是用 php 来完成这个功能。

php

所以如果你想按照本文的过程来为你的博客加上阅读量,应该先配置一个 php 环境,以 apt 为例

1
apt install php php-fpm composer -y

nginx

然后修改服务器相关配置,这里以 Nginx 为例

1
2
3
4
5
location ~* \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}

之后可以用 phpinfo 来测试下是否配置成功,更多的就不再赘述了。

获取 Google Analytics 相关信息

这里偷懒使用了 v3 的 API,但是由于功能简单其实迁移到 v4 非常容易。

服务账号

根据 Google Analytics 的说法,我们首先需要一个服务账号,创建的过程非常简单,只要按照他说的步骤一步一步来就行了,比如我的是这样的。

要注意的是创建完后会提示你下载 key,这个要保留好,后面会用到。

Google PHP Apiclient

然后我们需要配置依赖,这里可以选择直接下载源码解压或者使用 composer,由于懒得用 scp 传文件我选择用 composer。

1
2
composer require google/apiclient:^2.0
composer install --no-dev

数据视图id

为了获取数据,我们还需要获取 Google Analytics 数据视图的 id。

由于我们希望拿到的是全部阅读量,因此不用任何过滤,直接用整站数据视图即可。

点击数据视图设置就能看到 id 了。

简单的请求转发

由于我们的后端并不需要很复杂的功能,只要简单的按照页面标题获取点击量就可以了,所以这里我直接按照官方的例子改了下

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
header('Content-type: application/json');
/**
* Created by PhpStorm.
* User: Mio
* Date: 2018/7/17
* Time: 23:55
*/

// Load the Google API PHP Client Library.
require_once __DIR__ . '/vendor/autoload.php';

function initializeAnalytics()
{
// Creates and returns the Analytics Reporting service object.

// Use the developers console and download your service account
// credentials in JSON format. Place them in this directory or
// change the key file location if necessary.
$KEY_FILE_LOCATION = '/path/to/your/key.json'; // 填入刚才获取的 key 的路径
// Create and configure a new client object.
$client = new Google_Client();
$client->setApplicationName("Hello Analytics Reporting");
$client->setAuthConfig($KEY_FILE_LOCATION);
$client->setScopes(['https://www.googleapis.com/auth/analytics.readonly']);
$analytics = new Google_Service_Analytics($client);
return $analytics;
}

function getResults($analytics, $profileId, $title) {
// Calls the Core Reporting API and queries for the number of sessions
// for the last seven days.

return $analytics->data_ga->get(
'ga:' . $profileId,
'2017-01-01', // 这个时间足够久远即可
date('Y-m-d'), // 当天日期
'ga:pageviews', // 页面PV
['dimensions' => 'ga:pageTitle', // 按照标题划分
'filters' => 'ga:pageTitle=='.$title, // 按照标题过滤
'samplingLevel' => 'HIGHER_PRECISION']); // 最高精度
}

try {
$clicks = 0;
if(!isset($_GET['title'])){
$clicks = "(╯‵□′)╯︵┴─┴";
}
else {
$analytics = initializeAnalytics();
$results = getResults($analytics, YOUR_PROFILE_ID, rawurlencode(htmlspecialchars_decode($_GET['title']))); // 填入你的数据视图 id
$rows = $results->getRows();
switch (count($rows)) {
case 0:
$clicks = 0;
break;
case 1:
$clicks = $rows[0][1];
break;
default:
$clicks = "(╯‵□′)╯︵┴─┴";
break;
}
}
echo json_encode(['clicks' => $clicks]);
exit(0);
}
catch (Exception $e){
echo json_encode(["clicks" => "(╯‵□′)╯︵┴─┴"]);
exit(0);
}

注意这里要填入之前下载 key 的路径和你自己数据视图的 id。

然后现在只要在 GET 请求中带上标题就可以获取到阅读量了,比如对于我的博客访问这里就可以获取主页的点击量。

当然如果你发现了什么注入漏洞也欢迎告诉我…

前端

完成了后端后接下来需要的就是前端展示了。

以下都是建立在 NexT 主题的基础上,如果你使用其他的主题道理也是类似的。

发送请求

由于 NexT 中使用了 jQuery 因此前端代码编写就很简单了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var title = document.querySelector('title').innerHTML;

var setClicks = function(data){
var spans = document.querySelectorAll('#clicks');
var clicks = "(╯‵□′)╯︵┴─┴";
if(data.hasOwnProperty('clicks'))
clicks = data['clicks'];
for(var i=0;i<spans.length;i++)
spans[i].innerHTML = clicks;
}

$.ajax({
'url' : '/clicks/clicks.php',
'data' : {
'title' : title
},
'dataType' : 'json',
'method' : 'GET',
'success' : setClicks,
'error' : setClicks
});

把这个文件放到 next 主题的 source/js 下,但是这时候访问的话这个 js 还不会被加载。

接着修改 layout/_layout.swig 在最后引入我们的 js 文件。

1
2
3
4
5
6
7
8
9
10
11

...
...
...
{% include '_third-party/pangu.swig' %}
{% include '_third-party/scroll-cookie.swig' %}
{% include '_third-party/exturl.swig' %}
{% include '_third-party/bookmark.swig' %}
<script type="text/javascript" src="{{ url_for(theme.js) }}/src/clicks.js"></script>
</body>
</html>

现在只要页面中 id 为 clicks 的 span 的内容都会被延迟加载为阅读量了。

展示数据

接下来就是要在哪里展示阅读量了。

由于我是前端废,所以干脆就用原来主题的 CSS 并且尽量不破坏原主题的一体性,所以我选择的时候在文章下方发表时间和分类之间加上阅读量,同时在每页的页脚添加访问量展示,因此需要分别修改 layout/_partials/footer.swiglayout/_marcro/post.swig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
...
...
{% if theme.footer.powered %}
<div class="powered-by">{#
#}{{ __('footer.powered', '<a class="theme-link" target="_blank"' + nofollow + ' href="https://hexo.io">Hexo</a>') }}{#
#}</div>
{% endif %}
<div class="powered-by">
<span class="post-meta-divider">|</span>
当前页面阅读量
<span id='clicks'>>_<</span>
</div>
{% if theme.footer.powered and theme.footer.theme.enable %}
<span class="post-meta-divider">|</span>
{% endif %}
...
...
...

post.swig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
...
...
</span>

{% if not is_index %}
<i class="fa fa-mouse-pointer" aria-hidden="true"></i>
<span class="post-meta-divider">|</span>
<span class="post-meta-item-text">阅读量:</span>
<span id = "clicks">>_<</span>
{% endif %}

{% if post.categories and post.categories.length and theme.post_meta.categories %}
...
...
...

到此就大功告成了,现在打开博客就可以看到每个页面的阅读量了。

后记

到这里反而有点怀念以前的 WordPress 了,省心的多。

不过既然是为了安全和简洁选择了 hexo,那就还是得有折腾的精神呀。

以后最好能在主页的每篇文章上加上阅读量,有时间再做吧(挖坑)。

参考资料

Google Analytics(分析)API 入门:适用于服务帐号的 PHP 快速入门