必须先读 robots.txt,否则后续所有请求都可能违规;urllib.robotparser需显式调用read()并捕获urlerror/httperror,set_url必须传完整url,can_fetch参数顺序为(user_agent, url)且仅支持前缀匹配,crawl_delay需手动sleep实现,应缓存robots.txt但不缓存can_fetch结果。

必须先读 robots.txt,否则后续所有请求都可能违规——这不是建议,是合规底线。
urllib.robotparser 的正确初始化流程
很多人以为 RobotFileParser 创建后就能直接用,结果 can_fetch() 总返回 False 或抛出异常。根本原因是:它不自动发起网络请求,read() 必须显式调用,且依赖底层 urlopen —— 这意味着 DNS 失败、超时、404 都会导致解析失败,而错误信息极不明确。
- 务必在
rp.read()前用try/except包裹,捕获URLError和HTTPError,不能只靠if rp.read()判断(它根本不返回布尔值) -
rp.set_url()传入的必须是完整 URL,比如"https://example.com/robots.txt",不是"https://example.com";少写/robots.txt就会静默失败 - 如果目标站强制 HTTPS 但你用了 HTTP,
read()可能重定向失败,建议统一用https://协议构造地址
can_fetch() 的参数陷阱与匹配逻辑
can_fetch() 看似简单,但两个参数的顺序和含义常被搞反:can_fetch(user_agent, url),不是 (url, user_agent)。更关键的是,它内部按 User-agent 段逐条匹配,优先使用**最精确匹配的规则段**,而非第一个匹配段。
- 例如
User-agent: MyBot和User-agent: *同时存在时,can_fetch("MyBot", ...)只看前者定义的Disallow/Allow,不会 fallback 到*段 -
url参数必须是完整 URL(含协议和域名),不能只传路径如"/admin/";can_fetch()会从中提取路径部分做匹配,但传错格式会导致路径提取失败 - 通配符
*和结尾符$是 robots.txt 标准语法,但urllib.robotparser**不支持**;它只做前缀匹配,Disallow: /api/会拒绝/api/v1,但不会拒绝/apixxx
Crawl-delay 的手动实现与实际影响
urllib.robotparser 能解析 Crawl-delay,但**完全不提供任何延时控制能力**——它只把值存进 rp.crawl_delay 属性,你需要自己读取并 sleep。很多爬虫忽略这点,导致高频请求触发封禁。
-
rp.crawl_delay返回的是浮点秒数(如2.5),不是字符串;若该字段不存在,值为None,需做空值判断 - 注意:这个 delay 是针对**单个 User-agent** 的全局限制,不是每 URL 独立计时;你得在每次请求前检查并等待,而不是只在首次解析后 sleep 一次
- 真实场景中,多个域名共用同一爬虫实例时,必须为每个域名维护独立的
RobotFileParser实例,否则crawl_delay和规则会相互覆盖
缓存 robots.txt 与处理动态变更
反复请求 /robots.txt 不仅慢,还可能被限流。但盲目缓存又有风险:管理员随时可能更新规则,尤其是临时封禁某 UA 时。
- 标准做法是:首次解析后缓存内容(文本或
RobotFileParser实例),设置 TTL 为 24 小时;超过后强制刷新,避免长期失效 - 不要缓存
can_fetch()结果——路径匹配是实时计算的,缓存它毫无意义 - 遇到
403 Forbidden或401 Unauthorized返回的 robots.txt(少见但存在),rp.read()仍会成功,但规则可能为空或误导,需额外检查rp.default_entry是否为None
真正难的不是写几行 can_fetch(),而是把规则检查嵌入整个请求生命周期:DNS 解析前查缓存、请求前校验权限、响应后更新延迟计时器、失败后回退重试策略——漏掉任意一环,合规就只剩名义上的完整。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











