当爬虫发出的流量与目标站点对真实访客的预期不符时,就会遭到封锁。请求来自声誉不佳的地址,携带的请求头稀少或自相矛盾,触发频率超出任何人工操作的速度,或者请求的页面从未对机器人开放, 以上任何一个特征,都足以让现代反机器人系统返回 403、CAPTCHA 或空页面,而不是你所需要的数据。

本指南涵盖让爬虫看起来像真实浏览器的实用策略:轮换住宅 IP、发送逼真的请求头和 User-Agent、对请求频率进行限流和随机化、遵守 robots.txt、在页面需要时渲染 JavaScript、处理 CAPTCHA、管理会话与 Cookie,以及监控状态码以便在软性限速升级为封禁之前及时退让。没有任何一招是万能的,但按正确顺序叠加使用,大多数目标都能从一面封锁之墙变成源源不断的 200 响应。

爬虫被封锁的原因

在讲策略之前,先了解对手很有帮助。反机器人系统通过三类广义信号标记自动化流量,几乎每一次封锁都可以追溯到其中之一。

  • 指纹。 真实浏览器携带一组一致的信号:完整的请求头集合、与所声称 User-Agent 相匹配的 TLS 握手、JavaScript 运行时,以及在请求间持久保存的 Cookie。默认 HTTP 客户端几乎不具备这些特征,而半欺骗的客户端则携带互相矛盾的信号。无论哪种情况都会显得格外突兀。
  • 频率。 站点会统计每个 IP 和每个会话在单位时间内的请求数。无论单次请求看起来多么干净,只要流量到达速度超过人工操作的速度,或者保持完美规律的间隔,就会被识别为脚本。
  • IP 声誉。 来自已知数据中心段、在共享封锁列表上或有过滥用历史的地址,从第一个请求起就会受到怀疑。你所使用的 IP 在你发送任何请求头之前,就已经决定了你的初始可信度。

下面的每一个策略都是在修复上述三类信号之一。先应用成本最低的策略,衡量你的封锁率,只有在目标确实迫使你使用重型手段时,才去动用它们。

轮换住宅 IP

最常见的封锁也是最简单的:同一地址发送了太多请求。站点会统计每个 IP 的访问量,一旦超过阈值就开始返回 429 或封锁页面。将相同数量的请求分散到多个 IP 上,任何单个地址都不会触发限制。这就是为什么爬虫基础设施的核心是代理基础设施, 代理代替你发送请求,使目标看到的是代理地址而非你的真实地址。

IP 的类型与轮换本身同样重要。数据中心 IP 速度快、成本低,但它们位于托管段内,目标只需简单查询就能识别,因此在有防护的站点上会被视为自动化流量。住宅 IP 通过真实消费者连接出口,看起来像普通访客,但成本更高、速度更慢。完整的权衡分析见数据中心代理与住宅代理的对比。根据目标的要求购买恰好够用的可信度,不要多花一个档次:严格站点用住宅 IP,宽松站点用数据中心 IP。

手动轮换意味着维护一个地址池,按请求循环使用,然后剔除被封的地址。轮换网关将这一切隐藏在单个端点后面,在后端自动为你更换出口 IP,可以按请求每次更换,也可以在需要跨多个页面保持同一身份时按会话保持粘性。

发送逼真的请求头和 User-Agent

默认 HTTP 客户端在发出的第一行就暴露了自己。Python 的 requests 库会声明 User-Agent: python-requests/2.x,并且几乎不带其他请求头,而真实浏览器会按照特定顺序发送十几个请求头。仅仅读取这个请求头的站点会封锁第一个请求,放行第二个。

设置一个当前且真实的浏览器 User-Agent,并在一个小的 User-Agent 池中轮换,而不是永远使用同一个字符串。同时发送总是与之一起出现的请求头:AcceptAccept-LanguageAccept-Encoding,以及一个合理的 Referer。目标不是某个神奇的请求头,而是内部一致性。Chrome User-Agent 配上 Firefox 风格的 Accept 值,比完全不欺骗还要可疑。

python
import requests

headers = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/124.0 Safari/537.36"
    ),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Referer": "https://www.google.com/",
}
resp = requests.get("https://example.com", headers=headers)

更深一层是 TLS 指纹。在发送任何 HTTP 字节之前,客户端会建立 TLS 握手,其精确形态构成一个特征,通常以 JA3 哈希值来概括。真实的 Chrome 会产生一个众所周知的特征值,而 Python 客户端会产生一个截然不同的值。当你用 Python TLS 栈发送 Chrome User-Agent 时,两者并不匹配,无论请求头多么完美,指纹检查都会标记这一不一致。弥合这一差距意味着使用能模拟浏览器握手的客户端,或者驱动一个真实的浏览器引擎,后者会免费产生真实的指纹。

对请求频率进行限流和随机化

