分享一个我给 Github README.md 做的访客统计功能

jwenjian

这个是我很久之前准备开始用 Github issue 做博客时做的,从 hexo 迁移过去之后 发现用 github issue 写博客没有访客统计功能(虽然本来也没人看)。

但是由于不管是 github issues,还是 github 的 README,都只支持 markdown 语法,不能使用各种第三方统计了。

就自己做了一个访客统计的徽章( badge )服务。

原理很简单,徽章是一个 svg,你只需要在你的 issues 或者 readme 中添加一个 markdown 的图片:

![]( https://visitor-badge.glitch.me/badge?page_id=<your_page_id>)

当有人打开你的 issue 或者 github 仓库时,浏览器会加载这个图片,服务器就会发出一个请求。

之后根据请求里的 page_id 来计数,并将最新的数量生成到一个 svg 图片中,将这个 svg 返回,浏览器就可以显示出来了。

注意这里的参数:page_id, 需要自己手动给一个唯一的字符串,没有特定规则,只要能唯一标识当前页面即可。

最开始想用 http request header 里面的 referrer 字段,但是后来发现 github 对图片进行了代理,服务器收到的图片请求中没有了原先的 referrer 字段。

另外实现过程中还有一点,就是缓存,github 的图片代理服务器会对你的图片进行缓存,所以我在做第一版的时候发现这个 svg 图片更新有问题,就开始研究 github 用的这个 camo 服务器,后来在返回 svg 的时候在 response 的 header 中做了点手脚:

  1. 添加一个 no-cache 的 header: 'Cache-Control': 'no-cache,max-age=0'
  2. 将 Expires header 的值设置为了当前时间减去 10 分钟

这样设置了之后,就可以绕过 camo 的缓存策略,每次都可以顺利的 +1, 实际效果:

刷新一下这个页面试试?


Github:visitor-badge

85 条回复  •  2020-06-18 20:06:05 +08:00
ob
ob2 天前 via Android
这个不错,过后试下。
1
uuspider
uuspider2 天前 via Android
真是创意无限啊
2
chotow
chotow2 天前
你这个统计怎么和 V2EX 的点击统计不一样 🌸🐔
3
jwenjian
jwenjian2 天前
@chotow v2 的更有意义吧,这个说白了就是统计 图片加载次数
4
jwenjian
jwenjian2 天前
@ob 掘金好像不行,他是直接转存的图片,发布之后就编程一个静态图片了 不会变
5
jwenjian
jwenjian2 天前
@uuspider 也是收到其他 github 徽章的启发
6
chizuo
chizuo2 天前
不错不错,挺好的
7
zhw2590582
zhw25905822 天前
不错,但我只担心服务器稳不稳定,是不是一直维护
8
jwenjian
jwenjian2 天前
@zhw2590582glitch.me 上,只要一直有访问,没啥问题。可能偶尔出不来 :)

