ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

curl_cffi 与 requests 的兼容性差异指南:文件上传、流式响应、代理与传输层适配

2026/10/8 14:10:30 拓冰建站 浏览量
curl_cffi 与 requests 的兼容性差异指南:文件上传、流式响应、代理与传输层适配 网络网页爬虫后端【免费下载链接】curl_cffiPython binding for curl-impersonate fork via cffi. A http client that can impersonate browser tls/ja3/http2 fingerprints.项目地址https://gitcode.com/gh_mirrors/cu/curl_cffi点击查看免费下载本文是一份面向从requests迁移到curl_cffi的开发者指南完整梳理了两者在 API 层面的已知不兼容点文件上传接口的差异、重定向时的空域名 Cookie 丢失、流式响应对象的可序列化限制、代理参数的取舍以及传输层Transport/Adapter的替换约束。读完本文你将能快速识别迁移过程中的潜在坑位并掌握用curl_cffi作为requests适配器、在保留 requests 完整功能的同时获得浏览器指纹模拟能力的落地方案。兼容性总览尽力模仿但存在已知边界curl_cffi的目标是尽可能模仿requests的 API这一点在项目入口 curl_cffi/requests/init.py 中体现得很直观它导出了与requests同名的Session、AsyncSession、request、get、post、put、delete等函数以及Request、Response、Cookies、Headers等核心对象同步/异步 API 一应俱全还额外导出了BrowserType、WebSocket、RetryStrategy、CacheBackend等 requests 没有的扩展能力。但官方文档 docs/vs-requests.rst 明确指出由于底层实现差异有些功能不容易实现因而被省略或行为不同并给出了一个已知不兼容点清单。这是迁移前必须阅读的第一份对照表差异点requests 行为curl_cffi 行为文件上传files参数直接传files{name: file_obj}不再支持files改用multipart基于 libcurl 的CurlMime更不易出错重定向时的空域名 Cookie可正常携带可能丢失存在已知 issue 追踪流式响应序列化流式/非流式均可 pickle流式响应对象不可 pickle非流式响应可以代理配置推荐proxies字典支持proxies字典但更推荐proxy...单字符串除非 http/https 确需不同代理传输层扩展支持 transports/adapters 挂载与 libcurl-impersonate 深度耦合无法更换网络库或挂载适配器下面逐条展开说明并结合源码给出可验证的实现证据。文件上传files换成multipart更严谨也更省心requests的文件上传写法是files{field: (filename, fileobj, content_type)}curl_cffi则明确不支持files参数。在 curl_cffi/requests/utils.py 的请求参数组装逻辑中一旦检测到files非空会直接抛出提示files is not supported, usemultipart. See examples here: ...也就是说传了files会得到一个明确的报错而不是静默忽略这正是文档所说的slightly different, but more error-proof略有不同但更不易出错。基于 CurlMime 的 multipart 用法multipart参数接收一个CurlMime对象类型注解见 curl_cffi/requests/session.py 的multipart: Optional[CurlMime]。推荐用CurlMime.from_list批量声明字段测试用例 tests/unittest/test_upload.py 给出了标准写法from curl_cffi import CurlMime, requests multipart CurlMime.from_list( [ { name: image, # 表单字段名 content_type: image/jpg, # 文件 MIME 类型 filename: scrapfly.png, # 上传后显示的文件名 local_path: assets/scrapfly.png, # 本地文件路径 }, ] ) r requests.post(https://httpbin.org/post, multipartmultipart) print(r.json()) multipart.close() # 记得释放底层 mime 结构与requests的元组语法相比CurlMime的字段用显式字典键声明每个字段的含义一目了然。文本字段与文件字段可以混用data{foo: bar}与multipart同时传入见 tests/unittest/test_upload.py也可用multipart.addpart(name..., databbar)逐字段追加tests/unittest/test_upload.py。从底层看multipart最终通过CurlOpt.MIMEPOST交给 libcurl 处理curl_cffi/requests/utils.py上传过程中由 curl 原生负责 multipart 的边界生成与编码这比 requests 在 Python 层拼接表单更可靠也天然规避了文件名、Content-Type 等元数据的转义问题。注意multipart使用后需要调用multipart.close()释放底层资源每次上传建议新建CurlMime实例。若同时满足multipart is None且传入了content/data/json请求体才走普通 body 路径curl_cffi/requests/utils.py两者互斥由源码保证。重定向与空域名 Cookie一个已知边界问题文档列出的第二个差异点是empty-domains cookies may lost during redirects重定向过程中空域名的 Cookie 可能丢失并在 issue #55 中持续追踪。所谓空域名 Cookie指的是Domain属性为空或未显式指定域的 Cookie——这类 Cookie 通常只在设置它的主机上生效域匹配逻辑相对特殊。在重定向链路上curl_cffi默认由 libcurl 处理跳转allow_redirects默认开启max_redirects默认 30相关参数见 curl_cffi/requests/init.py 的文档注释跳转时会把当前响应中的 Set-Cookie 回写进会话 Cookie 容器。对于域匹配比较特殊的空域名 Cookie跨主机跳转时可能不会被带到下一跳请求中。迁移建议若目标站点的会话依赖这类 Cookie且存在多级重定向建议在跳转前后显式检查response.cookies与session.cookies的内容必要时可手动把关键 Cookie 写进请求头headers{Cookie: ...}或会话级cookies参数绕开域匹配逻辑关注上游 issue #55 的修复进展升级到包含修复的版本。这一差异属于 libcurl 域匹配语义与 requests 的 CookieJar 语义之间的细微差别属于低频边界场景大多数常规站点不受影响。流式响应的可序列化限制pickle 的明确约束第三个差异点在 curl_cffi/requests/models.py 有完整的源码实现Response类自定义了__getstate__/__setstate__来支持 pickle。其核心逻辑是如果响应处于流式状态内部queue、stream_task、astream_task、quit_now任一非空__getstate__会直接抛出TypeError错误信息原文为Streaming responses cannot be pickled; make the request without streamTrue before pickling the response.也就是说用streamTrue发起的请求其响应对象无法被 pickle无法跨进程/线程序列化传递而非流式响应在序列化时会把curl句柄、流队列、后台任务等与底层 libcurl 强绑定的属性剔除见state.pop(attribute, None)的清理逻辑反序列化后这些属性被重置为None从而得到一个干净的、可正常访问.content/.json()的普通响应对象curl_cffi/requests/models.py。迁移建议需要 pickle如存入 multiprocessing 队列、缓存、分布式任务的响应不要在请求时开启streamTrue如果既要流式接收又要保存可以先流式读完iter_content再把数据整体构造为新的非流式对象保存从源码结构看流式状态依赖queue与后台stream_taskcurl_cffi/requests/models.py这些运行时状态天然无法跨进程还原这是该限制的根因。代理配置proxies字典兼容但更推荐proxy在代理参数上curl_cffi同时支持两种写法# 方式一requests 风格dict 形式兼容但仅当确有需要 proxies {http: http://127.0.0.1:8080, https: http://127.0.0.1:8080} requests.get(url, proxiesproxies) # 方式二curl_cffi 推荐单字符串形式 requests.get(url, proxyhttp://127.0.0.1:8080)文档给出的取舍建议很明确proxies字典虽然被支持但除非 http 和 https 确实要使用不同的代理地址否则优先使用proxy...。原因在于底层 libcurl 的代理语义本身以单一代理为主proxies字典需要在 Python 层按协议拆分配置而proxy单字符串直接映射到 curl 的代理选项语义更直接、开销更小。值得注意的是curl_cffi的proxies字典还支持all键ProxySpec类型见 curl_cffi/requests/session.py含all/http/https/ws/wss五种键并支持all://hostname形式的主机级代理匹配。测试用例 tests/unittest/test_requests.py 覆盖了三种典型场景proxies{all: proxy_url}所有请求走同一代理proxies{http: proxy_url}仅 http 走代理proxies{fall://{host}: proxy_url}按主机名定向指定代理。此外还有proxy_auth参数代理的 basic auth(username, password)元组和会话级trust_env默认True控制是否读取http_proxy/https_proxy环境变量见 curl_cffi/requests/session.py。trust_envFalse可完全禁用环境变量代理这在容器或 CI 环境中屏蔽系统代理时很实用。迁移建议日常单代理场景直接写proxyhttp://user:passhost:port只有分流需求不同协议/不同主机走不同代理时才用proxies字典。传输层与适配器与 libcurl 深度耦合可用 curl-adapter 桥接这是文档强调的最本质差异。curl_cffi的整个请求链路都建立在libcurl-impersonatecurl 的浏览器指纹模拟分支之上因此Unlikerequestsorhttpx, there is no way to use a different networking library or mount different adapters.也就是说requests 的Transport/Adapter扩展机制如为某协议挂载自定义 HTTPAdapter在curl_cffi中不存在——你无法把网络栈换成别的库也无法在会话中挂载自定义适配器。从源码看每个Session内部持有的是一个Curl实例curl句柄由 curl_cffi/curl.py 封装所有请求最终都落到set_curl_options对 curl 选项的批量设置curl_cffi/requests/session.py网络层是唯一且固定的。桥接方案把 curl_cffi 当作 requests 的 adapter如果你确实需要 requests 的完整功能自定义 adapter、复杂的重试钩子、完整的 urllib3 生态等官方文档给出的方案是通过社区项目 curl-adapter 把 curl_cffi 作为 requests 的 adapter 使用。这样requests 负责高层 APISession、Adapter 分发、CookieJar、重试等curl_cffi 负责底层网络与 TLS/HTTP2 指纹模拟两者各取所长——既保住了 requests 的完整功能又拿到了浏览器指纹模拟能力。这种requests 外壳 curl_cffi 内核的组合适合以下场景已有大量基于 requests Session/Adapter 的既有代码不想整体重写需要 requests 的插件生态如各类自定义 HTTPAdapter、重试与限速中间件只需要在特定域名或特定请求上启用浏览器指纹模拟其他请求维持原网络栈。作为对比原生curl_cffi.requests则更适合全新项目或希望同时获得异步能力AsyncSession、WebSocket、缓存后端等一体化扩展的场景。快速对照从 requests 迁移的最小改动清单综合以上差异把一段典型 requests 代码迁移到 curl_cffi 时按此清单逐项检查导入替换import requests→from curl_cffi import requestsAPI 高度对齐get/post/session等用法不变文件上传files→ 构建CurlMime传给multipart用后close()流式响应确认是否有 pickle 需求有则避免streamTrue代理单代理用proxy多代理分流才用proxies传输层不要试图挂载 requests 的 adapter/transport需要 requests 完整功能时改用 curl-adapter 桥接Cookie涉及重定向与空域名 Cookie 的场景额外验证会话 Cookie 是否完整扩展红利迁移后可顺手使用impersonate浏览器指纹模拟、AsyncSession、WebSocket、RetryStrategy、CacheBackend等 requests 不具备的能力这些在 curl_cffi/requests/init.py 的导出清单中都能找到。总的来说curl_cffi与 requests 的兼容性覆盖了绝大多数日常用法差异集中在文件上传、流式响应序列化、代理写法与传输层扩展这几处边界。理解并绕开这些差异点你就能在保留 requests 开发体验的同时获得 TLS/HTTP2 浏览器指纹级别的请求伪装能力。赞分享网络网页爬虫后端【免费下载链接】curl_cffiPython binding for curl-impersonate fork via cffi. A http client that can impersonate browser tls/ja3/http2 fingerprints.项目地址https://gitcode.com/gh_mirrors/cu/curl_cffi点击查看免费下载相关推荐PHPMailer 邮件发送库新手三步搞定 PHP 邮件开发PHPMailer 邮件发送库新手三步搞定 PHP 邮件开发 PHPMailer 是 PHP 生态里最经典的邮件发送类库帮你把 HTML 正文、附件、SMT后端通信从 Hugging Face 到 MLXLFM2.5-1.2B-Thinking-8bit 格式转换原理与实战从 Hugging Face 到 MLXLFM2.5 1.2B Thinking 8bit 格式转换原理与实战 LFM2.5 1.2B Thinking 8bDataherald流式响应实时代理步骤的流式传输Dataherald流式响应实时代理步骤的流式传输 引言为什么需要流式SQL生成 在传统的数据查询场景中用户提交自然语言问题后需要等待整个SQL生成过程后端人工智能大模型RAG微调上一篇Statsmodels 生存分析指南SurvfuncRight、survdiff 与 Cox 比例风险回归PHReg完整实战下一篇3步掌握碧蓝航线自动化Alas智能助手解放你的游戏时间创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考