即使分散在多个 IP 上,以固定节奏发送请求的爬虫仍会被识别为自动化流量。500ms 的完美规律间隔本身就是一个指纹,因为人类不会像节拍器一样点击。用随机延迟代替固定延迟,并将并发控制在目标能够承受而不引起异常的水平。

使用不规则的、类似人工的爬取模式这一传统建议依然有效:变化你的间隔,不要按固定顺序爬取页面,避免同时向同一主机发送大量请求。频率控制的另一半是减少本不需要产生的负载。缓存已经获取过的页面,永不重复请求,只爬取你实际需要的内容,而不是整个站点。

遵守 robots.txt 并避开蜜罐

在任何绕过技术之前,先阅读站点的 robots.txt。它声明了运营者愿意被爬取的路径,以及通常会包含的 Crawl-delay,告诉你请求之间的最短间隔。遵守它一方面是礼貌,另一方面也是自我保护:忽视声明的规则是被标记的最快方式,而这也是服务条款问题开始出现的界限。同时查看站点的条款;如果明确禁止爬取,这是重新考虑目标的信号。

还有一个相关陷阱是蜜罐, 一种通过 CSS(display:none、零尺寸或屏幕外定位)对人眼隐藏,但仍存在于 HTML 中的链接。跟随所有 <a> 标签的简单爬虫会直接走入其中,立刻暴露自己是机器人,因为没有真实用户会点击看不见的链接。只跟随渲染后的浏览器实际显示的链接,跳过任何视觉上隐藏的内容。

像浏览器一样渲染 JavaScript

很多页面返回的 HTML 几乎是空的,真实内容在加载后由 JavaScript 构建。用普通 HTTP 客户端获取这类页面,只能得到一个没有数据的外壳。一些站点更进一步,提供 JavaScript 挑战:在真实页面交付之前必须运行并通过一段小脚本,而非浏览器客户端永远无法通过。

这两种情况都需要真实的浏览器引擎。Playwright、Puppeteer 或 Selenium 驱动 Chrome 的无头浏览器可以加载页面、运行其脚本,并将用户所见的 DOM 呈现给你。它还能产生真实的浏览器 TLS 指纹和真实的 navigator 对象,从而通过原始客户端无法通过的一类检查。代价是资源消耗:无头浏览器每页消耗的 CPU 和内存远多于简单请求,因此要将其保留给真正需要渲染的页面。完整演练见如何爬取 JavaScript 网站以及 Python 专项指南用 Python 爬取 JavaScript 页面

处理 CAPTCHA

CAPTCHA(全自动区分计算机和人类的公共图灵测试)是站点在怀疑请求为自动化时展示的挑战。许多站点集成了对每位访客进行评分的算法,当分数看起来像机器人时触发 CAPTCHA。一旦遇到,无论怎么调整请求头都无法在该请求中获取数据。

持久的解决方案是首先避免触发它们:干净的住宅 IP、一致的指纹,以及类人的频率,能将你的机器人分数保持在足够低的水平,使挑战永远不会触发。当目标仍然展示 CAPTCHA 时,在服务端解决或绕过 CAPTCHA 的托管爬取端点,远比将解决工具集成到自己的技术栈中更可靠。如需详细了解,如何在网页爬取中绕过 CAPTCHA 涵盖了各种选项。

Crawlbase Crawling API

当目标需要的不仅仅是一个干净的 IP 时,Crawling API 会为你接管整个技术栈:它在大量数据中心、住宅和移动出口池中轮换,发送可信的指纹,在页面需要时渲染 JavaScript,并在服务端处理 CAPTCHA 和封锁。如果你只需要轮换 IP 这一层,Smart AI Proxy 通过同一网络的单一端点路由普通请求。你发送一个 URL,获取最终结果。先用免费套餐测试你的真实目标。

许多站点在第一次访问时设置 Cookie,并期望在之后的每个请求中看到它。在请求之间丢弃 Cookie,你每次看起来都像一个全新的、可疑的无状态访客,这会触发假设真实用户在浏览过程中会积累状态的行为检查。使用在请求间持久保存 Cookie 的会话,使多步骤流程(搜索、翻页、打开详情页)在整个过程中保持同一身份。

会话与 IP 轮换相互影响,因此需要协调两者。如果你在会话中途轮换到新的 IP,之前地址颁发的 Cookie 现在从另一个地址到达,这本身就是一个标记。在一个逻辑会话期间保持粘性 IP,在开始新会话时再进行轮换。下面的示例使用 requests.Session 在多次调用中保持 Cookie 和请求头的一致性。

python
import requests

session = requests.Session()
session.headers.update(headers)

# Cookies set on the first call ride along on the rest.
session.get("https://example.com/search?q=phones")
session.get("https://example.com/search?q=phones&page=2")

监控状态码并及时退让

