访问计数器:会执行 JavaScript 的机器人
2026年11月4日上午,VosPosts 的访问计数器显示出漂亮的数字:一天有3352名“真人”访客,凌晨1点前又增加了2272名,而网站平时每天只有约400次访问。没有文章被大量转发,也没有链接突然走红。实际上,这些只是机器人,我们的计数器却把它们当成了读者。
下面讲述我们如何发现问题,为什么“能执行 JavaScript 就是真人”的传统判断已不可靠,以及如何在自己的服务器日志中找出这些虚假访客。
陷阱:“执行了 JavaScript,所以是真人”
许多自建计数器,包括我们的,都依赖一个简单思路:服务器记录请求的页面,然后浏览器中的小脚本发送第二次请求,以“确认”这次访问。过去的机器人只下载 HTML,不执行代码,因此第二次请求不会出现,也就不会计入统计。
如今这个过滤办法漏洞百出。大量机器人像真正的浏览器一样执行 JavaScript:
- 搜索引擎会渲染网页以理解内容:Googlebot 使用最新的 Chromium,还能看到 Baiduspider-render、Applebot、Bytespider(字节跳动)和 meta-webindexer。
- AdSense 广告机器人(Mediapartners-Google)会访问页面并分析内容。
- 现代抓取程序操控真正的无头浏览器,例如无头 Chrome、Puppeteer、Playwright,以及专为自动化和 AI 智能体设计的轻量引擎 Lightpanda。
对只检查脚本执行情况的计数器来说,这些访问与真人阅读毫无区别。更令人意外的是,问题并非从攻击当天才开始:我们检查完全正常的11月3日,发现621次被记为真人的访问中,有446次实际上来自机器人,超过七成。
无头浏览器抓取程序的迹象
11月4日的流量激增具有组织化抓取的全部特征。单看任何一条线索都不能下结论,但结合起来几乎没有疑问。
每个页面使用不同的 IP 地址
每个页面请求都来自不同的 IP,分布在世界各地。这是住宅代理的典型特征:请求经由普通家庭的网络连接转发,用户往往毫不知情。因此封禁 IP 没什么用:地址成千上万,而且属于真实家庭。
使用多年前的旧版浏览器
用户代理反复使用一小组版本:Chrome 118至120、Firefox 120和121、Edge 119和120。这些版本发布于2023年底。但 Chrome 和 Firefox 大约每四周自动更新一次,我们的真实读者使用的是 Chrome 148至156。偶尔有人使用三年前的浏览器很正常,一夜之间出现几千人则说明抓取工具使用了固定的用户代理列表。
这些伪装中甚至直接出现了毫不掩饰的 Lightpanda/1.0。
过于整齐的行为模式
- 全部来自台式电脑,没有手机,而真实流量大多来自手机;
- 没有来源页面:没有访客来自 Google、社交平台或其他网站;
- 通过站内搜索发起2340次不同查询,试图尽可能抓取更多页面;
- 当天共发出24772次请求,占服务器全部流量的42%。
隐藏的风险:广告
无头浏览器会执行一切操作,包括加载广告位。当天机器人加载我们的广告模块达2939次。对 AdSense 发布者来说,这不是小事:机器人产生的展示属于无效流量,Google 会从收入中扣除,反复发生还可能危及账户。因此虚高的访问量不只是面子问题。
如何在日志中识别机器人
采用“combined”格式的 Apache 或 Nginx 访问日志包含所需的全部信息。几个命令就能完成初步筛查。先查看最常见的用户代理:
awk -F'"' '{print $6}' access.log | sort | uniq -c | sort -rn | head -30然后检查 Chrome 版本分布。正常网站的访问主要集中在最近四五个版本,旧版本突然大量出现会非常显眼:
grep -oE 'Chrome/[0-9]+' access.log | sort | uniq -c | sort -t/ -k2 -n按小时统计请求量,可以准确确定流量高峰的起止时间:
awk '{print substr($4, 2, 14)}' access.log | sort | uniq -c最后,针对可疑特征,比较请求总数与不同 IP 的数量。如果两者几乎相等,就可能找到了代理网络:
grep -c 'Chrome/119.0.0.0' access.log
grep 'Chrome/119.0.0.0' access.log | awk '{print $1}' | sort -u | wc -l一个有用的细节:自2022年起,Chrome 只发送主版本号,后面接 .0.0.0。因此 Chrome/119.0.0.0 的格式本身正常;暴露机器人的其实是119这个旧版本号。
我们采取的措施
不封禁,只停止计数
第一层:由服务器决定某次访问是否应计入真人统计。若用户代理为空、明确标识为机器人(bot、spider、crawler、curl 或 Python 等 HTTP 工具、无头浏览器),或声称使用超过两年半的 Chrome 或 Firefox,就不再按真人计数。版本阈值根据日期自动计算,无须手动更新:
$jours = (time() - mktime(0, 0, 0, 12, 5, 2023)) / 86400; // Chrome 120, décembre 2023
$version_actuelle = 120 + (int)floor($jours / 28); // une version toutes les 4 semaines
if (preg_match('/Chrome/(d+)./', $ua, $m) && (int)$m[1] < $version_actuelle - 32) {
return true; // navigateur trop ancien pour être un lecteur réel
}一项重要保护措施是:已登录账户的用户始终计数,不受浏览器版本影响。过滤器针对陌生访客,而不是忘记更新旧电脑的忠实会员。
拒绝抓取程序,放行搜索引擎
第二层更主动:服务器现在对抓取程序的特征返回403错误,同时明确豁免合法爬虫。在 Apache 中只需几行配置:
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (Lightpanda|HeadlessChrome) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} Chrome/1(18|19|20).0.0.0 [OR]
RewriteCond %{HTTP_USER_AGENT} Firefox/12[01].
RewriteCond %{HTTP_USER_AGENT} !(bot|spider|crawl|google|bing|facebook) [NC]
RewriteRule ^ - [F]前三个条件由 [OR] 连接,最后一个条件与前面构成隐含的“与”关系:匹配任一可疑特征就阻止访问,但声称属于搜索引擎或社交网络的用户代理除外。部署后我们分别验证:抓取程序确实收到403,而新版 Chrome、iPhone、Googlebot、Bingbot 和 AdSense 机器人仍能正常访问。
误封 Googlebot 的代价可能远高于虚高的访问量。上线前务必使用搜索引擎的用户代理逐条测试规则。
需要了解的局限性
用户代理只需一行代码就能伪造。等这个抓取程序声称自己是 Chrome 155 时,这些规则便无法识别。它不是永久防护,而是基本维护:清除占多数的低水平机器人,让数据重新具有参考价值。若要进一步加强检测,需要结合多种信号(没有来源页面、每个 IP 只访问一页、没有鼠标移动或滚动),或使用专业的机器人检测服务。
真正的教训在于:如果计数器每年会在一次惊人的流量高峰中撒谎,那么其他日子很可能也在悄悄撒谎。如果你从未查看过访问日志,不妨挑一个普通日子,花十分钟运行上面的命令。结果可能让你大吃一惊。
Chinese
French
German
English
Spanish
Hindi
Italian
Japanese
Korean
Norwegian


