Python robotparser 的抓取延迟:已经读到三秒限制,为什么 can_fetch 仍连续返回真
给一个已获授权的数据采集程序接入robots解析后,Crawl-delay明明返回三秒,连续调用can_fetch却每次都为True。原因是这个方法判断路径政策,时间间隔由另一些方法提供为元数据;解析器不是请求调度器,不会替调用方记录最近发送时间或自动等待。
本文在Linux、CPython 3.12.14实跑。保存为demo.py,运行python3 demo.py。只调用parse读取写在程序里的教学规则,不调用read,不访问任何网站,也没有真实等待。后面的时间值为虚拟数字,专门用于解释调用方应承担的间隔计算。
AI生成的概念示意图:通行闸门与计时器分开放置,表示路径政策检查与请求节奏控制是独立职责;不是任何网站的访问许可。
完整程序与本次实跑
完整可运行程序
from urllib.robotparser import RobotFileParser
rules = """User-agent: TutorialBot
Disallow: /private/
Crawl-delay: 3
Request-rate: 2/10
User-agent: *
Disallow: /admin/
"""
rp = RobotFileParser()
rp.parse(rules.splitlines())
agent = "TutorialBot"
assert rp.can_fetch(agent, "/public/item")
assert not rp.can_fetch(agent, "/private/item")
print("public allowed:", rp.can_fetch(agent, "/public/item"))
print("private allowed:", rp.can_fetch(agent, "/private/item"))
print("repeated policy checks:", [rp.can_fetch(agent, "/public/item") for _ in range(3)])
print("crawl delay:", rp.crawl_delay(agent))
rate = rp.request_rate(agent)
assert (rate.requests, rate.seconds) == (2, 10)
print("request rate:", rate.requests, "requests /", rate.seconds, "seconds")
print("other agent delay:", rp.crawl_delay("OtherBot"))
invalid = RobotFileParser()
invalid.parse(["User-agent: *", "Crawl-delay: 0.5"])
print("fractional delay:", invalid.crawl_delay("OtherBot"))
delay = rp.crawl_delay(agent)
last_start = 20.0
now = 21.0
wait_for_delay = max(0.0, last_start + delay - now)
assert wait_for_delay == 2.0
print("virtual delay wait:", wait_for_delay)
print("policy still allows:", rp.can_fetch(agent, "/public/item"))本次实际输出(以下为结果,不是程序)
public allowed: True private allowed: False repeated policy checks: [True, True, True] crawl delay: 3 request rate: 2 requests / 10 seconds other agent delay: None fractional delay: None virtual delay wait: 2.0 policy still allows: True
允许路径的判断不会消耗额度
public allowed为True,private allowed为False,对应TutorialBot这一组中的禁止路径。紧接着连续三次检查公开路径,仍得到三个True。调用can_fetch不会把一次判断记成一次请求,也不会扣除某个内部计数器,因此不能把返回True理解为“现在可以立即发出下一次请求”。
同样,False表示当前解析规则不允许该路径,不是网络故障或服务端正在限流。把政策检查结果、网络响应和时间调度状态分开记录,排查时才知道应该修正路径、调整节奏还是处理服务失败,而不是对所有失败统一重试。
延迟与频率提供两种不同的信息
crawl delay返回整数三,request rate则拆出十秒内两次请求的两个字段。前者可以参与相邻开始时间的间隔计算,后者还需要窗口或等价的频率状态。只读取两项数值,不代表应用已经执行了它们;真正的请求入口必须接上相应限制。
示例同时写出两条指令是为了看到它们分别被解析,不给出适用于所有网站的合并公式。仅取十除以二当作固定五秒间隔,也未必等价于对方的窗口约定。真实实现应结合目标协议、并发数和服务端反馈选择保守策略,并保留清晰的节奏规则。
None不能随意理解为无限制
other agent delay为None,因为适用于OtherBot的通配组没有提供抓取延迟。另一个解析器读入小数0.5,也返回None;当前实现只把符合其整数形式要求的延迟识别为有效值。缺失与未识别的指令都可能呈现None,调用方不能据此断言远端没有任何访问限制。
配置检查时可以保留原始规则用于诊断,同时按自身已获授权范围采用明确的默认节奏。不要把None替换成零之后就开始无间隔循环。robots解析结果只是政策信号的一部分,服务条款、账户权限和实际响应仍需要分别遵守。
虚拟计算只演示延迟这一项
假设上次开始在二十秒、现在是二十一秒,三秒间隔意味着最早开始在二十三秒,所以virtual delay wait为二。随后can_fetch依然为True,这正好证明它没有替调用方等待。计算使用max把已经到期的等待截为零,而不会产生负睡眠时间。
这段算式没有实施Request-rate窗口,因此不是可以直接连接HTTP客户端的完整调度器。实际多任务同时访问时,还需要共享目标站点的节奏状态,原子地安排下一次开始时间;每个任务各自算出一个等待值,可能仍在同一瞬间一起发请求。
解析、调度和访问授权分别成立
can_fetch返回True只说明当前解析到的规则没有禁止该路径,不授予登录权限,也不能越过服务端的访问控制。本文的固定路径没有指向实际服务,适合本地验证程序行为。接入真实目标前,应先确定任务本身已经获得所需访问许可。
生产系统还需决定何时刷新robots、解析或读取失败时如何处理,以及收到限流响应后怎样调整节奏。这些状态不是反复调用can_fetch就能自动完成的。先让离线样本证明规则选择与缺失值处理正确,再把经过审查的调度层接上去,能避免把一个解析器误用成完整抓取管理器。
参考资料
官方资料核验于2026年10月2日;上述输出来自本次固定输入实跑,退出码为0。


