必须换底层 tls 实现而非硬改 requests——openssl 固化行为导致 ja3 指纹可被 waf 稳定识别;curl_cffi 复用 chromium boringssl 栈最可靠,需 session 级 impersonate、匹配 ua、启用 http/2 且禁用自动跳转。

不能靠 requests 硬改,必须换底层 TLS 实现——OpenSSL 不支持动态控制 Client Hello 字段顺序和扩展内容,所有基于 ssl.create_default_context() 的补丁都不可靠。
为什么 requests + urllib3 无论如何配置都过不了 JA3 检测
WAF(如 Cloudflare、Akamai)在 TCP 连接建立后的第一个 TLS 握手包(Client Hello)里就完成识别,根本不会等到 HTTP 层。而 requests 底层依赖系统 OpenSSL,其行为是固化且可预测的:
-
cipher_suites顺序固定,不支持 TLS 1.3 的key_share扩展字段 - 椭圆曲线只默认启用
secp256r1,缺x25519,且顺序不符合 Chrome 行为 - ALPN 列表只有
http/1.1,没有h2;不带application_settings、signed_certificate_timestamp等浏览器标配扩展 - JA3 哈希值长期稳定(例如
a0a1a2b3c4d5...),WAF 数据库已将其标记为 Python 脚本特征
curl_cffi 是目前最稳的方案:复用 Chromium BoringSSL 栈
它不是“拼参数”,而是把 Chrome 的 TLS 握手逻辑原样搬进 Python 进程,由 libcurl + BoringSSL 驱动:
- 必须用
requests.Session(impersonate="chrome120")创建会话,不能对单次请求传参(否则每次新建 session,指纹失效) -
User-Agent必须与impersonate值严格匹配,例如impersonate="chrome120"就得配 Chrome 120 的 UA 字符串 - 显式启用
http2=True,否则 ALPN 协商可能退化为http/1.1,导致 JA4 不匹配 - 禁用自动跳转:
follow_redirects=False,避免重定向时 TLS 状态重置、指纹错乱
tls-client 可控但更依赖环境:注意 client_identifier 和动态链接
它用 Go 编写的独立 TLS 客户端封装,暴露了 ja3_string 和 client_identifier 参数,但对运行环境敏感:
- Windows 下需确保
libcurl.dll在PATH中;Linux/macOS 要求libcurl.so≥ 8.5(旧版不支持key_share控制) - 不能填错
client_identifier,合法值见官方文档(如"chrome_120"),填错会回退到默认指纹 - 不自动注入完整扩展集,高防站点(如 Cloudflare Enterprise)需额外调用
set_http_version("HTTP/2")并手动补全 ALPN 列表 - 每个
Session实例绑定一个 TLS 指纹,多账号并发必须用独立实例
真正难的不是“怎么设参数”,而是让 TLS 握手的每一个字节——从扩展 ID 顺序、曲线排列、到 SNI 大小写——都和真实浏览器一致。任何试图在 Python 层手动构造 Client Hello 二进制的行为,几乎必然因大小写、字段缺失或顺序错位被 WAF 的“异常握手”规则拦截。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











