供應商的 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": "ada@example.com", "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 道題,作答後立即看到解釋。