Python爬虫进阶:使用curl_cffi模拟Chrome TLS指纹绕过反爬检测

Python爬虫进阶:使用curl_cffi模拟Chrome TLS指纹绕过反爬检测

1. 项目概述:当爬虫遇上TLS指纹检测

最近在折腾一个数据采集项目时,遇到了一个相当棘手的问题:目标网站明明没有复杂的验证码,也没有动态加载的JavaScript,但我的Python爬虫脚本就是连首页都打不开。用requests库发起的请求,要么直接返回一个空白页,要么就是经典的“403 Forbidden”或者“Access Denied”。一开始我以为是IP被封了,换了代理、加了延时,甚至模拟了完整的请求头,结果依然如故。后来,在浏览器的开发者工具里仔细对比了网络请求,我才发现问题出在一个更深层的地方——TLS(传输层安全协议)握手环节。对方服务器不是在检查我的请求头,而是在检查我的客户端(也就是爬虫程序)在建立加密连接时“出示”的“身份证”,也就是TLS指纹。

这就像你去参加一个高端俱乐部的聚会,门口保安不光看你穿什么衣服(请求头),还会用一种特殊仪器扫描你衣服的材质、纽扣的纹路,甚至走路的步态(TLS握手特征)。如果你穿得再像会员,但步态扫描不过关,一样会被拒之门外。传统的爬虫手段,如requestsaiohttp,甚至搭配selenium驱动真实浏览器,在应对这种级别的检测时都开始力不从心。requests的TLS指纹是固定的、容易被识别的Python客户端特征;而selenium虽然驱动了真实浏览器,但开销巨大,且浏览器的自动化特征(如webdriver属性)本身也容易被反爬。

于是,一个名为curl_cffi的Python库进入了我的视线。它的核心卖点,正是标题所说的:模拟特定版本Chrome浏览器的TLS指纹。它不是简单地修改请求头,而是深入到加密连接的底层,让我们的Python爬虫程序在建立HTTPS连接时,发出的“问候语”(Client Hello报文)和Chrome 124浏览器一模一样。这相当于给我们的爬虫程序克隆了一张Chrome 124的“网络身份证”,从而轻松绕过那些基于TLS指纹的检测和封禁。

这个项目,就是深入探索如何利用curl_cffi这个利器,将我们的Python爬虫武装到牙齿,使其在网络指纹层面“隐身”,从而稳定、高效地获取数据。无论你是遇到了类似的反爬困境,还是想提前为未来的数据采集项目做好技术储备,这篇从实战中总结的指南都值得你仔细阅读。

2. 核心原理:TLS指纹检测与curl_cffi的破局之道

要理解curl_cffi为何有效,我们必须先搞明白服务器端的TLS指纹检测到底在查什么。

2.1 TLS指纹是什么?

当我们用客户端(浏览器或爬虫)访问一个HTTPS网站时,第一步是进行TLS握手。在这个握手过程中,客户端会向服务器发送一个Client Hello消息。这个消息里包含了许多关键信息,共同构成了客户端的TLS指纹:

  1. TLS版本:例如 TLS 1.2 或 TLS 1.3。
  2. 加密套件(Cipher Suites):一个客户端支持的所有加密算法组合的列表,并且有严格的先后顺序。不同浏览器、不同版本的客户端,其支持的加密套件列表及顺序差异很大。
  3. 扩展(Extensions):例如SNI(服务器名称指示)、ALPN(应用层协议协商)、Supported Groups(支持的椭圆曲线组)等。扩展的类型、顺序和内容都是重要的指纹特征。
  4. 椭圆曲线(Elliptic Curves):客户端支持的椭圆曲线列表及顺序。
  5. 签名算法(Signature Algorithms):客户端支持的签名哈希算法对。

服务器收到Client Hello后,会解析这些信息。一个正常的Chrome 124浏览器,其Client Hello报文中的上述字段组合是唯一且可预测的。反爬系统会维护一个指纹库,将常见的浏览器指纹(如Chrome, Firefox, Safari各版本)标记为“正常”,而将一些已知的爬虫库、脚本客户端的指纹(如Python-urllib/3.x, Go-http-client/1.1)标记为“异常”。一旦匹配到异常指纹,请求在建立连接前就会被拦截或返回错误页面。