不过代码开源的 欢迎部署到更稳定的服务器上 提供稳定的服务
9
ChanKc
ChanKc2 天前 via Android
GitHub 好像就有访客功能吧,当然不如自己做的全就是
10
Ritter
Ritter2 天前
666
11
jwenjian
jwenjian2 天前
@ChanKc 这个之前还真没听说过,有链接么
12
securityCoding
securityCoding2 天前
创意无限啊
13
ChanKc
ChanKc2 天前 via Android
@jwenjian traffic 还是什么的,能看到每周克隆数折线图那个页面,手机上没找到
14
watzds
watzds2 天前 via Android
以前 bbs 图片签名,能显示 ip,天气之类,是不是也是这个原理
15
jwenjian
jwenjian2 天前
@watzds 应该是一个原理
16
jwenjian
jwenjian2 天前
@ChanKc 嗯嗯 我好像也见到过,不过我这个纯粹是为了统计打开次数。
17
NotFoundEgg
NotFoundEgg2 天前
让我想到了珊瑚虫 qq 印象中也是用发送 1 像素图片获取对方 ip 的
18
jwenjian
jwenjian2 天前
@NotFoundEgg 这个还真不清楚是不是这个原理 不过珊瑚虫。。。。哈哈好久之前的东西了 之前好多人都是安装魔改的 qq,各种功能。
19
metrue
metrue2 天前
👍,小功能已经用在了 https://github.com/metrue/fx 上面.
20
raaaaaar
raaaaaar2 天前 via Android
挺不错,能看见自己打开了多少次 T_T
21
jwenjian
jwenjian2 天前 via iPhone
感谢哪位大哥给的置顶…… 我都没注意
22
jwenjian
jwenjian2 天前 via iPhone
@raaaaaar 同惨……
23
Pyrex23
Pyrex232 天前 via iPhone
呃呃 是不是不行了?
This project has received too many request, please try again later
24
metrue
metrue2 天前
已经挂了?....
25
WittBulter
WittBulter2 天前
和我的 `views.show` 有点像: https://docs.views.show/
我一开始也支持 GitHub,后来我发现如果不对 Referrer 做识别,就无法避免暴力刷 views 的问题,虽然服务是在 Serverless 上无所谓压力,但是数据会很不准确。后面当我想实现只读、访客识别等功能的时候,就只好抛弃了 GitHub ...
没有找到特别好绕过 camo 的办法,所以我就转型只做个人博客或者网站的 views 统计了...
26
jwenjian
jwenjian2 天前
@metrue glitch 免费版 每分钟有最大次数限制好像... 你可以部署在 giki 的服务器上一份, :)
27
jwenjian
jwenjian2 天前
@WittBulter camo 绕不过去,本来用 referrer 是最合适的,不过因为有 camo 就只能这样了,不能说完美,只能说能用吧。
28
wysnylc
wysnylc2 天前
这就是 N 年前论坛里签名图片显示 IP 位置 系统信息的东西,上面大吃一惊的是装的吗
29
slmaaw
slmaaw2 天前 via Android
这应该也是 Google 的埋点原理吧 请求一个 1 像素的图片
30
jwenjian
31
leetao94
leetao942 天前
有没有统计下载次数的功能呢?
33
xcodebuild
xcodebuild2 天前
有个类似的服务 https://hits.dwyl.com/
34
jwenjian
jwenjian2 天前
@iamkun 嗯 你可以重点看下我提到的绕过 缓存服务器 camo 的部分,应该是没问题的。每一次生成的图片 github 都会缓存的,你再刷新一下,看看这两个 github 缓存的图片地址,应该是不一样的。
35
jwenjian
jwenjian2 天前
@xcodebuild 看起来比我薅 glitch 的羊毛靠谱 :)
36
iamkun
iamkun2 天前
@jwenjian 哦刚才没看太仔细 这思路不错
37
jwenjian
jwenjian2 天前
@leetao94 这个就用各种统计平台的埋点功能就可以了
38
jwenjian
jwenjian2 天前
@xcodebuild 刚看了一下 hits 应该是用的同一种策略绕过 camo 的:

cache-control: no-cache, no-store, must-revalidate
cf-cache-status: BYPASS
cf-ray: 5a4161bc6ebbe80d-LAX
cf-request-id: 035cbb69be0000e80dc7069200000001
content-encoding: br
content-type: image/svg+xml
date: Tue, 16 Jun 2020 03:18:08 GMT
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
expires: 0
pragma: no-cache
server: cloudflare
status: 200
vary: Ac

