遵守 robots.txt 是爬虫最基本的礼貌。但“遵守”的细节很多:Allow 与 Disallow 谁优先、通配符怎么算、UA 名字怎么匹配、robots.txt 本身打不开怎么办。这篇把标准库和 Scrapy 用的 protego 放在一起,用 26 个边界用例逐条对照,再看 HTTP 状态对 read() 的影响。
用例 标准库 protego 是否一致
最长匹配:Allow 更长压过 Disallow True True
最长匹配:Disallow 写在 Allow 之后也一样 True True
长度相同的 Allow 与 Disallow True True
长度相同,Allow 写在前面 True True
前缀匹配 False False
路径区分大小写 True True
通配符 * 在中间 False False
结尾 $ 精确匹配(.pdf 结尾) False False
结尾 $:路径后面还有查询串 True True
空的 Disallow = 全部允许 True True
Disallow: / 禁止整站 False False
Disallow: / 之外 Allow: /ok True True
查询串匹配 /search?q= False False
百分号编码:规则写 ~,请求写 %7E False False
百分号编码:规则写 %7E,请求写 ~ False False
UA 精确组 vs * 组 True True
UA 名字带版本号 MyBot/1.0 False False
组名是我的 UA 名字的子串("bot" 对 "MyBot") False True ← 不一致
组名比我的 UA 名字长("MyBotPro" 对 "MyBot") True True
UA 名字大小写 False False
同一个 UA 出现在两个组里(是否合并) False False
多个 UA 共用一组 False False
规则之间的空行与注释 False False
字段名大小写与空格 False False
没有 User-agent 行的规则 True True
robots.txt 本身 True True
共 26 个用例,两者判断不同的有 1 个:['组名是我的 UA 名字的子串("bot" 对 "MyBot")']两个解析器都按最长匹配决定 Allow 与 Disallow 谁优先(长度相同取 Allow),都支持 * 和结尾 $。路径区分大小写、前缀匹配、/robots.txt 自身总是允许。这是 Python 3.14.8 的标准库;更早版本的实现我没有测,不要把这里的结论套到旧版本上。
标准库:crawl_delay("MyBot") = 7,crawl_delay("Other") = 3,request_rate("Other") = RequestRate(requests=2, seconds=10),site_maps() = ['http://example.test/sitemap1.xml', 'http://example.test/sitemap2.xml']
protego:crawl_delay("MyBot") = 7.0,crawl_delay("Other") = 3.0,request_rate("Other") = RequestRate(requests=2, seconds=10, start_time=None, end_time=None),sitemaps = ['http://example.test/sitemap1.xml', 'http://example.test/sitemap2.xml']
Crawl-delay: 1.5 这种小数:标准库 = None,protego = None组名 bot 对我的 UA MyBot:标准库认为适用(子串匹配),protego 不适用——这是 26 个用例里唯一的分歧。Crawl-delay 两者都能读取整数,Crawl-delay: 1.5 这样的小数两者都得到 None,所以不能靠它们表达小于 1 秒的间隔。
robots.txt 返回 200:/x 允许=False,/y 允许=True;disallow_all=False,allow_all=False robots.txt 返回 404:/x 允许=True,/y 允许=True;disallow_all=False,allow_all=True robots.txt 返回 410:/x 允许=True,/y 允许=True;disallow_all=False,allow_all=True robots.txt 返回 401:/x 允许=False,/y 允许=False;disallow_all=True,allow_all=False robots.txt 返回 403:/x 允许=False,/y 允许=False;disallow_all=True,allow_all=False robots.txt 返回 429:/x 允许=True,/y 允许=True;disallow_all=False,allow_all=True robots.txt 返回 500:/x 允许=False,/y 允许=False;disallow_all=True,allow_all=False robots.txt 返回 503:/x 允许=False,/y 允许=False;disallow_all=True,allow_all=False robots.txt 返回 301:/x 允许=False,/y 允许=True;disallow_all=False,allow_all=False 从没读过 robots.txt(没调用 read/parse):can_fetch = False
标准库 read():404、410、429 等 4xx 视为没有限制;401、403、500、503 视为整站禁止;重定向会被跟随。如果你的爬虫遇到 429(被限速)时把整站当“全部允许”,要意识到这是标准库的选择,不一定是你想要的。