# curl_cffi 与 requests：解决了什么问题，无法解决什么问题

将 requests 替换为 curl_cffi 可以将许多 403 转换为 200。但对于被封的 IP 则无济于事。以下是两者之间的界限，以及一个十五分钟的测试，告诉您问题出在哪一边。

curl_cffi 与 requests 的问题通常在事件中期出现：一个运行了几个月的爬虫在第一次调用时开始返回 403，Reddit 上有人建议更换客户端，结果奏效。这是一个有真实解释的真实效果——但这种说法（“只需使用 curl_cffi”）掩盖了正在发生的事情以及它停止帮助的地方。requests 并不慢，也不是写得不好。在抓取上下文中，它只有一个缺点，这是一个大问题，与您输入的 API 无关。这是两个客户端真正不同的地方，切换时会发生什么变化，以及无论您选择哪个库，冒充永远无法解决的一个问题。

## 唯一重要的区别

两个库发送相同的头。区别在于下一层，在单个 HTTP 字节移动之前打开连接的 TLS 握手。requests 基于 urllib3 和 OpenSSL，广告的密码列表、扩展集和排序属于 Python，而不是其他任何东西。curl_cffi 是一个绑定到 curl 的修补分支的库，它逐字节地重现浏览器的 ClientHello 字节及其 HTTP/2 SETTINGS 帧——因此其 JA3、JA3N 和 Akamai 哈希匹配真实的 Chrome，而不是脚本库。反机器人供应商保留这些签名的数据库；Chrome User-Agent 头与 Python 握手之间的不匹配是一个您无法解释的矛盾。我们在 [JA3 和 JA4 指纹识别](https://quantumproxies.io/blog/tls-ja3-fingerprinting-scraping) 中解开了这些机制，这同样的效果解释了为什么 [curl 在相同的 URL 上获得 403 而您的浏览器获得 200](https://quantumproxies.io/blog/curl-403-but-browser-works)。

比较中的其他一切都来自实现。因为 curl_cffi 包装了 libcurl，它继承了 HTTP/2、HTTP/3、websockets 和 asyncio，而这些 requests 从未支持过。因为 requests 是纯 Python，它可以安装在任何地方，并且拥有十年的生态系统支持。两者的陈述同时成立，哪个占主导地位完全取决于您的目标。

## 您可以在五分钟内运行的可重复测试

不要盲信任何人的通过率表，包括我们的。指纹差异是可以直接观察到的：将两个客户端指向一个 TLS 回显端点并比较它们报告回来的哈希值。如果两行匹配，您的构建没有冒充任何东西。

```python
# pip install requests curl_cffi
import requests
import curl_cffi

URL = "https://tls.browserleaks.com/json"

a = requests.get(URL, timeout=30).json()
b = curl_cffi.get(URL, impersonate="chrome", timeout=30).json()

print("requests   ja3n:", a["ja3n_hash"], "| akamai:", a.get("akamai_hash", "-"))
print("curl_cffi  ja3n:", b["ja3n_hash"], "| akamai:", b.get("akamai_hash", "-"))

# Two different ja3n hashes = impersonation is working. The curl_cffi README
# documents aa56c057ad164ec4fdcb7a5a283be9fc as a Chrome-matched ja3n value.
# The akamai (HTTP/2) fingerprint is usually empty for requests: it is
# HTTP/1.1 only, so there is no SETTINGS frame to fingerprint in the first place.
```

自 v0.15 起，有一个相同检查的一行版本：`curl-cffi get tls.browserleaks.com/json --impersonate chrome`。在调试其他任何事情之前运行它——它将“我的冒充配置错误”与“我的冒充正常，但其他东西阻止了我”分开，这完全是两个不同的下午。

![requests 与 curl_cffi 在协议、并发、指纹和可移植性方面的并排对比：curl_cffi 在 HTTP/2、HTTP/3、asyncio 和匹配 Chrome 的 TLS 指纹上胜出，requests 在可移植性和生态上胜出，而两者都解决不了 IP 质量问题，因为那不是客户端的功能。](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785776553/blog/generated/qp-b10-007-diag1.png)
*requests 在可移植性和生态系统方面获胜；curl_cffi 在协议和指纹方面获胜。两者都没有在 IP 质量上获胜——这不是客户端功能。*

## curl_cffi 无法修复的：IP 声誉

这是更换库建议遗漏的部分。TLS 指纹回答了“这是什么软件？”的问题。它没有回答“这来自哪里？”——第二个问题通过对您的出口 IP 的单独查询来回答：哪个 ASN 拥有它，它是托管提供商还是消费者 ISP，它是否出现在滥用源中，在过去一小时内有多少其他会话从同一地址访问过该站点。从数据中心范围内的云 VM 到达的完美 Chrome 握手是一个显然安装在服务器机架中的 Chrome 浏览器。这并不比 python-requests 更有说服力。在某些情况下，它的说服力更低，因为矛盾更为明显。

项目自己的 FAQ 将 IP 质量放在其因素列表的首位，优先于请求速率和 JavaScript 指纹，以解释为什么仅靠冒充可能不够。这种排序不是偶然的：声誉是防御者评估的最便宜信号，也是攻击者最难伪造的信号，因为与头或密码列表不同，您无法在本地生成它。如果您的基于 requests 的爬虫已经通过数据中心池运行并被阻止，使用相同池切换到 curl_cffi 会改变两个失败检查中的一个。您将在软目标上看到部分改进，而在硬目标上则完全没有改进——这正是人们报告的困惑结果。

该轴的修复是地址质量，而不是代码：来自真实消费者 ISP 分配的 [住宅 IP](https://quantumproxies.io/residential-proxies)，这就是 90M+ 地址跨 200+ 国家/地区为您带来的。如果您想在更改任何内容之前检查当前出口的外观，我们的免费 [IP 质量检查器](https://quantumproxies.io/ip-checker) 会报告目标将看到的 ASN 和分类。

## 告诉您哪个轴出问题的 2x2

与其猜测，不如针对您的真实目标独立测试两个变量。四个请求，四行输出，结果命名您的问题：

```python
import requests
import curl_cffi

TARGET = "https://your-target.example/api/items"
DC  = "http://USER:PASS@your-datacenter-gateway:PORT"
RES = "http://USER:PASS@residentialpremium.quantumproxies.io:8080"

def probe(label, fn):
    try:
        print(f"{label:26} -> {fn().status_code}")
    except Exception as e:
        print(f"{label:26} -> {type(e).__name__}")

probe("requests  + datacenter",
      lambda: requests.get(TARGET, proxies={"https": DC}, timeout=30))
probe("curl_cffi + datacenter",
      lambda: curl_cffi.get(TARGET, proxy=DC, impersonate="chrome", timeout=30))
probe("requests  + residential",
      lambda: requests.get(TARGET, proxies={"https": RES}, timeout=30))
probe("curl_cffi + residential",
      lambda: curl_cffi.get(TARGET, proxy=RES, impersonate="chrome", timeout=30))
```

像真值表一样读取四个结果：

- **只有数据中心行失败**——这是 IP 声誉。客户端无关紧要；购买更好的出口。
- **只有 requests 行失败**——这是 TLS 指纹。切换客户端并保留现有池。
- **只有最后一行通过**——两个检查都有效。您需要同时进行冒充和清洁 IP；这是在严肃目标上的常见情况。
- **四个都失败**——您已经超出了任何 HTTP 客户端可以做的范围。这意味着 JavaScript 挑战、您未生成的令牌或帐户级别的阻止。使用真实浏览器或托管的 [Scraper API](https://quantumproxies.io/scraper-api)。
- **四个都通过**——您从未遇到过指纹问题。不要添加依赖项。

而不是一次运行几十次。两个阻止层都是概率性的，单个 200 几乎没有告诉您任何信息。

[使用真实家庭 IP 测试住宅行](https://quantumproxies.io/residential-proxies)

![同一个匹配浏览器的 curl_cffi TLS 握手从两个出口发出：来自数据中心 IP 时因信誉被拒，来自住宅 IP 时顺利通过。卡片列出两道相互独立的关卡，TLS 指纹和 IP 信誉，并说明为什么只修复其中一道仍会被拦截。](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785776554/blog/generated/qp-b10-007-diag2.png)
*冒充和 IP 声誉是独立的门槛。修复一个而留下另一个是“我切换到 curl_cffi 但没有变化”如此常见的报告的原因。*

## 当额外的依赖不值得时

坦诚比重写更便宜。当以下情况时，请继续使用 requests：

- **您正在调用您有权调用的 API。** 使用您自己的密钥的文档化端点不会对您进行指纹识别。在那里添加冒充是迷信行为。
- **您的部署目标很棘手。** curl_cffi 提供编译好的轮子，并且自 v0.14 起需要 Python 3.10 或更新版本。requests 几乎可以在任何地方运行，包括旧镜像和受限的嵌入式环境。
- **您依赖于 requests 生态系统。** 自定义适配器、requests-cache、requests-oauthlib 等都挂钩了 curl_cffi 故意不公开的传输层。
- **您需要开箱即用的状态码重试。** urllib3 的 `Retry` 配合 `status_forcelist` 在 429 和 503 上重试；curl_cffi 的 `retry` 参数仅在传输异常时重新运行。
- **您的阻止是行为性的。** 速率限制、帐户禁令和每会话配额根本不关心握手。

而且大多数人忽略了一条中间路径：您不必放弃 requests 来获得握手。维护者指向 curl-adapter，它将 curl_cffi 安装为 requests 传输适配器，而 PyPI 上的 httpx-curl-cffi 则为 httpx 做同样的事情。您保留现有的代码和生态系统，只有线上字节发生变化。

## 迁移需要先了解的陷阱

API 足够接近，大多数脚本在更改导入后即可运行，但兼容性页面列出了实际差异，在进行大规模移植之前阅读它们是值得的。重定向响应体不会保留在 `Response.history` 中。具有空域的 Cookie 在重定向时可能会丢失。流响应对象无法被序列化，尽管普通响应可以。文件 API 略有不同。而且根本没有传输或适配器，因为库故意与 libcurl-impersonate 焊接在一起。代理配置也有一个小的不同之处，这让人们感到困惑：

```python
# requests: dict, and retries mounted on an adapter
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

s = requests.Session()
s.mount("https://", HTTPAdapter(max_retries=Retry(total=4, status_forcelist=[429, 503])))
r = s.get(url, proxies={"https": "http://USER:PASS@residentialpremium.quantumproxies.io:8080"}, timeout=30)

# curl_cffi: single proxy string preferred, retries are a session parameter
from curl_cffi import Session
from curl_cffi.requests import RetryStrategy

s = Session(
    impersonate="chrome",
    proxy="http://USER:PASS@residentialpremium.quantumproxies.io:8080",
    retry=RetryStrategy(count=4, delay=1.0, backoff="exponential"),
    timeout=30,
)
r = s.get(url)  # note: retry fires on transport errors, not on 429/503
```

完整的代理界面——字典键、`proxy_auth`、每请求轮换、异步和生成无用的 WRONG_VERSION_NUMBER 错误的 `https://` 前缀——在我们的 [curl_cffi 代理指南](https://quantumproxies.io/blog/curl-cffi-proxy-guide) 中逐步介绍。如果您选择不更换，另一方的等效参考是我们的 [Python Requests 代理指南](https://quantumproxies.io/blog/python-requests-proxy-guide)。

## 常见问题

### curl_cffi 比 requests 快吗？

是的，该项目的基准测试将其与 aiohttp 和 pycurl 相提并论，而不是 requests。增益来自 libcurl 在 C 中完成的工作加上 HTTP/2 多路复用，而不是聪明的 Python。对于少量顺序调用，差异是不可见的；在高并发情况下，尤其是异步情况下，差异是显著的。

### curl_cffi 使用安全吗？

它是 MIT 许可的，广泛部署，并提供预编译的轮子，因此没有构建步骤需要审核。有一个值得注意的警告：v0.15.0 的建议涵盖基于重定向的 SSRF。如果您获取其他人提供的 URL，请设置 `allow_redirects="safe"` 或禁用重定向。冒充浏览器是一种技术措施，而不是忽视站点条款的许可。

### curl_cffi 能绕过 Cloudflare 吗？

它去除了 TLS 和 HTTP/2 指纹特征，从而清除了基本保护级别。它无法执行 JavaScript 挑战，解决 Turnstile 或修复被标记的出口 IP。维护者在他们的 FAQ 中也这么说，并建议为更高层级使用更好的代理池加上浏览器自动化。

### curl_cffi 与 httpx 或 tls_client——我应该使用哪个？

httpx 为您提供 HTTP/2 和异步，但没有指纹冒充，因此在隐蔽性方面介于 requests 和 curl_cffi 之间。tls_client 也伪造 TLS 配置文件，基准测试类似；curl_cffi 拥有更大的社区，并增加了 HTTP/3 和 websockets。如果 httpx 已经在您的堆栈中，httpx-curl-cffi 传输可以让您在不重写的情况下实现冒充。

简而言之：当您的目标读取握手时切换到 curl_cffi，当不读取时保持在 requests 上，并且永远不要期望任何一种选择能够清洗数据中心 IP。客户端在一个轴上不同，代理在另一个轴上不同，阻止的爬虫几乎总是关于两者的故事。运行四个探测，读取真值表，并修复数据指向的轴，而不是互联网大喊的那个。

[修复冒充无法触及的轴](https://quantumproxies.io/residential-proxies)
