您购买了住宅代理,设置了最新的 Chrome User-Agent,但网站在第一次请求时仍然返回 403。 这听起来熟悉吗?问题不在于 IP 或标头。您在服务器读取任何 HTTP 标头之前就被识别出来了——通过 TLS 握手。在 2026 年,这将是检测的主要途径,而普通的 requests 会自动失败。我们来分析一下它是如何工作的,以及如何通过几行代码使用 curl_cffi 修复它。
发生了什么:您被 TLS 握手识别
当客户端建立 HTTPS 连接时,它首先发送 ClientHello 数据包——在任何 HTTP 之前。在其中列出了:TLS 版本、支持的密码套件列表(cipher suites)、TLS 扩展(SNI、ALPN、supported_groups)、椭圆曲线和点格式。这些字段的顺序和组成在不同的客户端中 各不相同——根据这些字段,可以在客户端说出任何话之前识别出它。
从这些字段中计算出指纹。JA3(2017 年标准)获取格式为 TLSVersion,Ciphers,Extensions,EllipticCurves,ECPointFormats 的字符串,并对其进行 MD5 哈希,生成 32 字符的签名。JA3 的问题在于,自 2023 年 1 月起,Chrome 随机化扩展的顺序——16 个扩展产生 16!(超过 20 万亿)种组合,同一浏览器会输出不同的 JA3。
因此,行业转向了 JA4(FoxIO,2024–2025 年大规模实施)。JA4 在哈希之前根据十六进制值对扩展代码进行排序——Chrome 的随机化不再破坏它。哈希是截断的 SHA-256,格式可读且由三部分组成(a_b_c),包括 ALPN 和对 QUIC/HTTP3 的支持。例如:Chrome 124 生成 t13d1516h2(15 个密码,16 个扩展,ALPN h2),而裸 Python requests 生成 t13d1715h2。对于反机器人,第二个签名是明确的“这是脚本”的标记。
为什么在 2026 年没有这个就不行
JA4 检测嵌入了所有主要供应商中:Cloudflare 将指纹与允许列表进行比对,Akamai 添加了 HTTP/2 SETTINGS 帧的单独哈希,DataDome 与已知机器人数据库进行比较。逻辑简单而致命:如果您发送 User-Agent: Chrome 131,而 TLS 指纹却显示“urllib3/OpenSSL”——这就是 不同步,您会立即被阻止。没有任何代理能够拯救您:即使是完美的住宅 IP,带有 Python requests 的指纹也会失败。
正因如此,“代理 + 指纹伪造”的组合在 2026 年成为了抓取的基本卫生,而不是高级用户的选项。
解决方案:5 分钟内使用 curl_cffi
curl_cffi 是对 curl-impersonate(使用 Chrome 的 BoringSSL 或 Firefox 的 NSS 而非 OpenSSL 构建的修改版 curl)的 Python 封装。它重现了 真实 的浏览器握手,并且具有几乎与常用的 requests 相同的 API。
步骤 1. 安装。 curl-impersonate 的二进制文件会自动下载到 Windows/macOS/Linux:
pip install curl-cffi
步骤 2. 基本请求。 更改导入并添加一个参数:
from curl_cffi import requests
resp = requests.get("https://target.com/", impersonate="chrome")
print(resp.status_code)
print(resp.http_version) # HTTP/2 — 像真实浏览器一样
一行 impersonate="chrome" 同时伪造了四个层次:TLS 指纹(JA3/JA4)、HTTP 版本(HTTP/2 而非 HTTP/1.1)、标头顺序和 ALPN 协商。
步骤 3. 始终使用通用别名,而不是固定版本。 使用 impersonate="chrome"(或 "safari"、"safari_ios")——别名会自动解析为最新的配置文件。硬编码的 impersonate="chrome124" 会过时:Chrome 每 ~4 周更新一次,旧配置文件会变成异常。可靠的目标是 Chrome、Edge 和 Safari/iOS(配置文件从 chrome99 到 chrome131,safari15–18)。
步骤 4. 代理和会话。 对于实际抓取,保持会话状态并连接代理。住宅或移动 IP 是必需的——数据中心的 IP 在 TLS 之外会被单独识别:
from curl_cffi import requests
session = requests.Session(impersonate="chrome")
headers = {
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Referer": "https://www.google.com/",
}
proxies = {
"http": "http://user:pass@proxy-host:port",
"https": "http://user:pass@proxy-host:port",
}
resp = session.get("https://target.com", headers=headers, proxies=proxies)
步骤 5. 异步处理以提高吞吐量。 与 requests 不同,curl_cffi 默认支持异步和 HTTP/2:
import asyncio
from curl_cffi.requests import AsyncSession
async def fetch(session, url):
r = await session.get(url, impersonate="chrome")
return r.status_code
async def main(urls):
async with AsyncSession() as session:
return await asyncio.gather(*[fetch(session, u) for u in urls])
asyncio.run(main(["https://target.com"] * 20))
检查您的指纹——不要猜测
在发送实际流量之前,请确保伪造确实有效。向公共验证器发送请求,并将 JA4 与标准浏览器进行比较:
- tls.peet.ws——返回 JA3、JA4、Akamai 指纹和 HTTP/2 帧的 JSON。通过
curl_cffi和真实的 Chrome 请求它,比较哈希。 - ja4db.com——已知 JA4 的数据库,帮助您了解您与谁相似。
- browserleaks.com/tls 和 Scrapfly 的 JA3/JA4 工具——详细的字段拆分。
在阶段中,可以将 mitmproxy 放置在抓取器和目标之间,监控每个请求的实际 JA4 哈希。
诊断:仍然收到 403/429
如果指纹正确,但仍然被阻止——请按照常见到不常见的检查清单进行检查:
- 数据中心 IP。 原因 №1。切换到 住宅代理 或 移动代理——为什么单一指纹不足,详细讨论在关于 通过 IP Intelligence 检测住宅代理 的材料中。
- 过时的配置文件。
pip install -U curl-cffi和通用别名"chrome"。 - 速率过高。 在请求之间添加 1–3 秒的随机暂停。
- 裸标头。 一定要发送
Accept-Language、Accept-Encoding、Referer——缺少它们也是异常。 - 会话与 IP不同步。 规则:一个会话——一个 IP,持续整个生命周期。
- 状态 200 ≠ 成功。 检查响应体:状态码 200 可能是带有 CAPTCHA 的页面。
curl_cffi 的局限性
curl_cffi 关闭了网络层——就是这样。它不执行 JavaScript。因此,对于 JS 挑战,它无能为力:Cloudflare Turnstile、页面“检查您的浏览器……”(IUAM)、脚本在检查后设置的 cf_clearance cookie——所有这些都需要真实的浏览器环境。为什么在 2026 年,验证码解决者几乎无法对抗这些预防性系统,我们在关于 绕过 CAPTCHA 的单独分析中进行了讨论。
当您遇到 JS 障碍时该怎么办:
- 混合方案。 Playwright 或 Nodriver 处理挑战并获取
cf_clearance,然后将 cookie 传递给快速的curl_cffi进行大多数请求——这样您只需为重型浏览器支付一次。 - 解决者服务(CapSolver、2Captcha)用于自动发放令牌。
- 托管抓取 API,如果您不想维护基础设施。
并且请记住线程安全:每个线程都有自己的会话。在 requirements.txt 中固定 curl-cffi 的版本,并在浏览器更新时每 6–12 周审查一次配置文件。
curl_cffi 的替代品
- tls-client——基于 uTLS 的 Go 库的封装,具有配置文件(
chrome_124、safari_ios_17)和random_tls_extension_order=True标志。灵活的指纹微调。 - primp——基于 Rust 的客户端,允许独立设置
impersonate_os,并提供更高的带宽;缺点是 API 与requests不完全一致,且库较新。
需要什么样的代理以及原因
指纹伪造和代理解决了同一任务的不同方面:curl_cffi 解决了“连接的外观”问题,代理则解决了“来源”问题。反机器人独立检查这两个信号,因此完美的 JA4 与黑色数据中心 ASN 是无用的。对于受保护的目标(市场、社交媒体、旅游聚合器),请使用 住宅 或 移动代理:它们具有干净的运营商来源,而移动代理还隐藏在 CGNAT “人群效应”后面。数据中心则留给不敏感的目标和高流量。
结论
在 2026 年,抓取是一场身份的游戏,而不仅仅是 IP。裸 requests 在 TLS 握手级别被视为脚本,并在第一个标头之前就失败。将导入替换为 curl_cffi 和 impersonate="chrome" 可以在五分钟内消除这一失败,但仅在与干净的住宅或移动 IP 结合使用,并理解界限:网络层——可以,JavaScript 挑战——不可以。诚实地构建堆栈:正确的指纹、正确的代理、在有 JS 障碍的地方与浏览器的混合——这样在第一次请求时的 403 将成为过去。
