这个是我很久之前准备开始用 Github issue 做博客时做的,从 hexo 迁移过去之后 发现用 github issue 写博客没有访客统计功能(虽然本来也没人看)。
但是由于不管是 github issues,还是 github 的 README,都只支持 markdown 语法,不能使用各种第三方统计了。
就自己做了一个访客统计的徽章( badge )服务。
原理很简单,徽章是一个 svg,你只需要在你的 issues 或者 readme 中添加一个 markdown 的图片:

当有人打开你的 issue 或者 github 仓库时,浏览器会加载这个图片,服务器就会发出一个请求。
之后根据请求里的 page_id 来计数,并将最新的数量生成到一个 svg 图片中,将这个 svg 返回,浏览器就可以显示出来了。
注意这里的参数:page_id, 需要自己手动给一个唯一的字符串,没有特定规则,只要能唯一标识当前页面即可。
最开始想用 http request header 里面的 referrer 字段,但是后来发现 github 对图片进行了代理,服务器收到的图片请求中没有了原先的 referrer 字段。
另外实现过程中还有一点,就是缓存,github 的图片代理服务器会对你的图片进行缓存,所以我在做第一版的时候发现这个 svg 图片更新有问题,就开始研究 github 用的这个 camo 服务器,后来在返回 svg 的时候在 response 的 header 中做了点手脚:
这样设置了之后,就可以绕过 camo 的缓存策略,每次都可以顺利的 +1, 实际效果:
刷新一下这个页面试试?
Github:visitor-badge
再班门弄斧一下吧,这个原理被很多恶意的网站用来收集隐私信息,比如说有的网站给你发的邮件里,不管是营销邮件还是垃圾邮件或者其他的东西,在邮件正文中插入一个 1像素 的图片,图片的url地址都是精心构造好的,当你打开邮件时,发送者就可以知道:
谁,在什么时间,用什么设备,什么ip,什么网络打开了这个邮件。
有了ip就知道你是在什么地方(大致)打开的这个邮件。
从而可以对这个营销邮件的效果进行分析,比如发送了 10000 个邮件,有多少人打开之类的。
再往后挖,甚至还可以挖出你的兴趣,比如给你发不同的主题邮件,18+ 的,创业的,培训班的等等,一样发一个,看看你都打开了哪些,给你打标签...
所以现在一般邮件客户端都默认不加载图片,一是节省打开邮件的时间,加快打开速度,二也是为了保护你的隐私。
你可以检查一下你的邮件客户端,把默认加载图片给关掉。
![]() | 1 ob 2020-06-16 08:02:54 +08:00 via Android 这个不错,过后试下。 |
![]() | 2 uuspider 2020-06-16 08:04:05 +08:00 via Android 真是创意无限啊 |
![]() | 3 chotow 2020-06-16 08:04:27 +08:00 你这个统计怎么和 V2EX 的点击统计不一样 |
![]() | 7 chizuo 2020-06-16 08:43:48 +08:00 不错不错,挺好的 |
![]() | 8 zhw2590582 2020-06-16 09:02:43 +08:00 不错,但我只担心服务器稳不稳定,是不是一直维护 |
![]() | 9 jwenjian OP |
10 ChanKc 2020-06-16 09:14:08 +08:00 via Android GitHub 好像就有访客功能吧,当然不如自己做的全就是 |
![]() | 11 Ritter 2020-06-16 09:15:17 +08:00 666 |
13 securityCoding 2020-06-16 09:18:43 +08:00 创意无限啊 |
![]() | 15 watzds 2020-06-16 09:26:48 +08:00 via Android 以前 bbs 图片签名,能显示 ip,天气之类,是不是也是这个原理 |
![]() | 18 NotFoundEgg 2020-06-16 09:47:01 +08:00 让我想到了珊瑚虫 qq 印象中也是用发送 1 像素图片获取对方 ip 的 |
![]() | 19 jwenjian OP @NotFoundEgg 这个还真不清楚是不是这个原理 不过珊瑚虫。。。。哈哈好久之前的东西了 之前好多人都是安装魔改的 qq,各种功能。 |
![]() | 20 metrue 2020-06-16 09:59:35 +08:00 ,小功能已经用在了 https://github.com/metrue/fx 上面. |
![]() | 21 raaaaaar 2020-06-16 10:00:10 +08:00 via Android ![]() 挺不错,能看见自己打开了多少次 T_T |
![]() | 22 jwenjian OP 感谢哪位大哥给的置顶…… 我都没注意 |
![]() | 24 Pyrex23 2020-06-16 10:23:16 +08:00 via iPhone 呃呃 是不是不行了? This project has received too many request, please try again later |
![]() | 25 metrue 2020-06-16 10:25:57 +08:00 已经挂了?.... |
![]() | 26 WittBulter 2020-06-16 10:29:37 +08:00 和我的 `views.show` 有点像: https://docs.views.show/ 我一开始也支持 GitHub,后来我发现如果不对 Referrer 做识别,就无法避免暴力刷 views 的问题,虽然服务是在 Serverless 上无所谓压力,但是数据会很不准确。后面当我想实现只读、访客识别等功能的时候,就只好抛弃了 GtHub ... 没有找到特别好绕过 camo 的办法,所以我就转型只做个人博客或者网站的 views 统计了... |
![]() | 27 jwenjian OP @metrue glitch 免费版 每分钟有最大次数限制好像... 你可以部署在 giki 的服务器上一份, :) |
![]() | 28 jwenjian OP @WittBulter camo 绕不过去,本来用 referrer 是最合适的,不过因为有 camo 就只能这样了,不能说完美,只能说能用吧。 |
![]() | 29 wysnylc 2020-06-16 10:42:43 +08:00 这就是 N 年前论坛里签名图片显示 IP 位置 系统信息的东西,上面大吃一惊的是装的吗 |
![]() | 30 slmaaw 2020-06-16 10:51:25 +08:00 via Android 这应该也是 Google 的埋点原理吧 请求一个 1 像素的图片 |
![]() | 31 jwenjian OP @slmaaw google 统计是执行 js 代码,跟这个应该是不一样: https://developers.google.cn/analytics/devguides/collection/analyticsjs/how-analyticsjs-works?hl=zh-cn |
![]() | 32 iamkun 2020-06-16 11:10:28 +08:00 看起来 Github 会缓存一份图片到自己的服务上 https://camo.githubusercontent.com/92b3578005c83d0d417978e02333800d2e4575b2/68747470733a2f2f6170692e70726f6475637468756e742e636f6d2f776964676574732f656d6265642d696d6167652f76312f66656174757265642e7376673f706f73745f69643d313935313436267468656d653d6461726b 这会不会造成数据不准确?请求其实并没有到你的服务器 |
![]() | 33 leetao94 2020-06-16 11:12:11 +08:00 有没有统计下载次数的功能呢? |
![]() | 34 xcodebuild 2020-06-16 11:12:28 +08:00 有个类似的服务 https://hits.dwyl.com/ |
![]() | 35 jwenjian OP @iamkun 嗯 你可以重点看下我提到的绕过 缓存服务器 camo 的部分,应该是没问题的。每一次生成的图片 github 都会缓存的,你再刷新一下,看看这两个 github 缓存的图片地址,应该是不一样的。 |
![]() | 36 jwenjian OP @xcodebuild 看起来比我薅 glitch 的羊毛靠谱 :) |
![]() | 39 jwenjian OP @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 缓存策略的设置。 |
![]() | 40 wuhaoworld 2020-06-16 11:28:02 +08:00 这个方式已经不行了吧? 我看 Github 会把外链的图片抓取并保存到 githubusercontent.com 的域名下 |
![]() | 41 ioioioioioioi 2020-06-16 11:32:32 +08:00 这个机器人怕也算统计吧 |
![]() | 42 jwenjian OP @wuhaoworld 可以再仔细点看看帖子 |
![]() | 43 jwenjian OP @ioioioioioioi 哈 算 不过如果真的有人刷这个数量 也没办法 其实没啥意义,刷个 10w 8w 的,又不能换钱 |
![]() | 44 coldmonkeybit 2020-06-16 12:13:30 +08:00 涨知识,终于知道为什么我的邮箱会默认屏蔽图片了 |
![]() | 45 cyrbuzz 2020-06-16 12:53:35 +08:00 利用 img 图片进行的错误上报...前天刚看了实践就来了。 |
![]() | 46 huiyadanli 2020-06-16 13:03:26 +08:00 想到了 http://hits.dwyl.io/ 感觉没啥区别 |
![]() | 47 jwenjian OP @huiyadanli 这个之前有人提到了,功能上看应该是一样的功能。 |
![]() | 48 jwenjian OP 对了 这个项目的 [首页]( https://visitor-badge.glitch.me) 的 favicon 也是一个 svg 哦,里面放了一个 emoji 表情 ,你可以用这个方式来把你喜欢的 emoji 表情作为网页的 favicon,具体可以看 [这里]( https://www.yuque.com/jwenjian/blog/mpnmmb) |
![]() | 49 metrue 2020-06-16 14:17:18 +08:00 提一个需求,可以加一个 'since <date>' 的功能。 |
![]() | 50 zhwithsweet 2020-06-16 14:51:57 +08:00 http://blog.zouhaha.site/ 已经放在自己的 blog 上 ![]() |
![]() | 51 wellsc 2020-06-16 14:59:33 +08:00 Insights 的 traffic 挺好用的,能精确到某个 issue 的 unique visitors,还有图表 |
![]() | 54 Tink PRO 很 6 |
![]() | 55 Tink PRO |
![]() | 56 yanshenxian 2020-06-16 16:06:53 +08:00 这个挺好的 |
![]() | 57 jwenjian OP @yanshenxian 嗯嗯 主要是分享一下这个思路 |
![]() | 58 1daydayde 2020-06-16 17:05:58 +08:00 不错,已 star |
59 liuyibao 2020-06-16 17:36:00 +08:00 赞 |
![]() | 60 no1xsyzy 2020-06-16 18:19:07 +08:00 补充:Gmail 对图片进行缓存,不会暴露真实 IP 。 |
61 hercat 2020-06-16 18:27:42 +08:00 想法不错 |
64 windardyang 2020-06-16 21:34:28 +08:00 cool, like it. |
65 windardyang 2020-06-16 21:34:38 +08:00 stared |
66 sedgwickz 2020-06-16 22:00:05 +08:00 建议给 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() |
67 jinliming2 2020-06-16 23:53:58 +08:00 via iPhone 很多邮箱默认不加载图片之类的第三方资源的,要手动允许加载,而手动允许加载也有很多邮箱是由邮箱提供者的服务器代为请求然后处理一下,返回给你的不是原始文件,防止图片里有恶意代码…… |
![]() | 68 JCZ2MkKb5S8ZX9pq 2020-06-17 00:24:06 +08:00 思路不错,不晓得那些带统计的短链接网站能不能做到类似的事情。 就不展示,仅统计。 |
![]() | 69 aleung 2020-06-17 01:00:26 +08:00 via Android ![]() 20 年前的网站就是这样做的,而且计数器就是当年网站里唯一的动态内容。失传的技艺重新发掘出来了,哈哈 |
![]() | 71 jwenjian OP @jinliming2 嗯……之前也有人提到 Gmail 缓存图片 这更进一步保护邮箱安全了 |
![]() | 72 jwenjian OP @JCZ2MkKb5S8ZX9pq 那个不需要这么做 因为你只要访问了短链接,它在给你跳转之前就可以把这些工作做了,统计的出发点是你会直接访问短链接 |
![]() | 74 jwenjian OP @coldmonkeybit 还有人提到你的邮件服务器会替你代理发送图片请求,隐藏你的真实 IP |
75 OldPanda 2020-06-17 05:37:52 +08:00 创意不错,不知道有没有考虑过用 aws lambda + api gateway 来搭后台,每个月前一百万次请求免费,几乎等于不要钱 |
![]() | 77 myqoo 2020-06-17 09:08:35 +08:00 这就和当年网站计数器一样,写个脚本可以刷它爆表~ |
![]() | 78 dreamage 2020-06-17 09:20:25 +08:00 思路清奇 |
![]() | 79 VShawn 2020-06-17 10:57:51 +08:00 @jwenjian #76 我刚才在 Github 上试了一下以前用的统计工具,也是用图片进行统计,楼主可以参考一下。 https://github.com/VShawn/Shawn_pose_estimation_by_opencv/blob/master/README.md |
![]() | 80 jwenjian OP @VShawn 嗯嗯 这个我之前也见过 原理是一样的 只是它多做了很多工作 比如拿到你的 IP 之后 把 IP 转成地理位置 再画在一个地图上 最终生成一个图片,我做的这个比较简单 只是统计次数。 |
![]() | 82 yazoox 2020-06-18 12:35:36 +08:00 很有意思的功能 |
![]() | 83 lzj307077687 2020-06-18 18:41:16 +08:00  哈哈 我也写了个 |
![]() | 84 jwenjian OP |
![]() | 86 lzj307077687 2020-06-19 09:24:31 +08:00 @jwenjian #84 前几天看了一眼这贴,然后昨天在学 py 的时候突然想起来感觉可以写一下! 虽然功能比较简单,不过也学到了东西,svg 文件,重温了下 http header 什么的... 刚回来看发现原来有 github,而且也是拿 py 写的,巧了~ |
![]() | 87 jwenjian OP @lzj307077687 哈哈,github 的链接字太小,确实不容易发现。 |
![]() | 88 jwenjian OP 最近这两天被发现的一个彩蛋 GitHub 创建一个用户名同名的仓库 这个仓库的 README 就可以显示在你的 GitHub profile 页面,你可以在这个 README 里添加这个 badge 就可以知道你的 GitHub 页面被打开多少次啦~ 示例参见我的 github |