注意:这与简单的User-Agent检测有本质区别。修改User-Agent只是改了请求头里的一个字段,而TLS指纹是握手协议层面的特征,在请求头发出之前就已经被服务器感知到了。

2.2 为什么传统方法失效?

  • requests / aiohttp:这些库底层使用Python的ssl模块或httplib进行TLS握手。它们生成的Client Hello具有鲜明的Python客户端特征,极易被识别。
  • PyCurl:一个经典的cURL库的Python绑定。虽然功能强大,但其TLS指纹是cURL库本身的指纹,与主流浏览器不同,同样容易被标记。
  • Selenium / Playwright:它们驱动真实的浏览器内核,TLS指纹自然是真实的浏览器指纹。这是它们的优势,但代价是巨大的资源消耗(内存、CPU)和缓慢的速度。此外,浏览器自动化会留下诸如navigator.webdrivertrue等痕迹,需要额外脚本擦除,增加了复杂度。

2.3 curl_cffi如何实现完美模拟?

curl_cffi的“cffi”指的是C Foreign Function Interface。这个库的本质,是直接调用系统安装的cURL库(libcurl)的C语言API,而不是通过传统的Python绑定。它的魔法在于两个关键点:

  1. 底层调用:通过C接口直接操作libcurl,获得了对TLS握手过程更深层次的控制能力。
  2. 指纹注入curl_cffi项目预置了多个主流浏览器(如Chrome, Firefox, Safari, Edge)各个版本的TLS指纹配置文件。当你在代码中指定impersonate="chrome124"时,库会将这些指纹参数(加密套件顺序、扩展列表等)精确地配置给底层的libcurl。

这样,当libcurl代表你的Python程序发起TLS连接时,它发出的Client Hello报文与一台正在运行Chrome 124浏览器的电脑发出的报文在字节级别上几乎完全一致。对于只进行TLS指纹检测的服务器来说,你的爬虫程序就是一个“真正的”Chrome浏览器。

实操心得curl_cffi的模拟并非万能。它主要针对的是TLS指纹检测。如果网站还结合了JA3指纹(一种基于TLS握手报文生成的哈希值)、HTTP/2指纹、甚至是TCP/IP栈的指纹检测,那么curl_cffi可能仍需配合其他策略(如使用特定的代理网络)。但对于目前绝大多数基于TLS客户端指纹的反爬,它已经是降维打击般的解决方案。

3. 环境搭建与基础使用

理论讲清楚了,我们立刻动手,看看如何把curl_cffi用起来。

3.1 安装与前置条件

安装非常简单,使用pip即可。但有一点需要注意,curl_cffi依赖于系统安装的cURL库,并且要求版本不低于7.88.0(为了支持最新的指纹)。

# 直接使用pip安装 pip install curl-cffi # 或者使用清华源加速 pip install curl-cffi -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,你可以通过以下命令检查cURL库版本是否满足要求(虽然curl_cffi在导入时会检查,但提前确认更好):

curl --version

确保输出的版本号 >= 7.88.0。如果版本过低,你需要升级系统的libcurl。在Ubuntu/Debian上可以sudo apt update && sudo apt upgrade libcurl4,在macOS上通过brew upgrade curl

3.2 第一个爬虫:模拟Chrome 124访问

让我们写一个最简单的例子,感受一下它的威力。假设我们要访问一个对TLS指纹有检测的测试网站httpbin.org(它本身不检测,但我们可以观察请求的差异)。

from curl_cffi import requests # 传统的requests库 import requests as normal_requests print("【传统requests库】") try: resp = normal_requests.get("https://httpbin.org/headers", timeout=10) print(f"状态码: {resp.status_code}") # 打印服务器看到的请求头,其中User-Agent是python-requests print(resp.json().get('headers', {}).get('User-Agent', '')) except Exception as e: print(f"请求失败: {e}") print("\n" + "="*50 + "\n") print("【curl_cffi模拟Chrome 124】") try: # 注意:这里用的是 curl_cffi.requests,它的API和requests库基本一致 resp = requests.get("https://httpbin.org/headers", impersonate="chrome124", timeout=10) print(f"状态码: {resp.status_code}") # 查看User-Agent,已经是Chrome 124的版本 print(resp.json().get('headers', {}).get('User-Agent', '')) except Exception as e: print(f"请求失败: {e}")

