requests因OpenSSL默认TLS配置(固定加密套件顺序、缺ALPN/key_share扩展、椭圆曲线单一等)生成稳定JA3指纹,被WAF直接识别为爬虫;curl_cffi通过调用Chromium BoringSSL复刻真实浏览器握手行为,需用Session(impersonate="chrome120")确保指纹一致性。

现代网站在你发出第一个HTTP请求前,就已经把你拦在门外了——不是靠User-Agent,也不是靠IP频率,而是靠TLS握手时暴露的指纹。用requests发请求,哪怕User-Agent写得和Chrome一模一样,服务器收到ClientHello包的一瞬间就能判定:“这不是浏览器”,直接返回403或跳转到"Just a moment"页面。这不是配置问题,是协议层的身份 mismatch。
为什么requests的TLS指纹一眼就被识破
OpenSSL默认配置下,requests发出的ClientHello有这几个硬伤:
-
cipher_suites顺序固定、精简,缺少浏览器常用的CHACHA20、AES-GCM混合排列 - 不带
ALPN扩展,或只声明http/1.1,而Chrome/Firefox默认携带h2,http/1.1 -
supported_groups(椭圆曲线)只列secp256r1,缺x25519等现代曲线,且顺序反常 - 没有
key_share扩展(TLS 1.3必需),或内容格式不符合BoringSSL行为 - JA3指纹长期稳定如“a0a1a2b3c4d5”,WAF数据库里早被标记为Python脚本特征
curl_cffi:复用真实浏览器的TLS行为,不是伪造
它不靠Python代码拼凑参数,而是调用编译好的libcurl + Chromium BoringSSL,把Chrome的握手逻辑整个“搬”进Python进程。关键点不在“设什么值”,而在“怎么初始化”:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 必须用
requests.Session(impersonate="chrome120")显式创建会话,不能直接调requests.get(..., impersonate=...)(后者每次新建临时session,指纹失效) -
User-Agent头必须和impersonate值严格匹配,例如impersonate="chrome120"就要配Chrome 120的UA字符串 - 启用
http2=True(默认开启,但显式写上更稳妥),否则ALPN协商可能退化为http/1.1 - 禁用自动跳转:
follow_redirects=False,避免重定向时TLS状态重置导致指纹错乱
tls_client:轻量但需注意底层依赖版本
它走的是预编译libcurl动态链接路线,对系统环境更敏感:
立即学习“Python免费学习笔记(深入)”;
- Windows需确保
libcurl.dll路径在PATH中;Linux/macOS依赖libcurl.so,且版本必须≥8.5(旧版不支持key_share字段控制) - 不支持
impersonate字符串,而是用client_identifier="chrome_120"参数,可选值见官方文档,错填会回退到默认指纹 - 无法像
curl_cffi那样自动注入完整扩展集,某些高防站点(如Cloudflare Enterprise)需额外调用set_http_version("HTTP/2")并手动补全ALPN列表 - 并发场景下,每个
Session实例应绑定独立TLS上下文,共享实例可能因连接复用导致扩展字段污染
真正容易被忽略的不是“用哪个库”,而是会话生命周期管理——无论curl_cffi还是tls_client,只要每次请求都新建Session,就等于每发一次包都在重新亮明“爬虫”身份。指纹一致性只存在于单个Session实例的整个生命周期内。

















