# 403、429 和 CAPTCHA：响应，而非绕过

> 如何处理抓取时的 403、429 和 CAPTCHA：每种信号的含义，带 robots.txt 和 Retry-After 的礼貌 Python 爬虫，以及应避免的行为。

- URL: https://crawlrelay.com/zh/guides/403-429-captcha/
- 最后更新: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

403、429 或 CAPTCHA 是网站向您的爬虫发出的信号。正确的回应几乎总是放慢速度，阅读规则，或提出请求；错误的是伪装爬虫。这里教您如何将正确的回应写入代码。

**快速回答 — 爬虫应如何处理 403、429 和 CAPTCHA 响应？**

将它们视为信号，而非障碍。在 **429** 时，等待 `Retry-After` 并降低该主机上的请求频率。遇到 **403**，暂停后重试一次，若仍失败则停止并检查网站规则。遇到 **CAPTCHA**，停止访问该主机，不要自动破解。 **CrawlRelay 住宅代理**，来自 US$0.50/GB，分散负载并设定位置；它们不是绕过网站限制的方法。无需 KYC。支持加密货币支付。

## 三种信号一览

| 属性 | 值 |
| --- | --- |
| 429 请求过多 | 您爬取太快。遵守 Retry-After，退避，降低每主机并发请求数 |
| 403 禁止访问 | 请求被拒绝。暂停后重试一次，然后停止并检查 |
| CAPTCHA 或挑战页面 | 网站想确认您是否为真人。停止访问该主机并放慢速度 |
| 爬取规则规范 | robots.txt，RFC 9309；Crawl-delay 是常用扩展 |
| 我们不支持的内容 | CAPTCHA 破解，隐身插件，指纹伪装 |

## 每个信号对我的爬虫意味着什么？

- **429** 是最礼貌的信号：内容可用，但速度过快。RFC 6585 定义了它，通常带有指明何时重试的 `Retry-After` 头。
- **403** 是请求被拒绝。可能是可修复的（地区错误，缺少 header，请求过快）或最终拒绝（网站不允许自动访问该内容）。
- **CAPTCHA 或挑战页面**，通常伴随 403 或 503 状态码，表示网站的反爬机制怀疑您的流量。这是比忽略的 429 更严厉的升级措施。

这三者都是递增的信号。忽略 429 后，网站会升级到 403 和挑战，最终对整个 IP 段进行长时间封禁，影响该段所有用户。

## 每个信号爬虫该如何应对？

| 信号 | 首次响应 | 若持续出现 | 停止条件 |
| --- | --- | --- | --- |
| 429 | 等待 Retry-After 或带随机性的指数退避 | 该主机请求数减半 | 若 Retry-After 越来越长，或 429 持续单请求返回 |
| 403 | 暂停后重试一次，同国家 | 对比同国家浏览器访问，检查 robots.txt 和条款 | 同一 URL 模式第二次出现 403 |
| CAPTCHA / 挑战 | 暂停该主机数分钟 | 以更低速率恢复 | 低速下仍出现挑战，代表网站不允许自动访问 |
| 503 携带 Retry-After | 按要求等待 | 暂停该主机 | 多数请求失败超过几分钟 |

## 如何构建正确响应的爬虫？

将规则放在抓取器里，而非每个爬虫。该抓取器每主机一次只发一个请求，遵守 robots.txt 及其 `Crawl-delay`，等待 `Retry-After`，连续三次拒绝后停止访问。它将 4xx robots.txt 视为允许，5xx 视为完全禁止，符合 RFC 9309 规范。

*Python：礼貌的抓取器*

```python
import time
import urllib.robotparser
from urllib.parse import urlsplit

import requests

PROXY = "http://USERNAME-country-us:PASSWORD@gw.crawlrelay.com:7777"
USER_AGENT = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"


class PoliteFetcher:
    """One request at a time per host; robots.txt, Crawl-delay and Retry-After honoured."""

    def __init__(self, min_interval=1.0, max_refusals=3):
        self.min_interval, self.max_refusals = min_interval, max_refusals
        self.http = requests.Session()
        self.http.proxies = {"http": PROXY, "https": PROXY}
        self.http.headers["User-Agent"] = USER_AGENT
        self.robots, self.next_time, self.refusals = {}, {}, {}

    def _robots(self, root):
        if root not in self.robots:
            rp = urllib.robotparser.RobotFileParser(root + "/robots.txt")
            try:
                r = self.http.get(root + "/robots.txt", timeout=(10, 30))
                if r.status_code >= 500:
                    rp.parse(["User-agent: *", "Disallow: /"])  # unreachable: assume disallow
                else:
                    rp.parse(r.text.splitlines() if r.status_code == 200 else [])  # 4xx: allowed
            except requests.RequestException:
                rp.parse(["User-agent: *", "Disallow: /"])
            self.robots[root] = rp
        return self.robots[root]

    def get(self, url):
        scheme, host = urlsplit(url)[:2]
        if self.refusals.get(host, 0) >= self.max_refusals:
            return None  # this host said no: stop asking
        rp = self._robots("%s://%s" % (scheme, host))
        if not rp.can_fetch(USER_AGENT, url):
            return None
        delay = max(self.min_interval, rp.crawl_delay(USER_AGENT) or 0)
        wait = self.next_time.get(host, 0) - time.monotonic()
        if wait > 0:
            time.sleep(wait)
        r = self.http.get(url, timeout=(10, 30))
        pause = delay
        if r.status_code in (429, 503):
            retry_after = r.headers.get("Retry-After", "")
            pause = max(delay, int(retry_after) if retry_after.isdigit() else 60)
        refused = r.status_code in (403, 429) or "captcha" in r.text[:5000].lower()
        self.refusals[host] = self.refusals.get(host, 0) + 1 if refused else 0
        self.next_time[host] = time.monotonic() + pause
        return r


fetcher = PoliteFetcher()
resp = fetcher.get("https://example.com/")
print(resp.status_code if resp is not None else "skipped")
```

