
写爬虫这几年几乎每天都要跟robots.txt打交道。这玩意儿在圈内被叫作“爬虫君子协议”听着很文雅可真到了项目里我发现很多人对它的理解要么停留在“见过但没管过”要么干脆直接忽略。今天我就把这个文件从里到外拆开讲讲包括它的设计初衷、语法规则、Python 实操时怎么处理、以及我在真实项目里踩过的那些坑。不管你是刚开始学爬虫还是已经在写分布式采集这篇文章都能给你一些可以落地的参考。robots.txt本质上是一个放在网站根目录下的纯文本文件比如https://example.com/robots.txt。它告诉“自觉遵守规则的爬虫”哪些路径可以抓哪些路径不能碰。注意这里的关键词是“自觉”——因为它既不是防火墙也没有强制执行力一个不守规矩的爬虫完全可以视而不见。但正因为它非强制才更考验一个爬虫工程师的专业素养。1. 先搞清楚robots.txt 到底是什么1.1 一次被“封”的经历让我开始重视它我最早写爬虫的时候完全不在乎robots.txt。当时的想法很简单“只要我能请求到数据说明服务器允许我访问。” 后来做某个新闻网站的采集刚跑了不到半小时IP 就被封了。我一脸懵地去找原因折腾半天才发现那家网站的robots.txt里清清楚楚写着Disallow: /search而我正好在疯狂请求搜索接口。那次之后我才意识到robots.txt不仅仅是“给别人看的礼仪文件”它实际上是一份非常明确的信号网站运营者不希望哪些资源被机器访问。无视它轻则触发反爬机制重则被列入黑名单。而且后来我发现很多反爬策略的第一个判断条件就是“这个爬虫是否遵守了 robots.txt”。从本质上讲robots.txt是网站主与爬虫程序之间的一次“事前沟通”。它用最简单的文本格式划定了一块“允许进入”和“禁止进入”的边界。爬虫程序在发起请求之前先读取这个文件再根据规则来决定自己下一步的行为。这个过程完全靠爬虫方自觉执行没有任何技术门槛去强制拦截。1.2 “君子协议”这四个字的分量为什么叫“君子协议”因为robots.txt的约束力不靠技术靠的是默契。它不像登录鉴权那样有密码保护也不像验证码那样物理阻断。一个不遵守协议的爬虫技术上依然可以轻松抓取Disallow后面的路径协议对此毫无办法。但恰恰是这种“软约束”构成了整个互联网正常运转的信任基础。搜索引擎、数据分析公司、研究机构甚至很多企业内部的数据采集任务都默认遵循这个协议。大家可以设想一个没有robots.txt的互联网每个爬虫都凭自己喜好抓取服务器资源被随意消耗网站主只能靠 IP 封锁来对抗最终的结果一定是两败俱伤。所以我个人的看法是做爬虫技术的人可以把robots.txt当成一种行业默契来理解。它不强制你遵守但遵守它能让你少走很多弯路、少碰很多红线甚至在跟网站方沟通时更有底气。毕竟如果某天你的爬虫出了问题对方第一个查看的日志行为大概率就是“是否访问了 robots.txt”。2. 语法拆解看懂 robots.txt10 分钟够了2.1 核心指令User-agent、Disallow、Allowrobots.txt的语法非常朴素核心就三个指令User-agent、Disallow、Allow。它的作用对象是“爬虫程序的 UA 标识”。比如百度的爬虫叫BaiduspiderGoogle 的叫Googlebot我们常见的 Python requests 爬虫UA 默认是python-requests/x.x.x。一段最典型的配置长这样User-agent: * Disallow: /admin/ Disallow: /tmp/ Allow: /public/这里第一行User-agent: *表示“下面的规则适用于所有爬虫”。Disallow后面跟的是不允许抓取的路径Allow则表示允许抓取。组合起来的意思是除了/public/之外所有爬虫都不能访问/admin/和/tmp/下的内容。我在实际项目中总结了一套理解逻辑robots.txt的核心机制是“前缀匹配”。它不做正则表达式那种复杂匹配只看 URL 路径是否以某一段字符串开头。比如Disallow: /api那么/api/user、/api/data/list都会被禁止但/apiv2不受影响。这一点非常关键新手经常在这里搞混。还有几个特殊情况需要记清楚Disallow:冒号后面为空表示允许抓取所有内容相当于没有任何限制。Disallow: /表示禁止抓取全站这是最严格的情况。Allow: /配合某个具体的Disallow可以精确到“某个路径下除了某几项都能抓”。上个月我接手一个历史数据回填的活目标站点的robots.txt写得很乱Disallow和Allow混在一起路径有的带斜杠有的不带。最后我直接把所有规则拉出来逐条做前缀匹配测试才搞清楚了它真正的意图。所以千万别小看这个文件的解析真到了上线采集的时候一小段正则写错就能让你请求大量无关的 URL既浪费资源又容易触发风控。2.2 容易被忽略的补充指令Sitemap、Crawl-delay除了上面三个核心指令robots.txt里还经常出现两个补充指令Sitemap和Crawl-delay。它们虽然不是 RFC 标准里的必选项但实际应用非常广泛。Sitemap指令用于声明网站地图的地址方便搜索引擎更快发现新的页面。它的格式很简单Sitemap: https://example.com/sitemap.xml我自己的习惯是在写爬虫时先看一眼robots.txt里有没有Sitemap字段。如果有我往往会优先从 sitemap 入手。原因也很简单sitemap 是网站主动整理出来的链接清单通常结构清晰、分类明确比我在页面上自己解析链接要高效得多。有一次做活动数据采集目标站点有上万个详情页分散在几十个栏目里我从 sitemap 里一次性拿到了全部 URL省了大半天的解析时间。Crawl-delay则用来告诉爬虫“两次请求之间至少间隔多少秒”。最常见的格式是User-agent: * Crawl-delay: 5这个字段在 Google 的官方说明里已经被明确忽略但百度和很多中小站点仍然在使用。我的经验是只要robots.txt里出现了Crawl-delay不管对方有没有强制执行我都会在爬虫里加上对应的请求间隔。它不仅是礼貌问题也是降低对方服务器压力的有效手段。毕竟很多网站的崩溃事故都是从几个没做限速的爬虫开始的。2.3 通配符与匹配规则关于robots.txt的语法还有一个容易踩坑的点通配符。严格来说1994 年最初的 robots.txt 规范并没有定义*和$的用法只是后来 Google、Bing 扩展支持了它们2022 年发布的 RFC 9309 才把这些规则正式明确下来。目前实践中常用的通配符有两个*表示匹配任意字符序列相当于正则里的.*。$表示匹配 URL 末尾相当于正则里的$。举个例子User-agent: * Disallow: /*.php$这段规则表示禁止抓取所有以.php结尾的 URL。注意/是必需的因为路径匹配始终是从根路径开始的。我在实际代码里验证过Python 的urllib.robotparser对这两个通配符的支持是完整的所以直接用标准库也能处理绝大多数情况。但这里有个体验很差的细节urllib.robotparser只支持“首段匹配”它对*的解析逻辑跟常规正则略有一点点差异在极少数多重通配的规则上会解析不准确。如果遇到特别复杂的规则集我建议别纠结标准库直接用re模块自己写一个匹配器几十行代码就能搞定反而可控性更强。3. 实战用 Python 正确读取和解析 robots.txt3.1 请求前的必要准备先说一个最常见的误区很多人用requests把robots.txt抓下来然后用split粗暴地按行切分判断里面有没有某个路径。这种方式在简单场景下能跑但一旦规则稍微复杂一点就会出现误判。我现在的做法是分三步走先用requests请求robots.txt但不会直接用默认 UA。很多网站的robots.txt对默认的python-requestsUA 不太友好有的会返回 403有的会自动重定向到首页。我的做法是设置一个明确的爬虫标识比如my-crawler/1.0 (contactexample.com)这个标识既能表明身份又能让网站方在出问题时联系到你。检查响应状态码。只有200才需要继续解析404表示这个站没有设置 robots.txt默认全部允许。403就需要注意了虽然robots.txt本身不太可能被禁止访问但一旦出现这种状态通常意味着对方有更严格的反爬策略。对文本内容做编码容错。robots.txt规范上推荐 UTF-8 编码但实际遇到不少站点是 GBK 或者混合编码的甚至有的人直接在文件里写了中文注释。这种情况下如果用response.text直接解码很容易出现乱码但robots.txt的指令都是 ASCII 字符所以乱码影响不大。真正要注意的是文件末尾的换行符有些站点用\r\n有些只用\n解析时需要统一处理。这里我还想强调一点读取robots.txt这件小事也应该有超时和重试机制。因为它虽然简单但毕竟是一次网络请求。我的习惯是超时设置 5 秒重试 2 次。如果重试也失败就直接视为“无 robots.txt 限制”并记录日志而不是让整个爬虫卡在这一步。3.2 用 urllib.robotparser 做合规检查Python 标准库里的urllib.robotparser就是我上面提到的神器。它可以帮我们解析robots.txt并判断某个 UA 是否有权限抓取某个 URL。基本用法很简单from urllib.robotparser import RobotFileParser from urllib.parse import urlparse rp RobotFileParser() rp.set_url(https://example.com/robots.txt) rp.read() base_url https://example.com my_ua my-crawler/1.0 target_path /article/123.html if rp.can_fetch(my_ua, base_url target_path): print(允许抓取) else: print(禁止抓取)这段代码的核心就是can_fetch方法它会根据robots.txt的规则和传入的 UA 来返回布尔值。我建议在正式跑采集之前把所有待采集的 URL 先批量用can_fetch过滤一遍把不允许的路径直接跳过。不过urllib.robotparser也有一个隐藏坑它的read()方法默认使用urllib.request去请求robots.txt这个请求是不带自定义 header 的某些站点会因此返回异常内容。解决办法是先自己用requests获取文本再通过rp.parse()手动传入import requests from urllib.robotparser import RobotFileParser resp requests.get(https://example.com/robots.txt, timeout5) rp RobotFileParser() rp.parse(resp.text.splitlines())这样既保留了requests的优秀体验又能用urllib.robotparser做后续判断是我目前最推荐的组合。3.3 把 robots 解析整合进爬虫调度还有一个很实际的场景爬虫项目不是跑一次就完事的而是要长期、反复运行。在这种情况下每次请求都去读取robots.txt效率太低。更合理的做法是把解析结果缓存起来定时刷新。我在自己的项目里是这样做的启动时读取robots.txt解析后把结果放到内存里。设置一个定时任务每 6 小时重新拉取一次robots.txt更新缓存。每次请求前从缓存里判断当前 URL 是否允许抓取。这里有一个经验值6 小时的刷新频率是我测试过比较稳妥的。太频繁会给服务器造成不必要的压力太久又可能错过网站规则的变更。实际项目中我们真的遇到过凌晨网站更新了 robots.txt结果爬虫用它旧的缓存继续跑了一整天白白请求了上百个被禁止的 URL。另外如果是多线程或者分布式爬虫robots.txt的判断必须在分配任务之前做而不是在真正发请求时才临时判断。这样做的好处是不需要把每个 URL 都推送到各个爬虫节点之后再做无用功。我见过有人把 robots.txt 的检查写在了发送请求的前一行虽然逻辑没错但等于每个 URL 都要过一遍解析器高并发下性能损耗非常明显。4. 真实项目中的坑这些情况你八成遇到过4.1 有的站点根本没有 robots.txt不是每个网站都有robots.txt。我做过一个统计大概有三分之一的中小站点根本没有这个文件。根据规范当robots.txt返回 404 时默认所有内容都是允许抓取的。但这里有一个潜规则没有robots.txt不代表没有反爬。很多网站的防护重点在 UA 检测、请求频率控制、验证码这些环节robots.txt对他们来说反而没那么重要。所以我如果发现目标站点没有robots.txt不会庆幸“可以随便抓”反而会多留个心眼主动把请求频率调低一些。还有一种情况是robots.txt文件存在但内容是空的。这从规范角度讲等价于全站允许抓取但我在实际项目中更倾向于把它理解为“网站方没有精力维护这个文件”并不代表他们欢迎爬虫。遇到这种情况我一般会再参考sitemap.xml是否存在以及网站的版权声明、服务条款里有没有关于数据采集的说明综合判断之后再做决定。4.2 robots.txt 被加密、动态生成或者格式乱到怀疑人生你可能会觉得robots.txt就是个纯文本文件能有多复杂但真实环境永远比想象中精彩。我遇到过某个政府数据公开网站的robots.txt内容是经过 gzip 压缩的二进制流直接访问能看到乱码。还有某些大型电商平台的robots.txt表面上写得规规矩矩实际上里面的路径是动态生成的不同地区、不同 IP 段访问之后返回的规则完全不同。这明显是运维方故意做的“区分对待”给搜索引擎放行给不知名爬虫设置重重限制。最离谱的一次我解析一个站点的robots.txt发现它里面竟然嵌套了 HTML 标签而且有多行User-agent是空值。后来一查才知道那个站点的 robots.txt 是从某个 CMS 后台自动生成的模板写挂了导致输出的文件完全不符合规范。遇到这种情况如果还在用标准解析器大概率会直接报错或者解析出错误结果。我的应对策略是不只看文本内容还要看响应头里的Content-Type和Content-Encoding。如果是 gzip 压缩的先用gzip.decompress解压再解析。解析失败时不要直接放弃而是尝试用正则把User-agent、Disallow、Allow三段核心指令提取出来。实在解析不了的就在日志里标记为“Unknown”然后默认按“允许配套路径以外的全站”处理同时放到人工复审列表里。总之robots.txt的解析一定要有容错机制不能因为一个文件格式奇怪就阻塞整个爬虫任务。4.3 分布式爬虫里的合规配置容易漏分布式爬虫是另一个重点场景。很多人把robots.txt解析放在主节点但真正发请求的是各个子节点。如果主节点只做了一次判断然后把任务分发下去任务队列里同时有几十万个 URL主节点根本没有办法保证每个 URL 都实时被 robots 规则约束。我参与过一个分布式爬虫项目总共有 20 多个采集节点主节点负责 URL 去重和任务分发。最开始我们把 robots 判断放在了主节点的 URL 过滤阶段后来发现新上线的一个域名改了 robots 规则主节点缓存没刷新子节点又不会主动去检查导致那个域名下被禁止的路径被全部抓了一遍。虽然数据量不大但这个错误给团队提了个醒。现在我的做法是主节点负责粗粒度过滤域名级别每个子节点在真正请求 URL 之前再查一次本地缓存的 roborts 规则。这样即使主节点漏掉了子节点也能兜底。代价是每个子节点都要维护一份 robots 缓存但为了合规和稳定这个成本非常值得。4.4 爬虫请求频率与 Crawl-delay 的配合在真实项目里除了要遵守 robots 的允许或禁止规则另一个重要的点是频率控制。很多网站的robots.txt里会写Crawl-delay但有一些不写。不写不代表可以无限速地抓而是“没有明确约定”的意思。我自己一般会遵循以下几个原则如果robots.txt里写了Crawl-delay严格遵守。如果没有写默认设置 1 到 3 秒的请求间隔视网站体量而定。抓取过程中注意监控响应状态码如果开始出现大量的 429、503 或者断连说明频率太高必须立刻降低并发。有朋友问过我既然有些站点根本没有严格执行Crawl-delay我为什么还要这么保守我的答案是“君子协议”的关键在于自律而自律的首要体现就是尊重对方的服务器资源。一次爬取任务如果因为频率太高导致对方宕机影响的不仅是你的项目还有大量正常访问网站的用户。这种损人不利己的事做技术的真没必要干。5. 法律边界与从业素养5.1 robots.txt 为什么不是法律文件我发现很多刚入行的朋友有一个误解以为只要robots.txt允许抓取就可以放心大胆地采集并用于任何用途。这是一个危险的信号。robots.txt本质上是一个技术层面的“访问许可提示”它不是法律文件也不具备合同效力它只代表网站运营方对“机器访问行为”的一种态度。在实际司法案例中robots.txt的设定是判断爬虫行为是否合规的一个参考因素但绝对不是唯一依据。即使某条路径在robots.txt里是Allow的如果你抓取的数据包含用户隐私信息、版权内容或者将数据用于不正当竞争依然可能引发法律纠纷。反之即使robots.txt禁止抓取只要你抓的内容合法、手段合规、不造成服务器负担也有较大的抗辩空间。所以我认为成熟的爬虫工程师应该具备两个层面的判断能力第一层是“技术层”严格按照robots.txt来判断能不能访问第二层是“业务层”判断拿到数据之后做什么用途、是否合规。两个层面缺一不可。5.2 我常用的判断标准与底线干爬虫这行快十年我自己总结了一套简单可执行的判断标准在这里分享给大家个人学习、研究用途的公开数据抓取遵守robots.txt即可。企业商用、对外输出数据的产品一定要通过法务审核并保留数据来源和抓取方式的完整日志。凡是涉及个人隐私信息、未公开的商业数据、或者破解登录鉴权的抓取无论robots.txt怎么写都不要尝试。被抓取的内容如果明确标注了“未经授权禁止转载”一定要主动规避哪怕技术上完全能抓到。我在团队里给新同学培训时也反复强调爬虫技术本身是工具工具没有善恶但使用工具的人有选择。选择做一个让别人愿意沟通的爬虫工程师还是做一个到处被人拉黑的“爆破手”决定了这个行业对你来说是长期事业还是短期买卖。还有一点是日志。我发现很多爬虫项目对日志记录不够重视等出了问题要排查时连“什么时候抓了哪些 URL”都说不清。现在我要求团队里每个爬虫任务都必须记录每次请求的目标 URL请求时的 UA当时的 robots.txt 内容摘要响应状态码抓取耗时这份日志不仅是技术排障的依据也是合规审计的时候最重要的自我保护材料。真出了争议你能拿出一份完整的日志说明你是在可控、可追溯的前提下做技术实验这会比任何口头解释都有说服力。最后再分享一个我自己的小习惯每次新建一个爬虫项目我都会在项目 README 的最前面放上目标站点的robots.txt核心规则摘要以及“是否允许抓取”的判断结论。这样哪怕项目半年后没人维护新接手的人也能第一时间了解这个爬虫当初设计的合规边界在哪。很多人瞧不上这个细节但我在几个长期项目的交接里确实从中受益不少。