FiniCounter: 静态网站访问量统计工具
静态博客如Hexo/Hugo/Jekyll近些年很流行,markdown写作,一键生成部署,无需后端,可托管在各种网站平台,非常方便。但正因为无后端,动态信息的存取就成为了痛点:文章阅读数统计,评论系统等等。本站采用的是Hexo+Waline的方式实现文章阅读数统计与评论系统,最近也去掉了LeanCloud的依赖,所有数据使用MongoDB存储。
突然发现缺少一个全站访问量统计的功能,Waline目前不支持。大多数静态网站使用不蒜子,但看到由于使用人数众多,常常出现502错误和服务不稳定的情况。遂考虑自行开发这样的一个服务:FiniCounter。使用Vercel Serverless Function作为Web框架,MongoDB为后端存储。用户访问任意页面时,通过Fetch API调用Serverless Function,在MongoDB中计数加1并返回最新计数,在前端展示。
本想开发个小工具自己用,后来发现天然支持多用户,独乐乐不如众乐乐,大家一起用吧 :-)。效果展示:FiniCounter。
统计API后端实现
后端非常简单,只需要接收一个GET请求,在MongoDB中计数加1即可。Node.js不熟,主要精力都花在了如何在Vercel中使用Python Web框架Sanic了,趟了不少坑。
API为Http
GET请求,格式如下:https://finicounter.eu.org/counter?host=xxx.com
,按hostname进行计数。
核心代码实现:
MONGODB_URL = os.environ.get("MONGODB_URL")
CORS_ORIGINS = os.environ.get("CORS_ORIGINS")
DB_NAME = os.environ.get("DB_NAME") if os.environ.get("DB_NAME") is not None else "MyCounter"
COLLECTION_NAME = "Counter"
cors_headers = {"Access-Control-Allow-Origin": CORS_ORIGINS, "Access-Control-Allow-Method": "POST, GET, OPTIONS"}
mongoClient = pymongo.MongoClient(MONGODB_URL)
db = mongoClient[DB_NAME]
collection= db[COLLECTION_NAME]
app = Sanic("finicounter")
@app.route("/counter")
async def counter(request):
args = request.get_args()
host = args["host"][0]
result = collection.find_one_and_update({"host": host}, {"$inc": {"views": 1}, "$set": { "updateTime": datetime.datetime.utcnow()}}, upsert = True, return_document = pymongo.collection.ReturnDocument.AFTER, maxTimeMS=50)
return response.json({"views": result['views']}, headers = cors_headers)
app.run(host="0.0.0.0", port=8000, fast=True)
自行部署
自行部署非常方便,仅需一个Vercel账户和一个MongoDB。
fork 代码库 https://github.com/finisky/finicounter
到你的GitHub账户,在Vercel中新建Project并导入该库,并设置如下三个环境变量即可:
Value | |
---|---|
CORS_ORIGINS | * (or your domain) |
DB_NAME | MongoDB DB name |
MONGODB_URL | MongoDB Connection String |
数据存储在 DB DB_NAME
中的 Counter
Collection。
前端实现
前端采用Fetch API实现,获取计数完成后会将网页中id为
finicount_views
元素对应的innerText
设置为计数,前端API的javascript实现:
async function getViews() {
let url = "https://finicounter.eu.org/counter?host=" + window.location.hostname;
try {
let res = await fetch(url);
return await res.json();
} catch (error) {
console.log(error);
}
}
async function renderViews() {
let res = await getViews();
let elem = document.getElementById("finicount_views");
if (typeof elem !== 'undefined' && elem !== null) {
elem.innerText = formatNumber(res.views);
}
}
function formatNumber(num) {
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
}
renderViews();
其中 formatNumber
函数会将较大的数字按逗号分隔显示,如
12,345,678
。使用的同学可以按需定制修改。
如何使用
基本用法
- 添加一行javascript到网页:
<script async src="//finicounter.eu.org/finicounter.js"></script>
- 添加任意容器来显示访客数,id 必须为
finicount_views
:
<span id="finicount_views"></span>
搞定!访客数已经可以在你的网站中显示了,就像这样:12,345,678
。
Hexo NexT集成
如果使用NexT主题,可采用 Injects 的方法进行集成,将网站访问数显示在页脚:
在Hexo根目录的 scripts/
中添加新文件
totalpageview.js
即可:
hexo.extend.filter.register('theme_inject', function(injects) {
injects.footer.raw('totalpageview', '<div><span><a href="https://finicounter.eu.org/" target="_blank">Total Pageview:</a></span><span id="finicount_views" style="display:inline;padding-left:5px;"></span><div> <script async src="//finicounter.eu.org/finicounter.js"></script>', {}, {cache: false});
});
如果受欢迎,可以开发一个Hexo NexT Plugin进一步简化集成操作 :-) 。
Demo
本站也正在使用此服务 :-) ,页脚有显示,本站的 Demo网站。
完整代码
完整代码见此 代码库 。