或者说这应该是一个标准的 http 缓存策略的设置。
39
wuhaoworld
wuhaoworld2 天前
这个方式已经不行了吧? 我看 Github 会把外链的图片抓取并保存到 githubusercontent.com 的域名下
40
ioioioioioioi
ioioioioioioi2 天前
这个机器人怕也算统计吧
41
jwenjian
jwenjian2 天前
@wuhaoworld 可以再仔细点看看帖子
42
jwenjian
jwenjian2 天前
@ioioioioioioi 哈 算 不过如果真的有人刷这个数量 也没办法 其实没啥意义,刷个 10w 8w 的,又不能换钱
43
coldmonkeybit
coldmonkeybit2 天前
涨知识,终于知道为什么我的邮箱会默认屏蔽图片了
44
cyrbuzz
cyrbuzz2 天前
利用 img 图片进行的错误上报...前天刚看了实践就来了。
45
huiyadanli
huiyadanli2 天前
想到了 http://hits.dwyl.io/
感觉没啥区别
46
jwenjian
jwenjian2 天前
@huiyadanli 这个之前有人提到了,功能上看应该是一样的功能。
47
jwenjian
jwenjian2 天前
对了 这个项目的 [首页]( https://visitor-badge.glitch.me) 的 favicon 也是一个 svg 哦,里面放了一个 emoji 表情 👀,你可以用这个方式来把你喜欢的 emoji 表情作为网页的 favicon,具体可以看 [这里]( https://www.yuque.com/jwenjian/blog/mpnmmb)
48
metrue
metrue2 天前
提一个需求,可以加一个 'since <date>' 的功能。
49
zhwithsweet
zhwithsweet2 天前
http://blog.zouhaha.site/ 已经放在自己的 blog 上
50
wellsc
wellsc2 天前
Insights 的 traffic 挺好用的,能精确到某个 issue 的 unique visitors,还有图表
51
meisen
meisen2 天前
楼主很强大,能否帮忙看下 https://www.v2ex.com/t/682055 是否可以解决?
52
jwenjian
jwenjian2 天前 via iPhone
@wellsc 不瞒你说 我从来没点开过 insight 的那个 tab 页 😂
53
Tink
Tink2 天前
很 6
54
yanshenxian
yanshenxian2 天前
这个挺好的
56
jwenjian
jwenjian2 天前 via iPhone
@yanshenxian 嗯嗯 主要是分享一下这个思路
57
0312birdzhang
0312birdzhang2 天前
不错,已 star 👍
58
liuyibao
liuyibao2 天前
59
no1xsyzy
no1xsyzy2 天前
补充:Gmail 对图片进行缓存,不会暴露真实 IP 。
60
hercat
hercat2 天前
想法不错 👍
61
jwenjian
jwenjian2 天前
@no1xsyzy 👍
62
jwenjian
jwenjian2 天前
@cyrbuzz 也是一种跨域新思路
63
windardyang
windardyang1 天前
cool, like it.
64
windardyang
windardyang1 天前
stared
65
sedgwickz
sedgwickz1 天前
建议给 ip 加个 cache,否则容易被刷,刚试了下,一会刷了好几千。

测试地址: https://visitor-badge.glitch.me/badge?page_id=visitor-badge.v2ex.sharing1

测试代码:

···
import requests
import time
import threading

def sendReq():
try:
r = requests.get('https://visitor-badge.glitch.me/badge?page_id=visitor-badge.v2ex.sharing1')
except:
pass

def run():
try:
while 1:
threading.Thread(target=sendReq).start()
time.sleep(0.01)
except:
pass

run()
···
66
jinliming2
jinliming21 天前 via iPhone
很多邮箱默认不加载图片之类的第三方资源的,要手动允许加载,而手动允许加载也有很多邮箱是由邮箱提供者的服务器代为请求然后处理一下,返回给你的不是原始文件,防止图片里有恶意代码……
67
JCZ2MkKb5S8ZX9pq
JCZ2MkKb5S8ZX9pq1 天前
思路不错,不晓得那些带统计的短链接网站能不能做到类似的事情。
就不展示,仅统计。
68
aleung
aleung1 天前 via Android
20 年前的网站就是这样做的,而且计数器就是当年网站里唯一的动态内容。失传的技艺重新发掘出来了,哈哈😄
69
jwenjian
jwenjian1 天前 via iPhone
@sedgwickz 无所谓了,刷这个不顶吃不顶喝的……
70
jwenjian
jwenjian1 天前 via iPhone
@jinliming2 嗯……之前也有人提到 Gmail 缓存图片 这更进一步保护邮箱安全了
71
jwenjian
jwenjian1 天前 via iPhone
@JCZ2MkKb5S8ZX9pq 那个不需要这么做 因为你只要访问了短链接,它在给你跳转之前就可以把这些工作做了,统计的出发点是你会直接访问短链接
72
jwenjian
jwenjian1 天前 via iPhone
@aleung 嗯……老技术 新场景……
73
jwenjian
jwenjian1 天前 via iPhone
@coldmonkeybit 还有人提到你的邮件服务器会替你代理发送图片请求,隐藏你的真实 IP
74
OldPanda
OldPanda1 天前
创意不错,不知道有没有考虑过用 aws lambda + api gateway 来搭后台,每个月前一百万次请求免费,几乎等于不要钱
75
jwenjian
jwenjian1 天前 via iPhone
@OldPanda 谢谢你。aws 这一套我捣鼓过 后来好像要验证我的信用卡 我没有……
76
myqoo
myqoo1 天前
这就和当年网站计数器一样,写个脚本可以刷它爆表~
77
dreamage
dreamage1 天前
思路清奇
78
VShawn
VShawn1 天前
@jwenjian #76 我刚才在 Github 上试了一下以前用的统计工具,也是用图片进行统计,楼主可以参考一下。

https://github.com/VShawn/Shawn_pose_estimation_by_opencv/blob/master/README.md
79
jwenjian
jwenjian1 天前 via iPhone
@VShawn 嗯嗯 这个我之前也见过 原理是一样的 只是它多做了很多工作 比如拿到你的 IP 之后 把 IP 转成地理位置 再画在一个地图上 最终生成一个图片,我做的这个比较简单 只是统计次数。
80
jwenjian
jwenjian1 天前
@myqoo ...
81
yazoox
yazoox7 小时 43 分钟前
很有意思的功能
82
lzj307077687
lzj3070776871 小时 37 分钟前
![]( http://badge.roblog.top/?key=v2ex_reply)
哈哈 我也写了个
83
jwenjian
jwenjian14 分钟前
84
jwenjian
jwenjian12 分钟前
@yazoox 嘿嘿 虽然原理简单 技术也不难,不过有了新的应用场景还是挺好玩儿的
85