运行这段代码,你会看到两个请求都成功了。但关键在于第二个请求发出的User-Agent已经自动被设置成了类似Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36的格式。更重要的是,在肉眼看不到的TLS握手层,第二个请求已经完全伪装成了Chrome。

impersonate参数是核心,它接受以下字符串值来模拟不同浏览器:

  • "chrome110","chrome120","chrome124"等:模拟特定版本的Chrome。
  • "firefox110","firefox120"等:模拟特定版本的Firefox。
  • "safari15_5"等:模拟Safari。
  • "edge99","edge110"等:模拟Edge。

3.3 会话(Session)与高级配置

requests.Session()一样,curl_cffi.requests.Session()可以用于保持Cookie、连接池等,提升效率。

from curl_cffi import requests import time # 创建一个模拟Chrome 124的会话 session = requests.Session(impersonate="chrome124") # 可以为会话设置默认请求头,更逼真 headers = { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Sec-Ch-Ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"', # 注意这个头,现代浏览器都有 "Sec-Ch-Ua-Mobile": "?0", "Sec-Ch-Ua-Platform": '"Windows"', "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Sec-Fetch-Site": "none", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-User": "?1", "Sec-Fetch-Dest": "document", } session.headers.update(headers) # 使用会话发起请求 url = "https://httpbin.org/cookies/set/sessionid/abc123" resp = session.get(url) print(f"第一次请求状态码: {resp.status_code}") # 会话会自动管理cookies,第二次请求会带上cookie url2 = "https://httpbin.org/cookies" resp2 = session.get(url2) print(f"第二次请求状态码: {resp2.status_code}") print(f"服务器看到的Cookies: {resp2.json()}") # 可以像普通requests一样使用代理 proxies = { "http": "http://your-proxy:port", "https": "http://your-proxy:port", } # resp3 = session.get("https://httpbin.org/ip", proxies=proxies)

注意事项Sec-开头的请求头(如Sec-Ch-Ua)是客户端提示(Client Hints),是现代浏览器用于向服务器传递设备、浏览器信息的新标准。在模拟浏览器时加上它们,能使请求看起来更真实。但请注意,这些头通常只在HTTPS请求中发送,且服务器必须通过Accept-CH头明确表示接受,爬虫中不是必须,但加上能增加隐蔽性。

4. 实战:攻克一个具有TLS检测的网站

让我们模拟一个完整的实战场景。假设目标网站https://target-site.com/data提供了我们需要的公开数据,但它使用了Cloudflare或类似服务商的TLS指纹检测。

4.1 侦察与分析

首先,永远不要一上来就写爬虫。用你最熟悉的浏览器(比如Chrome)手动访问目标页面。

  1. 打开开发者工具(F12),切换到Network(网络)标签页。
  2. 刷新页面,记录下第一个文档请求(通常是document类型)。点击这个请求,查看其Headers(标头)
  3. 重点关注Request Headers(请求头),特别是User-AgentAccept-*系列,Sec-*系列,Referer等。将这些头完整地复制下来,作为我们爬虫的模板。
  4. 查看Response Headers(响应头), 注意是否有Set-Cookie, 以及像Cf-Ray这样的Cloudflare特有头(如果存在,说明可能用了Cloudflare)。
  5. 关键一步:在开发者工具的Network标签页,找到这个请求,右键选择Copy -> Copy as cURL。这将得到一个完整的cURL命令,它包含了浏览器发起该请求的所有信息,包括Cookie、头、TLS指纹(因为它是从真实浏览器会话中复制的)。这个命令是我们验证curl_cffi配置的黄金标准。

4.2 构建爬虫脚本

基于侦察结果,我们构建爬虫脚本。

