解决不蒜子文章阅读计数错误的问题

本博客应用Hexo框架和NexT主题构建,并一直使用不蒜子 (busuanzi)提供的访客和阅读次数统计功能。但是,最近几个月发现统计数据没有实时更新,而且不同浏览器显示的计数也不一样。经过一番查询和调研,终于找到解决办法。

问题描述

NexT主题已集成了不蒜子的访客人数和文章阅读统计功能,下面是本博的主题配置文件中的相关部分

_config.yml
1
2
3
4
5
6
7
8
busuanzi_count:
enable: true
total_visitors: true
total_visitors_icon: fa fa-user
total_views: true
total_views_icon: fa fa-eye
post_views: true
post_views_icon: far fa-eye

这些设置分别对应:

  • 首页底部显示的总访客人数和总阅读次数
  • 点击“阅读全文”时显示的当前文章阅读次数

然而,从一开始我就发现单个文章的阅读计数在 macOS 的 Safari 浏览器中显示异常 —— 刚发的文章的阅读量就是一个很大的数字。在谷歌的 Chrome 和火狐浏览器中则显示正常阅读统计数值。因为太忙,而且只有 Safari 存在问题,就没有去花时间深究。

可是,在过去的几个月里,发现所有的浏览器都无法显示文章阅读计数值。打开文章后,在标题下面根本就没有“阅读次数”这一数据项。所以,现在不得不着手解决这一问题。

原因分析

网络搜索后发现原来 Safari 浏览器的阅读量显示错误是许多人都遇到的问题。Safari 浏览器默认都是阻止跨站点跟踪(cross-site tracking)的。要修复,只要在 Safari 设置的 “PRIVACY & SECURITY” 部分,关掉 “Prevent Cross-Site Tracking” 选项即可。

但是,还有一个更大的问题。不蒜子计数器的工作原理是在引入的 JavaScript 脚本中,把当前页面 URL 注册到其第三方服务器,服务器上保存对应的计数值,点击页面后通过更新服务器上的计数值,并在页面初始化时在本地标签加载、显示计数值。不蒜子统计博客文章访问量是通过 Referrer 来计算的。

什么是 Referrer?

简单理解,就是请求 Web 服务器时,可以在 HTTP Request 的请求头 (header) 中加上当前页面的 URL,例如我们在浏览某个博客页面,需要加载一些图片,从服务器请求这些图片时,Referrer 就是当前的博客页面 URL。从这里也可以看出,Referrer 可能会暴露请求来源的某些信息或者隐私,有一定的隐私或安全风险。

如果 Referrer Policy 是 strict-origin-when-cross-origin,不蒜子接收到的只有博客的域名,没有文章的具体路径,所以具体某个文章的 PV 统计会出现错误。所以,要想正确统计博客每篇文章的 PV,可以用 meta tag 把 Referrer Policy 设置为 no-referrer-when-downgrade。

即便如此,如果浏览器不尊重 meta tag,如上面提到的 Safari 默认阻止跨站点跟踪,就没法实现正确计数。基于 Referrer 的计数方法有太多限制,既不安全也不可靠,现在已经被认定为过时了。博客网站需要更有效的计数方案。

解决方案

参考一些讨论资料和其他博客的介绍,最后决定采用 Vercount 网站计数工具。Vercount 是一个基于 NextJS 和 Redis 的高效网站计数器,是一个不蒜子计数完美替代方案。它具有以下特点:

  • 极速响应:服务器响应时间在 10ms 以内。
  • 高可用性:支持中国加速版本或 Vercel 全球 CDN,确保 99.99% 的可用性。
  • 精准统计:使用 POST 请求,克服传统 Referrer 方法在移动端和某些浏览器上的不足。
  • 安全防护:采用 JSON 回调方式,杜绝 CSRF 攻击风险,了解更多请查看:JSONP
  • 自动数据同步:无需手动操作,site_pvsite_uvpage_pv 数据会自动同步。
  • 无缝兼容:支持不蒜子的 span 标签,轻松切换。
  • 持久数据存储:使用 Redis 定期备份,确保数据不丢失。
  • Serverless 架构:通过 Vercel Serverless Functions 提供后端支持,保证 99.99% 的可用性。
  • 自托管:支持自托管,可以部署到任何支持 NextJS 的平台。

由于 Vercount 还没有集成到 Next 主题中,要在 Hexo 中实现和不蒜子一样的效果,得动手改些代码。修改分三步:

  1. 编辑不蒜子计数器的 njk(Nunjucks, 一个基于 JavaScript 的模版编辑语言)文件,用Vercount URL 替换不蒜子的 URL :

    themes/next/layout/_third-party/statistics/busuanzi-counter.njk
    1
    2
    3
    4
    5
    6
    @@ -1,3 +1,4 @@
    {%- if theme.busuanzi_count.enable %}
    - <script{{ pjax }} async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    + <script defer src="https://vercount.one/js"></script>
    + {#<script{{ pjax }} async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>#}
    {%- endif %}

  2. 启用 NexT 配置中的 style: source/_data/styles.styl

    themes/next/_config.yml
    1
    2
    3
    4
    5
    6
    @@ -28,7 +28,7 @@ custom_file_path:
    #bodyEnd: source/_data/body-end.njk
    variable: source/_data/variables.styl
    #mixin: source/_data/mixins.styl
    - #style: source/_data/styles.styl
    + style: source/_data/styles.styl

  3. 在新的文件 styles.styl 文件中添加如下内容来覆盖默认行为:

    source/_data/styles.styl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @@ -0,0 +1,9 @@
    +#busuanzi_container_page_pv {
    + display: inline;
    +}
    +.busuanzi-count #busuanzi_container_site_uv {
    + display: inline;
    +}
    +.busuanzi-count #busuanzi_container_site_pv {
    + display: inline;
    +}

修改完成后,重新生成博客即可。结果如下

可以看到,统计数据都恢复正常了。三种浏览器 Safari、Chrome 和火狐的显示结果也完全一致。Vercount 的替代方案工作得很好! 当然,缺憾是每次使用 npm 重新安装或升级后需要手动修改上面的相关源码。希望未来 Vercount 能直接集成到 Next 主题中,就可以省却这一麻烦了。