bigywater.tech
爬虫工程 · 41

爬虫陷阱:无限日历、会话 ID、参数排列组合

爬虫碰到“无限日历”“每次不同的会话 ID”“筛选条件的排列组合”这些页面,会把预算耗光在永远抓不完的 URL 上。这篇造一个有这些陷阱的本机站点,真实抓取,量一量各种常见防御到底有多管用、又会伤到什么。

1738 / 2000
无防御时,抓取预算里浪费的请求数(找到了 262 / 263 个真实页)
103 / 263
“每个 URL 模板最多 50 个”时找到的真实页——200 篇文章共用一个模板被误伤
10094
无防御、预算 6000 时队列里仍然待抓的 URL 数——永远抓不完

四类陷阱和几种防御

实测 · 本机执行输出
  BFS,最多抓 2000 个页面,各防御措施下找到多少个真实内容页、预算花在了哪类页面上
  防御措施                                  抓到的真实页     覆盖率  浪费的请求(重复内容或无内容:日历/会话/筛选/循环)             
  无防御                               262 / 263    100%  日历 370、会话 1074、筛选 288、循环 6、其它 0(共抓 2000)
  只限制深度 ≤ 6                         259 / 263     98%  日历 264、会话 348、筛选 288、循环 5、其它 0(共抓 1164)
  URL 长度 ≤ 80                       262 / 263    100%  日历 370、会话 1074、筛选 288、循环 6、其它 0(共抓 2000)
  检测路径段重复 ≥ 3 次                     262 / 263    100%  日历 375、会话 1074、筛选 288、循环 1、其它 0(共抓 2000)
  规范化时去掉 sid 参数                     263 / 263    100%  日历 1424、会话 0、筛选 288、循环 25、其它 0(共抓 2000)
  每个 URL 模板最多 50 个                  103 / 263     39%  日历 101、会话 40、筛选 50、循环 1706、其它 0(共抓 2000)
  去 sid + 路径重复 + 模板上限 50            113 / 263     43%  日历 101、会话 0、筛选 50、循环 1、其它 0(共抓 265)
  去 sid + 路径重复 + 模板上限 300           263 / 263    100%  日历 601、会话 0、筛选 288、循环 1、其它 0(共抓 1153)

陷阱:日历(上下月、每月 28 个日期页,无限)、会话页(同样 20 个页面但链接里 sid 每次不同)、筛选列表(288 种参数组合)、相对链接循环。无防御时 BFS 靠“真实页在前”的运气找到几乎全部真实页,但 2000 个请求里 1738 个是浪费。只限深度 ≤ 6 能让队列空掉(1164 个请求,98% 覆盖);去掉 sid 参数让会话浪费归零,却让预算转移到日历上(1424);URL 长度和路径重复检测对这个站点作用很小。

URL 模板上限的取舍

实测 · 本机执行输出
  无防御:抓 6000 个页面,找到真实页 263 / 263;队列里还剩 10094 个待抓
  去 sid + 路径重复 + 模板上限 50:抓 265 个页面,找到真实页 113 / 263;队列里还剩 0 个待抓
  去 sid + 路径重复 + 模板上限 300:抓 1153 个页面,找到真实页 263 / 263;队列里还剩 0 个待抓

按模板限制数量是最通用的办法,但上限定得太低会误伤正常的大站点:上限 50 时,200 篇文章只抓了 50 篇,真实页覆盖只剩 39%。上限 300 配合去 sid 与路径重复检测,263 / 263 全部找到,抓了 1153 个后队列清空,日历仍浪费了 601 个。更好的办法需要内容层面的信号(重复内容、页面是否有新的出链),本文没有实现。

分步演示

爬虫陷阱
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。站点由一个本机服务器生成:文章 200 篇(分 10 页列表)、会话页 20 个(链接带随机 sid)、日历(无限月份)、筛选页(颜色 6 × 尺寸 6 × 排序 4 × 方向 2)、30 个商品、/loop/ 相对链接循环;响应头 X-Real-Id 只用于统计真实页,爬虫不看它;爬虫是 BFS(requests + lxml 提取链接 + urljoin),同站点链接,最多抓指定预算个页面;“浪费”=日历页、筛选排列页、或已经见过的真实内容又抓一遍;模板=路径数字换 #、查询参数值换 *
    • 没有验证:真实站点的陷阱形态(无限分页、重定向环、动态生成的 URL 等);基于内容哈希或链接新颖度的陷阱检测;robots.txt 里 Disallow 日历的效果;遵守 Crawl-delay 对陷阱代价的影响;DFS 或优先级队列在这个站点上的表现(上一批文章讲了抓取顺序);站点规模更大时模板上限的合理取值
    京ICP备2026067502号-1京公网安备11010502063487号