from curl_cffi import requests import json import logging # 设置日志,方便调试 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def scrape_target_site(): """ 模拟Chrome 124浏览器爬取 target-site.com 的数据 """ # 1. 创建模拟会话 # 根据侦察,目标网站最新浏览器访问记录是Chrome 124 session = requests.Session(impersonate="chrome124") # 2. 配置请求头(从浏览器开发者工具中复制并微调) headers = { "authority": "target-site.com", "method": "GET", "path": "/data", "scheme": "https", "accept": "application/json, text/plain, */*", # 根据实际API返回类型调整 "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", "cache-control": "no-cache", "pragma": "no-cache", "referer": "https://target-site.com/", # 重要!来源页 "sec-ch-ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"Windows"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", # 如果侦察发现还有其他自定义头,也一并加上 # "x-requested-with": "XMLHttpRequest", } session.headers.update(headers) # 3. 设置Cookies(如果需要) # 如果首次访问需要先获取Cookie,可以先访问首页 # home_resp = session.get("https://target-site.com/") # logger.info(f"首页访问状态: {home_resp.status_code}") # 或者手动设置已知的必要Cookie(从浏览器复制) # session.cookies.update({"session_id": "your_session_id_here"}) # 4. 发起目标请求 target_url = "https://target-site.com/data" # 可能还需要GET参数 params = { "page": 1, "size": 20, "category": "news", } try: logger.info(f"开始请求: {target_url}") # 超时时间设置长一些,因为TLS握手和指纹模拟可能需要额外时间 response = session.get(target_url, params=params, timeout=30) logger.info(f"响应状态码: {response.status_code}") logger.info(f"响应头: {dict(response.headers)}") # 检查是否成功 if response.status_code == 200: # 假设返回的是JSON data = response.json() logger.info(f"成功获取数据,条目数: {len(data.get('items', []))}") # 处理数据... # process_data(data) return data elif response.status_code in [403, 429]: logger.error(f"请求被拒绝或限流。状态码: {response.status_code}") logger.error(f"响应体: {response.text[:500]}") # 打印前500字符看错误信息 # 可能是IP问题,或者指纹模拟仍有破绽,需要进一步分析 else: logger.error(f"请求失败,状态码: {response.status_code}") logger.error(f"响应体: {response.text[:500]}") except requests.exceptions.Timeout: logger.error("请求超时,可能是网络或代理问题。") except requests.exceptions.RequestException as e: logger.error(f"请求发生异常: {e}") except json.JSONDecodeError: logger.error("响应不是有效的JSON格式。") logger.info(f"原始响应文本: {response.text[:1000]}") return None if __name__ == "__main__": result = scrape_target_site() if result: print("数据爬取成功!") # 这里可以保存数据,例如: # with open('data.json', 'w', encoding='utf-8') as f: # json.dump(result, f, ensure_ascii=False, indent=2)

4.3 验证与调试

如果脚本运行后返回了403等错误,不要灰心。按以下步骤排查:

  1. 对比cURL命令:将之前从浏览器复制的cURL命令在终端运行。如果cURL命令能成功获取数据,而你的脚本不能,说明问题不在TLS指纹,而在请求头、Cookie或参数上。仔细对比你的headers和cURL命令中的-H参数。
  2. 检查Cookie动态性:有些网站的Cookie是动态生成的,且有有效期。你的脚本可能需要先访问一个登录页或首页来获取初始Cookie,然后再用这个会话去请求数据API。
  3. 查看响应内容:即使返回403,服务器也可能在响应体中给出提示信息,如“Blocked by WAF”或“Access Denied: Invalid Client”。这些信息是宝贵的调试线索。
  4. 尝试不同浏览器指纹:如果目标网站可能识别并屏蔽了“过于完美”的Chrome 124指纹(虽然少见),可以尝试降级模拟impersonate="chrome110"或模拟Firefoximpersonate="firefox120"
  5. 使用代理池:如果IP被封锁,再完美的指纹也无用。确保你使用了稳定、干净的代理IP。curl_cffi的Session完美支持proxies参数。

实操心得:在实战中,我遇到过一个网站,使用chrome124指纹一直返回一个奇怪的JavaScript挑战页面。后来我将指纹切换到safari15_5,竟然一次性通过了。推测可能是该网站的反爬规则对最新版本的Chrome指纹有特殊的“照顾”或检测逻辑。因此,准备多个浏览器版本的指纹进行轮换或降级使用,是一个有效的备用策略