## 如何合法修复 403？

部分 403 原因在您这边，修正它们是合理的：

- **地区错误。** 许多网站仅对特定区域提供服务。使用 `-country-<code>` 针对目标国家。
- **缺失 Header。** 发送 `Accept`，`Accept-Language` 和带联系 URL 的真实用户代理。仅 HTTP 客户端使用类浏览器 User Agent 不一致，常被拒绝。
- **请求速度过快。** N 次请求后出现 403 是速度问题，应放慢。

不能算修复的是：频繁切换 User Agent 装作不同用户、伪造浏览器指纹、或者更换 IP 以绕过网站故意设置的封禁。如果网站过滤托管 IP 段以屏蔽自动流量，使用住宅代理 IP 并不能让访问合法。

## 为什么不直接解决 CAPTCHA？

因为 CAPTCHA 是网站提出一个问题，而你可能会误答。自动解决会把一个礼貌的爬虫变成一个专门绕过访问控制的工具，这会增加你的法律风险：2026 年美国关于搜索抓取案件，如 Google 诉 SerpApi 和 Reddit 诉 SerpApi 等，涉及被告绕过反自动化系统违反 DMCA 的反规避条款。法院对此尚无定论，但风险是真实存在的。

CrawlRelay 不支持 CAPTCHA 解决或反机器人检测，且其[可接受使用政策](https://crawlrelay.com/zh/acceptable-use/)] 已禁止通过网关使用这些功能。如果你需要访问受挑战保护的数据，请向网站申请访问权限，使用其 API，或许可数据。

## 我什么时候应该联系网站？

当你需要超过网站默认允许的访问时。许多网站提供 API、数据源、合作伙伴计划，或者对能够自报身份并说明用途的爬虫提高限制。在你的用户代理里放入联系 URL，保持公开一页介绍你的爬虫和退出方式，及时响应滥用报告。能被联系的爬虫在被封锁前会收到警告。

## 403，429 和 CAPTCHA：常见问题

### 住宅代理能阻止 CAPTCHA 吗？

别指望它。住宅出口 IP 会改变网站看到的 IP，而不是你的爬虫行为：因请求频率或行为而触发的 CAPTCHA 依然存在，这是网站的本意。如果低频请求仍被挑战，说明网站不欢迎自动访问该内容。

### 如果 429 状态码没有 Retry-After，我该等多久？

从几秒开始，遇到每个新的429时将等待时间翻倍，同时加一点随机延迟，最多等约一分钟。当请求恢复成功，按触发 429 前的较低频率恢复爬取，随后在错误率稳定时慢慢提高速率。

### 轮换 IP 来规避速率限制允许吗？

轮换有助于分散负载并选择不同地区，是正常做法。但用轮换来维持低于单 IP 限制的请求频率，同时保持网站拒绝的速率，是规避手段。大多数使用条款禁止此行为，我们的可接受使用政策也禁止。

### 我的爬虫应该发送什么用户代理？

用你自己的：包含名字、版本和可说明爬虫及联系方式的 URL，例如 `ExampleCrawler/1.0 (+https://example.com/crawler-info)`。不要用其他爬虫的名字，比如 Googlebot 或 GPTBot。

## 来源

1. [RFC 9309：机器人排除协议](https://www.rfc-editor.org/rfc/rfc9309.html) — IETF (2022-09)
2. [RFC 6585：新增 HTTP 状态码（429）](https://www.rfc-editor.org/rfc/rfc6585) — IETF (2012-04)
3. [Reddit 诉 SerpApi LLC 案件](https://www.loeb.com/en/insights/publications/2026/08/reddit-v-serpapi-llc) — Loeb & Loeb (2026-08)
4. [Google 诉 SerpApi 案件：法院同意我们的撤诉请求](https://serpapi.com/blog/google-v-serpapi-the-court-granted-our-motion-to-dismiss/) — SerpApi (2026-07-21)

**礼貌爬虫的住宅代理** US$1.50，1 GB起，US$0.50/GB从50 GB起。 [查看价格](https://crawlrelay.com/zh/pricing/) · [错误参考](https://crawlrelay.com/zh/docs/errors/)

---

CrawlRelay: CrawlRelay 是为开发者设计的住宅代理和 4G/5G 移动代理轮换服务，适用于爬虫和 AI 数据管道：按需付费且流量永不过期，最低流量从 US$0.50/GB 起，支持加密货币支付，无需上传身份证明。无需 KYC —— 无需身份证、自拍和任何证件。支持加密货币支付。
