bigywater.tech
爬虫工程 · 70

蜜罐链接:页面里的隐藏链接会被谁触发

有的站点会在页面里放人看不到、但程序会取到的链接。谁会去访问它们?这篇在本机用 17 种隐藏写法逐个测:不同的爬虫写法各自会跟进哪些、会漏掉哪些,以及服务器日志里这种请求是什么样子。这是描述性的测量,不涉及怎么绕过它。

13 / 5 / 1
17 种写法里人眼看不到的 13 个链接,静态全取 / 静态加内联过滤 / Chrome 可见性判断 各会跟进几个
60 对 0
60 页的合成站点上,不看 robots.txt 的爬虫请求的 /trap/ 页面数 与 遵守 robots.txt(或只跟进可见链接)的爬虫
0.6 ms
陷阱请求与所在页面的请求之间的间隔(中位)

哪些写法会被哪种办法跟进

实测 · 本机执行输出
  写法                       人眼能看到    A   B   C  
  visible                  能        ✓   ✓   ✓  
  aria_hidden              能        ✓   ✓   ✓  
  display_none             不能       ✓   -   -  
  visibility_hidden        不能       ✓   -   -  
  opacity_0                不能       ✓   -   -  
  hidden_attr              不能       ✓   -   -  
  zero_size                不能       ✓   -   -  
  offscreen                不能       ✓   -   -  
  font_size_0              不能       ✓   -   -  
  text_same_color          不能       ✓   ✓   ✓  
  parent_hidden            不能       ✓   -   -  
  ext_css_class            不能       ✓   ✓   -  
  js_hidden                不能       ✓   ✓   -  
  js_created               能        -   -   ✓  
  noscript                 不能       ✓   ✓   -  
  html_comment             不能       -   -   -  
  skip_link(无障碍,屏幕外)       不能       ✓   ✓   -  

静态解析(lxml 取全部 a[href])会跟进所有人眼看不到的链接。再加对内联样式和 hidden 属性的正则过滤,仍会跟进 5 个:来自外部样式表的类、脚本加载后才隐藏的、noscript 里的、屏幕外的无障碍链接、文字与背景同色的——样式表里的规则和脚本的结果静态 HTML 里看不出来。

浏览器的判断

实测 · 本机执行输出
  A 静态全取:跟进了 13/13 个人眼看不到的链接 ['display_none', 'ext_css_class', 'font_size_0', 'hidden_attr', 'js_hidden', 'noscript', 'offscreen', 'opacity_0', 'parent_hidden', 'skip_link', 'text_same_color', 'visibility_hidden', 'zero_size'];漏掉 1/3 个人眼看得到的链接 ['js_created']
  B 静态 + 内联过滤:跟进了 5/13 个人眼看不到的链接 ['ext_css_class', 'js_hidden', 'noscript', 'skip_link', 'text_same_color'];漏掉 1/3 个人眼看得到的链接 ['js_created']
  C Chrome 可见性:跟进了 1/13 个人眼看不到的链接 ['text_same_color'];漏掉 0/3 个人眼看得到的链接 []
  静态 HTML 里一共有 15 个 a[href](不含脚本创建的 js_created,含 noscript 里的链接);Chrome 里一共 15 个(含 js_created:True,含 noscript 里的:False)

用真实的 Chrome 加载后,用 checkVisibility 加位置尺寸判断,只剩文字与背景同色的会被跟进。代价是更慢(60 页用 3.8 秒,静态解析不到 0.1 秒);并且会漏掉“跳到正文”这类平时在屏幕外、键盘聚焦才出现的无障碍链接,这是正常链接。

robots.txt

实测 · 本机执行输出
  页面里共 9 个链接,其中 /trap/ 开头的 1 个;robotparser.can_fetch 允许 8 个、禁止 1 个(被禁止的就是 /trap/ 那一个)

站点在 robots.txt 里 Disallow 的路径,遵守它的爬虫根本不会请求。实验里陷阱路径写在 Disallow 里,can_fetch 把它挡在外面,不需要判断可见性。

合成站点的结果

实测 · 本机执行输出
  A 取全部 a[href],不看 robots.txt:请求了 60 个真页面(站点共 60 个),60 个 /trap/ 页面;用时 0.1 秒
  B 取全部 a[href],遵守 robots.txt:请求了 60 个真页面(站点共 60 个),0 个 /trap/ 页面;用时 0.0 秒
  C 用 Chrome,只跟进可见的链接:请求了 60 个真页面(站点共 60 个),0 个 /trap/ 页面;用时 3.8 秒

60 页的站点上,不看 robots.txt 的静态爬虫请求了 60 个 /trap/ 页面;遵守 robots.txt 的,和只跟进可见链接的 Chrome 爬虫,都是 0 个,而且都爬全了 60 个真页面。

站长看到什么

实测 · 本机执行输出
  取到页面之后 0.6 ms(中位,共 20 次)就请求了陷阱链接;陷阱请求的 Referer:{'(没有)': 20};UA:{'python-requests/2.34.2': 20}

陷阱请求在取到页面后约 0.6 ms 就来了,不带 Referer,UA 是库的默认 UA。这样的请求很容易和人区分。这里只是描述,不是教怎么避开;避免的做法是遵守 robots.txt、只跟进人会看到的链接、遇到被禁止的路径就别去碰。

分步演示

隐藏链接
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / requests 2.34.2 / aiohttp 3.14.3 / lxml 6.1.1(系统 Python);所有请求只发给本机自己起的测试服务器(127.0.0.1,端口 9960 以上,不访问任何真实网站);服务器可以按实验需要制造延迟、断连、压缩、重定向等行为,并记录收到的原始请求;计时用 time.perf_counter,数字每次运行会有小幅抖动。页面和站点都是本机服务器上的合成页面(17 种链接写法、60 页的站点、一个 Disallow: /trap/ 的 robots.txt);静态解析用 lxml,浏览器用 Chrome 154 通过 CDP(pcdp.py);各情形跑一次;“人眼能不能看到”是我按写法设的标签,没有真人看过;第一节的内联样式过滤是我写的正则,只是一种粗糙的静态启发式
    • 没有验证:真实站点上的蜜罐是怎么写的(这里的页面是我构造的);按鼠标行为、点击速度判断的检测;CSS 里用 clip-path、transform、z-index 遮盖等没有测的隐藏写法;文字与背景同色这种写法怎么被检测到(三种办法都没有);用真人浏览器点击与程序访问的比较;站点收到陷阱请求之后会做什么(封禁、限流、返回假数据),本机服务器没有实现这些
    京ICP备2026067502号-1京公网安备11010502063487号