5. 性能、异步与最佳实践

curl_cffi在功能强大的同时,也需要我们关注其使用方式,以兼顾效率和稳定性。

5.1 同步与异步

curl_cffi提供了异步支持,这对于需要高并发的爬虫场景至关重要。

import asyncio from curl_cffi.requests import AsyncSession async def async_scraper(): urls = [ "https://httpbin.org/headers", "https://httpbin.org/ip", "https://httpbin.org/user-agent", ] # 创建异步会话 async with AsyncSession(impersonate="chrome124") as s: tasks = [] for url in urls: task = asyncio.create_task(s.get(url)) tasks.append(task) # 并发执行所有请求 responses = await asyncio.gather(*tasks, return_exceptions=True) for i, resp in enumerate(responses): if isinstance(resp, Exception): print(f"请求 {urls[i]} 失败: {resp}") else: print(f"请求 {urls[i]} 成功,状态码: {resp.status_code}") # 运行异步函数 asyncio.run(async_scraper())

使用AsyncSession可以极大地提升I/O密集型爬虫的效率。但请注意,并发数并非越高越好,需要根据目标网站的承受能力和你的代理IP质量来调整。

5.2 连接池与超时设置

Session对象会自动管理连接池。但对于长时间运行或高并发的爬虫,合理配置超时和重试策略是必要的。

from curl_cffi import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session = requests.Session(impersonate="chrome124") # 配置重试策略 retry_strategy = Retry( total=3, # 总重试次数 backoff_factor=1, # 退避因子,等待时间 = backoff_factor * (2^(重试次数-1)) 秒 status_forcelist=[429, 500, 502, 503, 504], # 遇到这些状态码才重试 allowed_methods=["GET", "POST"] # 只对GET和POST方法重试 ) # 创建适配器并挂载到会话 adapter = HTTPAdapter(max_retries=retry_strategy, pool_connections=10, pool_maxsize=30) session.mount("https://", adapter) session.mount("http://", adapter) # 设置全局超时(单位:秒) timeout_config = (10, 30) # (连接超时, 读取超时) # 在每次请求时传入 timeout=timeout_config

5.3 指纹更新与版本选择

curl_cffi库的浏览器指纹数据是内置在库中的。当Chrome发布新版本(如Chrome 125)后,旧的chrome124指纹理论上仍然有效,因为互联网上存在大量不同版本的浏览器。但是,为了保持最佳的隐蔽性,建议关注curl_cffi项目的更新,它会随着时间推移添加对新版本浏览器指纹的支持。

在代码中,impersonate参数的选择策略可以是:

  • 固定最新稳定版:如chrome124。优点是特征新,符合大多数用户环境。
  • 随机选择:从一个预定义的列表["chrome120", "chrome121", "chrome122", "chrome123", "chrome124"]中随机选择,模拟浏览器版本的多样性。
  • 根据目标网站调整:有些老旧的网站,其反爬规则库可能对老版本浏览器更宽松。

6. 常见问题与排查技巧实录

在实际使用curl_cffi的过程中,我踩过不少坑,也总结了一些排查问题的经验。

6.1 问题一:安装失败或导入错误

  • 症状pip install curl-cffi失败,或安装后导入from curl_cffi import requests时报错,提示找不到libcurl或版本不对。
  • 排查
    1. 确认系统已安装libcurl,且版本>=7.88.0。在Linux上,可能需要安装libcurl4-openssl-devlibcurl4等开发包。
    2. 在Windows上,如果通过官方pip安装失败,可以尝试从 Christoph Gohlke的非官方Windows二进制包 下载对应Python版本和系统架构的.whl文件进行安装。
    3. 在macOS上,确保通过Homebrew安装了curl:brew install curl,并注意Python可能链接的是系统自带的旧版curl,需要调整环境变量。

