供应商的 robots.txt 禁止抓取 /wholesale/,而那正是 Maya 想要的页面。许可现在也是解析器的一部分。当答案是「不行」时,你怎么办?
解析器能工作,不代表你有权收集所有能访问的内容。robots.txt 发布抓取偏好,站点条款规定额外规则,隐私法规还可能限制个人数据。礼貌是技术问题:表明身份、限制请求、缓存结果,并在站点拒绝时停止。 你还会让 RobotFileParser 应用路径规则,并解析重试时间。
阅读站点公布的规则
robots.txt 是站点根目录下的纯文本文件。它包含面向不同 User-agent 的分组,以及 Allow、Disallow、Crawl-delay 和 Sitemap 等指令。它是抓取偏好,不是许可证,也不能替代站点条款。
robots = "User-agent: *\nDisallow: /private\nAllow: /docs\nCrawl-delay: 2"
rules = {}
for line in robots.splitlines():
if ":" in line:
key, value = line.split(":", 1)
rules[key.strip().lower()] = value.strip()
print(rules["disallow"])
print(rules["crawl-delay"])一个很小的键值视图
遵守匹配的 User-agent 分组
规则按 User-agent 分组。没有更具体分组匹配时,* 分组才适用。生产爬虫应使用维护良好的 robots 解析库;这里的小函数只展示问题形状,不是完整标准实现。
def rules_for(text, agent):
active = False
rules = []
for line in text.splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
key, _, value = line.partition(":")
key = key.strip().lower()
value = value.strip()
if key == "user-agent":
active = value == "*" or value.lower() == agent.lower()
elif active and key in {"allow", "disallow"}:
rules.append((key, value))
return rules
text = "\n".join([
"User-agent: *",
"Disallow: /private",
"User-agent: GoodBot",
"Allow: /private/public",
])
print(rules_for(text, "GoodBot"))按分组读取,而不是拍平
请求前先检查路径
路径检查应发生在 URL 进入队列之前。更长、更具体的规则通常优先,Allow 可以覆盖范围更大的 Disallow。把本地实现当成护栏,而不是法律意见。
rules = [("allow", "/docs"), ("disallow", "/private")]
path = "/private/report"
decision = "allowed"
for kind, prefix in rules:
if path.startswith(prefix):
decision = kind
print(decision)简单的路径护栏
放慢速度,少收数据
遵守 Crawl-delay 和 Retry-After,遇到 429 或 503 要退避,缓存已经拿到的页面。只收集任务需要的字段。不要绕过登录、付费墙、验证码或访问控制,也不要在没有合法依据时收集个人数据。
record = {"name": "Ada", "email": "[email protected]", "bio": "Python"}
public = {key: value for key, value in record.items() if key != "email"}
print(public)只保留任务需要的字段
让 RobotFileParser 判断路径规则
不要用一个子串检查来实现 robots.txt 的优先级。urllib.robotparser 会处理分组、通配 user-agent,以及更长的 Allow/Disallow 路径。测试时解析一份保存的规则,再用 can_fetch() 查询具体 user-agent 和路径。
from urllib.robotparser import RobotFileParser
lines = [
"User-agent: *",
"Disallow: /private",
"Allow: /private/public",
"",
"User-agent: BadBot",
"Disallow: /",
]
robots = RobotFileParser()
robots.parse(lines)
for path in ["/", "/private", "/private/public"]:
print(path, robots.can_fetch("PyPracticeBot", path))
print(robots.can_fetch("BadBot", "/"))具体分组和更长路径
解析 Retry-After 和 Crawl-delay
Retry-After 可以是秒数,也可以是 HTTP 日期。Crawl-delay 是请求间隔提示,不是站点能承受任意并发的承诺。两者都转成有上限的等待;缺失或格式错误时用保守默认值。
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
def retry_seconds(value, now):
try:
return max(0, int(value))
except (TypeError, ValueError):
try:
when = parsedate_to_datetime(value)
except (TypeError, ValueError):
return 60
return max(0, int((when - now).total_seconds()))
now = datetime(2026, 10, 28, 12, 0, tzinfo=timezone.utc)
for raw in ["120", "Wed, 28 Oct 2026 12:02:00 GMT", "bad"]:
print(raw, retry_seconds(raw, now))
print("Crawl-delay", 2)秒数、日期和兜底值
本课要点
- robots.txt 发布抓取偏好,不是许可证,也不构成法律意见。
- User-agent 分组决定哪些 Allow 和 Disallow 规则适用。
- 路径检查应在请求入队前完成,而不是下载之后。
- 遇到 429 和 503 要退避,并遵守 Crawl-delay 与 Retry-After。
- 不要绕过访问控制,也不要在没有合法依据时收集个人数据。
- 用真正的 robots 解析器;Retry-After 要封顶,Crawl-delay 只当提示。
常见错误
- 因为页面公开可访问,就认为 robots.txt 可选。
- 忽略 429,立即重复请求。
- 任务只需要两个字段,却收集页面上的所有内容。
- 用 startswith() 判断 robots 规则,忽略更长的 Allow 路径。
把一段小型 robots.txt 解析成字典,打印 Disallow 路径和 Crawl-delay,并解释生产爬虫为什么需要完整解析器。
动手试试
改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。
练习本课
12 道题,作答后立即看到解释。