有的站点会在页面里放人看不到、但程序会取到的链接。谁会去访问它们?这篇在本机用 17 种隐藏写法逐个测:不同的爬虫写法各自会跟进哪些、会漏掉哪些,以及服务器日志里这种请求是什么样子。这是描述性的测量,不涉及怎么绕过它。
写法 人眼能看到 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 秒);并且会漏掉“跳到正文”这类平时在屏幕外、键盘聚焦才出现的无障碍链接,这是正常链接。
页面里共 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、只跟进人会看到的链接、遇到被禁止的路径就别去碰。