实测 robots.txt 的解析:Python 3.14.8 标准库 urllib.robotparser 与 protego 0.7.0 在 26 个边界用例中只有 1 个不一致(组名是 UA 名字的子串时是否适用);两者都是最长匹配并支持 * 与 $;Crawl-delay 的小数两者都读不出;robots.txt 本身返回 404/410/429 时全部允许,401/403/500/503 时全部禁止。
用服务器记录的到达时间量爬虫的限速:每个 worker 各自 sleep(0.1) 时 1、5、20 个 worker 分别是 6.4、33.8、143.7 个/秒;全局固定间隔稳定 10.0 个/秒;令牌桶 burst=20 时任意 0.1 秒内最多 21 个;空闲后桶重新装满再次突发;从完成算起的间隔让速率降到 1.7–6.4 个/秒。
用三个速度不同的本机站点实测爬虫调度:全局先进先出队列里,慢站点排前面会让快站点晚到 3.1 秒才完成(排后面是 0.1 秒);每个站点独立队列加 3 个 worker 后快站点恒为 0.4 秒,每站点并发峰值 3,但总时间从 4.2 秒变成 10.0 秒。
在一个自己设计的合成站点(6374 页,6000 个商品页)上比较抓取顺序:预算 100 时 BFS 抓到 15 个商品页,DFS 33 个,按 URL 模式排优先级的 93 个;预算接近全站时三者趋同;待抓队列峰值 BFS 270、DFS 72;标签云陷阱让 BFS 和 DFS 各浪费 61 页。
200 个客户端同时遇到 1 秒故障的模拟:立刻重试向服务器砸了 2400 个请求且全部放弃;没有抖动的指数退避让 200 个客户端每轮同时重来,31.6 秒才结束还有 26 个放弃;加完全抖动 5.3 秒、去相关抖动 2.9 秒全部成功。
实测爬虫对 429 和 Retry-After 的处理:立刻重试向限流服务器发了 110 个请求仍失败 15 个,遵守 Retry-After 只发 23 个、3.0 秒全部成功;urllib3 支持整数和 HTTP-date 两种写法,默认上限 21600 秒;429/503 只要带 Retry-After,即使不在 status_forcelist 里也会被重试。
1000 个 URL 的模拟(瞬时故障、永久 404/403、永远 500 的 bug 页):不重试成功 400;任何失败都重试成功 750 但 1950 个请求中 500 个浪费在永久错误上;只重试网络错误成功 550;网络错误 + 429/502/503/504 白名单成功 750、浪费 0。
实测 POST 重试:urllib3 的 Retry 默认只重试 DELETE/GET/HEAD/OPTIONS/PUT/TRACE;对 POST 打开重试后,创建订单后丢回应的场景重复下了 2 个订单;带 Idempotency-Key 且服务器去重时只有 1 个;服务器先创建订单再回 503 同样重复。
对真实爬虫进程做 SIGKILL 随机杀死再续爬的实验:只放内存重复抓取平均 112 个;JSON 直接覆盖写 20 次里有 1 次状态文件损坏,原子改名 0 次;SQLite 每个 URL 提交一次重复最多 8 个(在途请求数)、几乎不增加耗时(0.55 对 0.54 秒),而整体重写 JSON 让耗时翻倍(1.12 秒)。
实测 asyncio 爬虫收到信号的行为:默认 SIGINT 取消 8 个在途请求(完成 0 个,19 ms 退出);用 add_signal_handler 优雅停止则让 8 个在途请求完成(313 ms),队列剩下的 192 个可保存;SIGTERM 在没有处理器时直接杀死进程;退出用时等于在途请求剩余时间。
实测 URL 规范化对去重的影响:20 组等价写法里 yarl 合并 8 组、w3lib 10 组、自写语法规范化 11 组、再加语义规范化 15 组;1000 个页面 8 种写法共 8000 个 URL,不规范化是 8000 个“不同的”,只做语法规范化 5000 个,加上排序参数、去追踪参数、合并双斜杠后恰好 1000 个。
用 Node 的 WHATWG URL 对照 Python urljoin:36 个相对链接用例有 11 个结果不同(反斜杠、前后空格、中文编码、大写主机、:80、https:foo 等);urljoin 不过滤 javascript:、file:// 等 scheme;lxml 的 make_links_absolute(resolve_base_href=False) 实测仍应用 <base>;重定向后必须用最终 URL 当 base。