前言

本文教程主要针对 Hexo 博客,参考冰老师的 hexo-githubcalendar 插件,对博客站点的每日的访问进行统计,绘制出类似与 GitHub 贡献日历的博客访问日历。

博客访问日历

  • 本文数据来源均为百度统计,请确保博客站点已加入百度统计,以 butterfly 主题为例,可参照 Butterfly 安装文档(四) 主题配置-2 的分析统计段落实现。
  • 本地主机访问(localhost)也会记录到百度统计,推荐在 【百度统计】--【管理】--【统计规则设置】--【过滤规则设置】--【受访域名统计规则】--【勾选排除 localhost(本地主机)】 排除本地主机访问(貌似在勾选后生效,但是以前的访问记录仍会统计)。
  • 本教程将会泄漏属于百度统计的站点 ID 和百度统计 AccessToken,请先前往 百度统计用户手册 了解,介意者请谨慎部署。
  • 2022-03-29 使用 LeanCloud 存储百度统计的 AccessTokenRefreshToken,并使用 Github Action 实现每周更新。

博客访问统计

获取网站统计数据

  1. 已经添加百度统计分析的小伙伴可以登录百度统计的官网网站,此处博主使用的是百度账号登录。

    百度账号登录

  2. 登录进入首页后可以点击基础报告查看网站访问统计数据(若无绑定网站,需要先在 管理页面--账户管理--网站列表 添加博客网址)。

    首页

  3. 此时我们想要获取这些数据可以调用百度统计 API,点击管理,进入管理页面后在左边侧边栏找到数据导出服务

    数据导出服务

  4. 登录百度开发者中心控制台,创建工程,应用名称任意。

    创建工程

  5. 点击刚刚创建的工程,记录下 API Key,Secret Key。

    基本信息

  6. 填写下面链接参数后打开链接获取授权码,具体步骤可以参考 Tongji API 用户手册

    http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope=basic&display=popup

    • API Key:{CLIENT_ID}
    • 回调 URI:{REDIRECT_URI},可以填写 oob

      授权码

  7. 复制第 6 步获取的授权码,填写下面链接参数后打开链接获取 ACCESS_TOKEN :。

    http://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&code={CODE}&client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&redirect_uri={REDIRECT_URI}

    • Auth Code:{CODE},第 6 步获取的授权码。
    • API Key:{CLIENT_ID}
    • Secret Key:{CLIENT_SECRET}
    • 回调 URI:{REDIRECT_URI},可以填写 oob

      ACCESS_TOKEN

    注意:从上述步骤得到的数据中包含 ACCESS_TOKENREFRESH_TOKEN 两个值,其中 ACCESS_TOKEN 的有效期为一个月,REFRESH_TOKEN 的有效期为十年。REFRESH_TOKEN 的作用就是刷新获取新的 ACCESS_TOKENREFRESH_TOKEN , 如此反复操作来实现 ACCESS_TOKEN 有效期永久的机制。

    一旦 ACCESS_TOKEN 过期,可根据以下请求更换新的 ACCESS_TOKENREFRESH_TOKEN

    http://openapi.baidu.com/oauth/2.0/token?grant_type=refresh_token&refresh_token={REFRESH_TOKEN}&client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}

    • API Key:{CLIENT_ID}
    • Secret Key:{CLIENT_SECRET}
    • Refresh Token:{REFRESH_TOKEN}
  8. 调用百度统计 API

    第 7 步获取的 ACCESS_TOKEN 是所调用 API 的用户级参数,结合各 API 的应用级参数即可正常调用 API 获取数据,填写下面链接参数后打开链接获取网站 ID:

    https://openapi.baidu.com/rest/2.0/tongji/config/getSiteList?access_token={ACCESS_TOKEN}

    • Access Token:{ACCESS_TOKEN}

      网址 ID

    也可以在 Tongji API 调试工具 输入 ACCESS_TOKEN 获取网址 ID:

    API 调试工具

  9. Tongji API 调试工具 选择需要获取的报告数据,填写 ACCESS_TOKEN 、必填参数、选填参数获取百度统计数据。

获取百度统计数据

网站统计代码