6.2 问题二:请求成功但返回异常内容(如验证页面)

  • 症状:状态码是200,但返回的不是预期的JSON或HTML,而是一个包含JavaScript挑战、重定向或人机验证的页面。
  • 排查
    1. 检查请求头完整性:这是最常见的原因。用浏览器访问,通过开发者工具仔细对比每一个请求头。特别注意RefererOriginSec-Fetch-*系列头部以及任何自定义头部(如X-Requested-With,X-CSRF-Token)。缺失或错误都可能触发反爬。
    2. 检查Cookie流程:很多网站采用“先访问首页获取初始Cookie,再携带Cookie访问API”的模式。确保你的爬虫模拟了这个流程。使用session对象会自动管理Cookie。
    3. 检查请求参数:GET参数或POST的Form Data/Body是否完全正确?时间戳、签名等动态参数是否成功生成?
    4. 尝试不同指纹:如之前所述,换一个浏览器版本模拟试试。
    5. 分析响应体:仔细阅读返回的HTML或JS代码,里面可能包含下一步该怎么做(例如一个需要提交的表单,或一个需要计算的token)。

6.3 问题三:遭遇更高级别的检测(如JA3指纹、HTTP/2指纹)

  • 症状:使用了curl_cffi,头、Cookie、参数都正确,但依然被阻断。
  • 分析与对策
    • JA3指纹:JA3是一种基于TLSClient Hello报文特定字段生成的MD5哈希。curl_cffi模拟了浏览器指纹,理论上JA3哈希值也会与对应浏览器一致。你可以使用在线JA3测试工具,分别用你的爬虫脚本和真实浏览器访问测试站点,对比JA3值是否相同。如果不同,说明curl_cffi的模拟可能在该网站环境下有细微差异,或者网站检测了其他特征。
    • HTTP/2指纹:如果网站使用HTTP/2协议,客户端发送的SETTINGS帧参数和顺序也是一种指纹。curl_cffi目前主要专注于TLS指纹,对HTTP/2指纹的模拟可能不完整。一个解决办法是强制使用HTTP/1.1协议。这可以通过在请求头中设置"Upgrade": "http/1.1"或配置libcurl参数实现(curl_cffi底层支持)。
    • 综合指纹:最棘手的情况是反爬系统结合了TLS指纹、TCP窗口大小、TTL、IP信誉等多种因素。此时,仅靠客户端模拟可能不够。解决方案通常是:
      1. 使用高质量住宅代理:让请求从一个看起来像真实家庭宽带的IP地址发出。
      2. 降低请求频率:模拟人类浏览行为,加入随机延时。
      3. 使用更底层的工具:如结合curl_cffi的底层API和pycurl进行更精细的TCP/IP栈参数调优(这属于高阶技巧,难度较大)。

6.4 一份简易排查清单

当你遇到问题时,可以按此清单逐步检查:

步骤检查项工具/方法
1. 基础连通目标网址在浏览器中是否能正常打开?手动浏览器访问
2. 请求复制用浏览器开发者工具“Copy as cURL”,在终端运行能否成功?终端执行cURL命令
3. 指纹模拟你的脚本是否正确设置了impersonate参数?检查代码impersonate="chrome124"
4. 请求头对比你的脚本请求头与浏览器cURL命令中的头是否完全一致?(顺序不重要)对比headers字典与cURL的-H参数
5. Cookie流程是否需要先访问某个页面获取初始Cookie?分析浏览器网络请求顺序
6. 参数验证GET/POST参数是否正确?动态参数(如_t)是否有效?对比浏览器请求的Payload
7. 代理与IP是否因IP被封锁而失败?换一个代理IP试试。使用proxies参数测试不同IP
8. 协议版本尝试强制使用HTTP/1.1。在请求头中添加"Upgrade": "http/1.1"
9. 指纹降级换一个更旧或不同的浏览器指纹试试。修改impersonate"chrome110","firefox115"

最后,记住爬虫伦理。curl_cffi是一个强大的技术工具,它帮助我们绕过了一些过于宽泛、误伤友好的技术封锁。但请务必将其用于获取公开的、允许爬取的数据,遵守网站的robots.txt协议,控制请求频率,避免对目标服务器造成过大压力。技术是中立的,但使用技术的人需要承担责任。