你的爬虫应该将 HTTP 状态码视为实时反馈,而不仅仅是成功或失败。连续的 429(请求过多)或 503 响应是服务器在告诉你放慢速度。要遵从这一信号:指数退避,在服务器发送 Retry-After 响应头时尊重它,并将一连串 429 视为降低整体频率的信号,而不是更用力地重试。以全速猛击限速端点,正是软性限速变成硬封禁的方式。

其他状态码也各有含义。403 通常意味着指纹或 IP 声誉封锁,因此更改请求频率无济于事,你需要更好的 IP 或更可信的指纹。突然出现的 200,但返回的是 CAPTCHA 页面而非内容,这是伪装的封锁,因此要验证响应体,而不仅仅是状态码。

python
import time, random

def fetch(session, url, tries=4):
    for attempt in range(tries):
        resp = session.get(url)
        if resp.status_code == 200:
            return resp
        if resp.status_code in (429, 503):
            wait = int(resp.headers.get("Retry-After", 2 ** attempt))
            time.sleep(wait + random.uniform(0, 1))
            continue
        resp.raise_for_status()
    raise RuntimeError("exhausted retries")

负责任地爬取

保持不被封锁与负责任地爬取,是从两个角度看待同一纪律。阅读每个站点的服务条款和 robots.txt 并尊重其声明,坚守公开页面而非任何登录后的内容,并将请求频率控制在目标可以承受的水平。缓存已获取的内容以避免重复请求,只提取你实际需要的数据。像体贴的访客一样行事的爬虫,不仅被封锁的可能性大大降低,也更不可能造成值得封锁的问题。

回顾

核心要点

  • 封锁可追溯到三类信号。 指纹、频率和 IP 声誉涵盖了几乎所有封锁,每一个策略的作用都是修复其中之一。
  • 首先轮换住宅 IP。 大多数封锁是每 IP 的频率限制,因此将请求分散到大量可信地址,是成本最低、影响最大的修复手段。
  • 保持信号的一致性。 逼真的请求头、匹配的 TLS 指纹、持久的 Cookie 以及随机化的频率,组合起来比任何单一因素都更有说服力。
  • 尊重站点。 遵守 robots.txt 和服务条款,避开蜜罐,坚守公开数据,一旦 429 或 503 告诉你放慢,就立即退让。
  • 难度增加时外包出去。 当目标同时在渲染、CAPTCHA 和声誉上发起反击,托管爬取 API 或智能代理可以吸收整个技术栈,让你无需自己维护。

常见问题

为什么我的网络爬虫不断被封锁?

因为它的流量在三个维度中至少有一个看起来不像真实浏览器:指纹、频率或 IP 声誉。请求可能来自被标记的数据中心 IP,携带稀少或自相矛盾的请求头,或者到达速度超过人工点击的频率和规律性。反机器人系统只需要这些特征中的任何一个,就能返回 403、CAPTCHA 或空页面。

避免网络爬取请求被封锁,最有效的单一方法是什么?

在良好 IP 池(对严格目标而言最好是住宅 IP)中轮换。最常见的封锁是每 IP 的频率限制,将相同数量的请求分散到多个地址意味着没有任何一个地址会越过阈值。这是成本最低、影响最大的修复手段,这也是为什么它通常是在调整请求头或频率之前首先应用的技术。

仅仅更改 User-Agent 就足以阻止封锁吗?

在防御最弱的站点上有时可以;在任何认真的站点上,则不行。逼真的 User-Agent 必须与浏览器发送的完整请求头集合、与该浏览器匹配的 TLS 指纹、持久的 Cookie 以及可信的请求频率配合使用。在默认 HTTP 客户端 TLS 栈上使用伪造的 User-Agent,是指纹检查很容易识别的矛盾。

如何处理 429 Too Many Requests 响应?

放慢速度,而不是更用力地重试。指数退避,在服务器发送 Retry-After 响应头时尊重它,并将一连串 429 视为降低整体请求频率的信号。以全速猛击限速端点,是临时限速变成永久封禁的方式。

我需要无头浏览器来避免被封锁吗?

只有当页面在加载后由 JavaScript 构建内容,或者提供普通客户端无法通过的 JavaScript 挑战时,才需要。无头浏览器渲染页面并产生真实的浏览器指纹,可以通过原始请求无法通过的一类检查,但它每页消耗的 CPU 和内存要多得多。对于静态 HTML,配置良好的 HTTP 请求更快、更便宜,同样不会被封锁。

什么时候托管爬取 API 比自己构建更合适?

当目标同时在多个层面发起反击时。维护住宅代理池、请求头和 TLS 轮换、Cookie 会话、退避逻辑、无头浏览器集群和 CAPTCHA 路径,是真实的工程负担,一个新的挑战可能在一夜之间打破它。爬取 API 或智能代理将这一切隐藏在单个请求后面,你用每请求成本和一定的控制权,换取不再自己运营反机器人基础设施。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量