以 butterfly 主题为例,可以在 [Blogroot]\js\ 目录下新建 visit_calendar.js 文件,然后添加以下内容:

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
var visit_calendar = (site_id, access_token, metrics, visit_color) => {
var date = new Date();
var end_date = '' + date.getFullYear() + (date.getMonth() > 8 ? (date.getMonth() + 1) : ("0" + (date.getMonth() + 1))) + (date.getDate() > 9 ? date.getDate() : ("0" + date.getDate()));
date.setFullYear(date.getFullYear() - 1);
date.setTime(date.getTime() - 24 * 3600 * 1000 * date.getDay());
var start_date = '' + date.getFullYear() + (date.getMonth() > 8 ? (date.getMonth() + 1) : ("0" + (date.getMonth() + 1))) + (date.getDate() > 9 ? date.getDate() : ("0" + date.getDate()));
var metrics_name = (metrics === 'pv_count' ? '次' : (metrics === 'visitor_count' ? '人' : ''));
var visit_apiurl = 'https://baidu-tongji-api.vercel.app/api?site_id=' + site_id + '&access_token=' + access_token + '&method=overview/getTimeTrendRpt' + '&metrics=' + metrics + '&start_date=' + start_date + '&end_date=' + end_date;
var visit_fixed = 'fixed';
var visit_px = 'px';
var visit_month = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
var visit_monthchange = [];
var visit_oneyearbeforeday = '';
var visit_thisday = '';
var visit_amonthago = '';
var visit_aweekago = '';
var visit_weekdatacore = 0;
var visit_datacore = 0;
var visit_total = 0;
var visit_datadate = '';
var visit_visit_data = [];
var visit_positionplusdata = [];
var visit_firstweek = [];
var visit_lastweek = [];
var visit_beforeweek = [];
var visit_thisweekdatacore = 0;
var visit_mounthbeforeday = 0;
var visit_mounthfirstindex = 0;
var visit_crispedges = 'crispedges';
var visit_thisdayindex = 0;
var visit_amonthagoindex = 0;
var visit_amonthagoweek = [];
var visit_firstdate = [];
var visit_first2date = [];
var visit_montharrbefore = [];
var visit_monthindex = 0;
var retinaCanvas = (canvas, context, ratio) => {
if (ratio > 1) {
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
canvas.width = canvasWidth * ratio;
canvas.height = canvasHeight * ratio;
canvas.style.width = '100%';
canvas.style.height = canvasHeight + 'px';
context.scale(ratio, ratio);
}
};

function responsiveChart () {
var ratio = window.devicePixelRatio || 1
var visit_tooltip_container = document.getElementById('visit_tooltip_container');
var visit_x = '';
var visit_y = '';
var visit_span1 = '';
var visit_span2 = '';
if (document.getElementById("visitcanvas")) {
var c = document.getElementById("visitcanvas");
c.style.width = '100%';
c.style.height = '';
var cmessage = document.getElementById("visitmessage");
var ctx = c.getContext("2d");
width = c.width = document.getElementById("visitcalendarcanvasbox").offsetWidth;
height = c.height = 9 * 0.96 * c.width / visit_data.length;
retinaCanvas(c, ctx, ratio)
var linemaxwitdh = height / 9;
var lineminwitdh = 0.8 * linemaxwitdh;
var setposition = { x: 0.02 * width, y: 0.025 * width };
for (var week in visit_data) {
weekdata = visit_data[week];
for (var day in weekdata) {
var dataitem = { date: "", count: "", x: 0, y: 0 };
visit_positionplusdata.push(dataitem);
ctx.fillStyle = visit_thiscolor(visit_color, weekdata[day].count);
setposition.y = Math.round(setposition.y * 100) / 100;
dataitem.date = weekdata[day].date;
dataitem.count = weekdata[day].count;
dataitem.x = setposition.x;
dataitem.y = setposition.y;
ctx.fillRect(setposition.x, setposition.y, lineminwitdh, lineminwitdh);
setposition.y = setposition.y + linemaxwitdh
}
setposition.y = 0.025 * width;
setposition.x = setposition.x + linemaxwitdh
}

if (document.body.clientWidth > 700) {
ctx.font = "600 Arial";
ctx.fillStyle = '#aaa';
ctx.fillText("日", 0, 1.9 * linemaxwitdh);
ctx.fillText("二", 0, 3.9 * linemaxwitdh);
ctx.fillText("四", 0, 5.9 * linemaxwitdh);
ctx.fillText("六", 0, 7.9 * linemaxwitdh);
var monthindexlist = width / 24;
for (var index in visit_monthchange) {
ctx.fillText(visit_monthchange[index], monthindexlist, 0.7 * linemaxwitdh);
monthindexlist = monthindexlist + width / 12
}
}

c.onmousemove = function (event) {
if (document.querySelector('.visitmessage')) {
visit_tooltip_container.innerHTML = ""
}
getMousePos(c, event)
};
visit_tooltip_container.onmousemove = function (event) {
if (document.querySelector('.visitmessage')) {
visit_tooltip_container.innerHTML = ""
}
};

function getMousePos (canvas, event) {
var rect = canvas.getBoundingClientRect();
var x = event.clientX - rect.left * (canvas.width / rect.width);
var y = event.clientY - rect.top * (canvas.height / rect.height);
for (var item of visit_positionplusdata) {
var lenthx = x - item.x;
var lenthy = y - item.y;
if (0 < lenthx && lenthx < lineminwitdh) {
if (0 < lenthy && lenthy < lineminwitdh) {
visit_span1 = item.date;
visit_span2 = item.count;
visit_x = event.clientX - 100;
visit_y = event.clientY - 60;
html = tooltip_html(visit_x, visit_y, visit_span1, visit_span2);
append_div_visitcalendar(visit_tooltip_container, html)
}
}
}
}
}
}

function addlastmonth () {
if (visit_thisdayindex === 0) {
thisweekcore(52);
thisweekcore(51);
thisweekcore(50);
thisweekcore(49);
thisweekcore(48);
visit_thisweekdatacore += visit_firstdate[6].count;
visit_amonthago = visit_firstdate[6].date
} else {
thisweekcore(52);
thisweekcore(51);
thisweekcore(50);
thisweekcore(49);
thisweek2core();
visit_amonthago = visit_first2date[visit_thisdayindex - 1].date
}
}

function thisweek2core () {
for (var i = visit_thisdayindex - 1; i < visit_first2date.length; i++) {
visit_thisweekdatacore += visit_first2date[i].count
}
}

function thisweekcore (index) {
for (var item of visit_data[index]) {
visit_thisweekdatacore += item.count
}
}

function addlastweek () {
for (var item of visit_lastweek) {
visit_weekdatacore += item.count
}
}

function addbeforeweek () {
for (var i = visit_thisdayindex; i < visit_beforeweek.length; i++) {
visit_weekdatacore += visit_beforeweek[i].count
}
}

function addweek (data) {
if (visit_thisdayindex === 6) {
visit_aweekago = visit_lastweek[0].date;
addlastweek()
} else {
lastweek = data.contributions[51];
visit_aweekago = lastweek[visit_thisdayindex + 1].date;
addlastweek();
addbeforeweek()
}
}

function spArr (arr, num) {
let newArr = []
for (let i = 0; i < arr.length;) {
newArr.push(arr.slice(i, i += num));
}
return newArr
}

fetch(visit_apiurl).then(data => data.json()).then(res => {
var date_arr = res.result.items[0];
var value_arr = res.result.items[1];
var total = 0;
var contributions = [];
for (let i = 0; i < date_arr.length; i++) {
if (value_arr[i][0] !== '--') {
contributions.push({ date: date_arr[i][0].replace(/\//g, '-'), count: value_arr[i][0] });
total += value_arr[i][0];
} else {
contributions.push({ date: date_arr[i][0].replace(/\//g, '-'), count: 0 });
}
}
var data = {
total: total,
contributions: spArr(contributions, 7)
};

visit_data = data.contributions;
visit_total = data.total;
visit_first2date = visit_data[48];
visit_firstdate = visit_data[47];
visit_firstweek = data.contributions[0];
visit_lastweek = data.contributions[52];
visit_beforeweek = data.contributions[51];
visit_thisdayindex = visit_lastweek.length - 1;
visit_thisday = visit_lastweek[visit_thisdayindex].date;
visit_oneyearbeforeday = visit_firstweek[0].date;
visit_monthindex = visit_thisday.substring(5, 7) * 1;
visit_montharrbefore = visit_month.splice(visit_monthindex, 12 - visit_monthindex);
visit_monthchange = visit_montharrbefore.concat(visit_month);
addweek(data);
addlastmonth();

var html = visit_main_box(visit_monthchange, visit_data, visit_color, visit_total, visit_thisweekdatacore, visit_weekdatacore, visit_oneyearbeforeday, visit_thisday, visit_aweekago, visit_amonthago);
append_div_visitcalendar(document.getElementById('visit_container'), html);
if (document.getElementById('visit_loading')) {
document.getElementById('visit_loading').remove()
};
responsiveChart()
}).catch(function (error) {
console.log(error)
});
window.onresize = function () {
responsiveChart()
};
window.onscroll = function () {
if (document.querySelector('.visitmessage')) {
visit_tooltip_container.innerHTML = ""
}
};
var visit_thiscolor = (color, x) => {
if (metrics === 'pv_count') {
if (x === 0) {
var i = parseInt(x / 20);
return color[0]
} else if (x < 20) {
return color[1]
} else if (x < 200) {
var i = parseInt(x / 20);
return color[i]
} else {
return color[9]
}
} else {
if (x === 0) {
var i = parseInt(x / 2);
return color[0]
} else if (x < 2) {
return color[1]
} else if (x < 20) {
var i = parseInt(x / 2);
return color[i]
} else {
return color[9]
}
}
};
var tooltip_html = (x, y, span1, span2) => {
var html = '';
html += '<div class="visitmessage" style="top:' + y + 'px;left:' + x + 'px;position: fixed;z-index:9999"><div class="angle-wrapper" style="display:block;"><span>' + span1 + '&nbsp;</span><span>' + span2 + ' ' + metrics_name + '访问</span></div></div>';
return html
};
var visit_canvas_box = () => {
var html = '<div id="visitcalendarcanvasbox"> <canvas id="visitcanvas" style="animation: none;"></canvas></div>';
return html
};
var visit_info_box = (color) => {
var html = '';
html += '<div id="visit_tooltip_container"></div><div class="contrib-footer clearfix mt-1 mx-3 px-3 pb-1"><div class="float-left text-gray">数据来源 <a href="https://tongji.baidu.com/" target="blank">百度统计</a></div><div class="contrib-legend text-gray">Less <ul class="legend"><li style="background-color:' + color[0] + '"></li><li style="background-color:' + color[2] + '"></li><li style="background-color:' + color[4] + '"></li><li style="background-color:' + color[6] + '"></li><li style="background-color:' + color[8] + '"></li></ul>More </div></div>';
return html
};
var visit_main_box = (monthchange, visit_data, color, total, thisweekdatacore, weekdatacore, oneyearbeforeday, thisday, aweekago, amonthago) => {
var html = '';
var canvasbox = visit_canvas_box();
var infobox = visit_info_box(color);
var style = visit_main_style();
html += '<div class="position-relative"><div><span class="visit_title">博客访问日历</span></div><div class="border py-2 graph-before-activity-overview"><div class="js-visitcalendar-graph mx-md-2 mx-3 d-flex flex-column flex-items-end flex-xl-items-center overflow-hidden pt-1 is-graph-loading graph-canvas visitcalendar-graph height-full text-center">' + canvasbox + '</div>' + infobox + '</div></div>';
html += '<div style="display:flex;width:100%"><div class="contrib-column contrib-column-first table-column"><span class="text-muted">过去一年访问</span><span class="contrib-number">' + total + '</span><span class="text-muted">' + oneyearbeforeday + '&nbsp;-&nbsp;' + thisday + '</span></div><div class="contrib-column table-column"><span class="text-muted">最近一月访问</span><span class="contrib-number">' + thisweekdatacore + '</span><span class="text-muted">' + amonthago + '&nbsp;-&nbsp;' + thisday + '</span></div><div class="contrib-column table-column"><span class="text-muted">最近一周访问</span><span class="contrib-number">' + weekdatacore + '</span><span class="text-muted">' + aweekago + '&nbsp;-&nbsp;' + thisday + '</span></div></div>' + style;
return html
};
var visit_main_style = () => {
style = '<style>#visit_container{text-align:center;margin:0 auto;width:100%;padding:10px;display:flex;display:-webkit-flex;justify-content:center;align-items:center;flex-wrap:wrap;}.visit_title{font-size:1rem;font-weight:550;}.visitcalendar-graph text.wday,.visitcalendar-graph text.month{font-size:10px;fill:#aaa;}.contrib-legend{text-align:right;padding:0 14px 10px 0;display:inline-block;float:right;}.contrib-legend .legend{display:inline-block;list-style:none;margin:0 5px;position:relative;bottom:-7px;padding:0;}.contrib-legend .legend li{display:inline-block;width:10px;height:10px;}.text-small{font-size:12px;color:#767676;}.visitcalendar-graph{padding:15px 0 0;text-align:center;}.contrib-column{text-align:center;border-left:1px solid #ddd;border-top:1px solid #ddd;}.contrib-column-first{border-left:0;}.table-column{padding:10px;display:table-cell;flex:1;vertical-align:top;}.contrib-number{font-weight:400;line-height:1.3em;font-size:24px;display:block;}.visitcalendar img.spinner{width:70px;margin-top:50px;min-height:70px;}.monospace{text-align:center;color:#000;font-family:monospace;}.monospace a{color:#1D75AB;text-decoration:none;}.contrib-footer{font-size:11px;padding:0 10px 12px;text-align:left;width:100%;box-sizing:border-box;height:26px;}.left.text-muted{float:left;margin-left:9px;color:#767676;}.left.text-muted a{color:#4078c0;text-decoration:none;}.left.text-muted a:hover,.monospace a:hover{text-decoration:underline;}h2.f4.text-normal.mb-3{display:none;}.float-left.text-gray{float:left;}#user-activity-overview{display:none;}.day-tooltip{white-space:nowrap;position:absolute;z-index:99999;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.85);border-radius:3px;display:none;pointer-events:none;}.day-tooltip strong{color:#dfe2e5;}.day-tooltip.is-visible{display:block;}.day-tooltip:after{position:absolute;bottom:-10px;left:50%;width:5px;height:5px;box-sizing:border-box;margin:0 0 0 -5px;content:" ";border:5px solid transparent;border-top-color:rgba(0,0,0,.85)}.position-relative{width:100%;}@media screen and (max-width:650px){.contrib-column{display:none}}.angle-wrapper{z-index:9999;display:inline;width:200px;height:40px;position:relative;padding:5px 0;background:rgba(0,0,0,0.8);border-radius:8px;text-align:center;color:white;}.angle-box{position:fixed;padding:10px}.angle-wrapper span{padding-bottom:1em;}.angle-wrapper:before{content:"";width:0;height:0;border:10px solid transparent;border-top-color:rgba(0,0,0,0.8);position:absolute;left:47.5%;top:100%;}</style>';
return style
}
};
var append_div_visitcalendar = (parent, text) => {
if (parent !== null) {
if (typeof text === 'string') {
var temp = document.createElement('div');
temp.innerHTML = text;
var frag = document.createDocumentFragment();
while (temp.firstChild) {
frag.appendChild(temp.firstChild)
}
parent.appendChild(frag)
} else {
parent.appendChild(text)
}
}
};
var loading_visit = (color) => {
loading = '<div id="visit_loading" style="width:10%;height:100%;margin:0 auto;display: block"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50" xml:space="preserve"><path fill="' + color + '" d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" transform="rotate(275.098 25 25)"><animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.6s" repeatCount="indefinite"></animateTransform></path></svg></div>';
return loading
};

(function init () {
var visit_container = document.getElementById('visit_container');
var visit_loading = document.getElementById('visit_loading');
var visit_purple = ['#d7dbe2', '#fdcdec', '#fc9bd9', '#fa6ac5', '#f838b2', '#f5089f', '#c4067e', '#92055e', '#540336', '#48022f', '#30021f'];
var visit_yellow = ['#d7dbe2', '#f9f4dc', '#f7e8aa', '#f7e8aa', '#f8df72', '#fcd217', '#fcc515', '#f28e16', '#fb8b05', '#d85916', '#f43e06'];
var visit_green = ['#d7dbe2', '#f0fff4', '#dcffe4', '#bef5cb', '#85e89d', '#34d058', '#28a745', '#22863a', '#176f2c', '#165c26', '#144620'];
var visit_blue = ['#d7dbe2', '#f1f8ff', '#dbedff', '#c8e1ff', '#79b8ff', '#2188ff', '#0366d6', '#005cc5', '#044289', '#032f62', '#05264c'];
var visit_color = visit_purple;
append_div_visitcalendar(visit_container, loading_visit(visit_color[4]));
// 统计访问次数 PV 填写 'pv_count',统计访客数 UV 填写 'visitor_count',二选一
visit_calendar('16****', '121.c644d8c4*****', 'pv_count', visit_color)
})()

项目部署(可选)

使用 Github + LeanCloud + Vercel

整个部署均在云端完成。运行原理:

  1. 数据存储在 LeanCloud 数据库,也可以自行修改源码存储在其他数据库;
  2. 通过 Github Action 更新百度统计的 AccessTokenRefreshToken
  3. Vercel 部署的 API 从 LeanCloud 获取 AccessToken,再执行请求,获取百度统计数据,返回给前端。

LeanCloud 部署

  1. 新建结构化数据,创建 Class,填写名称为 BaiduToken,设置默认 ACL read 和 write 权限都为所有用户;

    LeanCloud 新建 BaiduToken Class

  2. 点击添加列,分别新增 accessTokenrefreshToken

    BaiduToken Class 添加列

  3. 点击新增行,填写前面获取到的 access_tokenrefresh_token

    BaiduToken BaiduToken Class 添加列

GitHub 部署

由于在 js 文件中请求百度统计 API 获取数据会出现跨域请求的错误。

博主参考 Akilar 的 基于 Butterfly 主题的首页置顶 gitcalendar 自建 Vercel API 部署教程,以及冰老师的 python_github_calendar_api 搭建了获取百度统计数据的 Vercel API,力求解决百度统计 API 的跨域请求问题。

虽然 Vercel 的访问应当没有次数限制,但是不排除存在因访问次数过多而限流等限制。所以还是建议各位自建 API。使用 Vercel 部署,完全免费。且无需服务器。

  1. 在 baidu-tongji-api 项目 【Settings】--【Security】--【Secrets】--【Actions】 中添加以下秘钥:

    • APIKEY: 百度统计 API Key
    • SECRETKEY:百度统计 Secret Key
    • APPID:LeanCloud AppID
    • APPKEY:LeanCloud AppKey

    Github Action Secret

Vercel 部署

部署 Vercel 项目后添加 LeanCloud 的环境变量。 Vercel 添加环境变量

添加环境变量完成后,将获取到的默认域名替换 visit_calendar.js 文件中的 visit_apiurl,如:

1
2
- var visit_apiurl = 'https://baidu-tongji-api.vercel.app/api?site_id=' + site_id + '&access_token=' + access_token + '&method=overview/getTimeTrendRpt' + '&metrics=' + metrics + '&start_date=' + start_date + '&end_date=' + end_date;
+ var visit_apiurl = 'https://域名/api?site_id=' + site_id + '&access_token=' + access_token + '&method=overview/getTimeTrendRpt' + '&metrics=' + metrics + '&start_date=' + start_date + '&end_date=' + end_date;

如果使用了数据库存储 visit_apiurl,则可以省略 visit_apiurl 参数。

1
2
3
4
5
6
- var visit_apiurl = 'https://baidu-tongji-api.vercel.app/api?site_id=' + site_id + '&method=overview/getTimeTrendRpt' + '&metrics=' + metrics + '&start_date=' + start_date + '&end_date=' + end_date;
+ var visit_apiurl = 'https://域名/api?site_id=' + site_id + '&method=overview/getTimeTrendRpt' + '&metrics=' + metrics + '&start_date=' + start_date + '&end_date=' + end_date;

// 统计访问次数 PV 填写 'pv_count',统计访客数 UV 填写 'visitor_count',二选一
- visit_calendar('16****', '121.c644d8c4*****', 'pv_count', visit_color)
+ visit_calendar('16****', '', 'pv_count', visit_color)

使用访问日历

在需要显示访问日历的 md 文件中添加以下内容:

1
2
3
<div id="visit_container" style="border-radius: 8px;"></div>

<script defer data-pjax src="/js/visit_calendar.js"></script>

可能遇到的问题

  • 控制台报错 Access to fetch at 'https://baidu-tongji-api.vercel.app/api?...' from origin 'http://...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

    解决方案:

    仍在努力解决跨域问题。