本文最后更新于:2026年8月14日 下午
本文为工具使用整理,基于官方仓库与示例编写
文中代码均来自官方 examples/ 目录,可直接验证。License:Apache-2.0。
一、rnet 和 wreq 是什么关系?
先把最容易混淆的一点说清楚:
- rnet 是旧名,现在已改名为 wreq。 原仓库
0x676e67/rnet 已重定向到 0x676e67/wreq-python,新版所有示例都改成了 import wreq。
- PyPI 上两个包目前都在:旧包
rnet(v2.4.x)仍可安装使用,新包 wreq(v0.12.x)是现在主推的。
- 架构上:
wreq(Rust)是底层高性能 HTTP 客户端 → wreq-python(即原 rnet)是它的 Python 绑定(用 PyO3 + maturin 打包成 wheel)。
本文以新名 wreq 为主。如果你还在用旧包 rnet,把代码里的 import wreq 换成 import rnet、from wreq import X 换成 from rnet import X 即可,API 基本一致。
⚠️ 版本注意:新包 wreq 要求 Python ≥ 3.11;旧包 rnet 支持 Python ≥ 3.7。低版本环境暂时只能用 rnet。
二、它解决什么问题?
一句话:它是一个自带浏览器 TLS / HTTP2 指纹伪装的 HTTP 客户端,用来对抗基于指纹的反爬。
普通的 requests、httpx、aiohttp 用的是 Python 自己的 TLS 栈,其 JA3 / JA4 指纹和真实浏览器完全不同,稍强一点的风控(Cloudflare、Akamai 等)一看指纹就知道你不是浏览器。curl_cffi 是这个领域的常见方案,而 wreq 是更新的一个 Rust 实现,基准测试中号称优于 requests / httpx / aiohttp / curl_cffi(性能数据仅供参考,随环境变化)。
它的核心特性:
- 异步 + 同步 两种
Client
- TLS 通过 BoringSSL,可精细控制 TLS 扩展、密码套件、曲线,以及 HTTP/2 的 SETTINGS / 伪头顺序
- 100+ 浏览器设备模拟 profile(Chrome、Firefox、Safari、Okhttp 等,含 Android/iOS 平台)
- Body 支持 plain / JSON / urlencoded / multipart
- Cookie Store、重定向策略、旋转代理、连接池、流式传输、WebSocket 升级、自动解压
关于指纹的官方设计理念值得一提:它不主张用「一串 JA3 字符串」去模拟指纹,因为 TLS + HTTP/2 的真实指纹很复杂,字符串无法可靠还原;而是提供对 TLS / HTTP2 各项扩展和设置的细粒度控制,用内置的浏览器 profile 来精确还原真实浏览器行为。
三、安装
旧包(Python 3.7~3.10 环境):
预编译 wheel 覆盖 Linux(glibc/musl:x86_64、aarch64、armv7、i686)、macOS(x86_64、aarch64)、Windows(x86_64、i686、aarch64)、Android,一般无需本地编译。
四、快速开始
最小示例(异步 + Chrome 指纹模拟):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import asyncio from wreq import Client, Emulation
async def main(): client = Client(emulation=Emulation.Chrome149)
resp = await client.get("https://pingly.us.kg/api/all") print(await resp.text())
if __name__ == "__main__": asyncio.run(main())
|
如果懒得建 client,也有模块级快捷函数 wreq.get/post/request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import asyncio import wreq from wreq import Method
async def main(): resp: wreq.Response = await wreq.request(Method.GET, url="https://www.google.com/") print("Status Code:", resp.status) print("Version:", resp.version) print("Response URL:", resp.url) print("Headers:", resp.headers) print("Cookies:", resp.cookies) print("Content-Length:", resp.content_length) print("Remote Address:", resp.remote_addr) print("set-cookie:", resp.headers["set-cookie"])
for key, value in resp.headers: print(f"{key}: {value}")
if __name__ == "__main__": asyncio.run(main())
|
五、常用用法
1. 浏览器指纹模拟(Emulation)
这是 wreq 最核心的能力。最简单的用法是直接选一个 profile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from wreq import Client from wreq.emulation import Emulation, Profile, Platform
client = Client(emulation=Emulation.Firefox135)
resp = await client.get( "https://tls.peet.ws/api/all", emulation=Emulation( profile=Profile.Chrome134, platform=Platform.Android, ), default_headers=False, )
|
验证指纹是否生效,可以请求 https://tls.peet.ws/api/all 或 https://pingly.us.kg/api/all 这类回显 TLS/JA3 信息的服务,对比真实浏览器。
2. JSON / 表单 / 查询参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import wreq
resp = await wreq.post("https://httpbin.io/anything", json={"key": "value"}) print(await resp.json())
resp = await wreq.post( "https://httpbin.io/anything", form={"keyA": "valueA", "number": 789, "flag": False}, )
resp = await wreq.get( "https://httpbin.io/anything", query=[("key1", "value1"), ("number", 123)], )
|
3. multipart 文件上传
支持文本、字节、文件路径、异步字节流四种 Part:
1 2 3 4 5 6 7 8 9 10 11 12
| from pathlib import Path import wreq from wreq import Multipart, Part
resp = await wreq.post( "https://httpbin.io/anything", multipart=Multipart( Part(name="def", value="111", filename="def.txt", mime="text/plain"), Part(name="abc", value=b"000", filename="abc.txt", mime="text/plain"), Part(name="LICENSE", value=Path("LICENSE"), filename="LICENSE", mime="text/plain"), ), )
|
4. 代理(含旋转 / SOCKS5 / Unix socket)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import wreq from wreq import Client, Proxy
client = Client(proxies=[Proxy.http("socks5h://user:pass@127.0.0.1:6152")]) resp = await client.get("https://httpbin.io/anything")
resp = await wreq.get( "https://httpbin.io/anything", proxy=Proxy.all( url="http://127.0.0.1:6152", custom_http_headers={"user-agent": "wreq", "x-proxy": "wreq"}, ), )
resp = await wreq.get( "http://localhost/v1.41/containers/json", proxy=Proxy.unix("/var/run/docker.sock"), )
|
5. 认证
1 2
| resp = await wreq.get("https://httpbin.io/anything", auth="token")
|
(另有 examples/basic_auth.py、bearer_auth.py 对应 Basic / Bearer 认证。)
6. 流式响应
1 2 3 4 5 6 7 8 9
| import asyncio import wreq from wreq import Response
resp: Response = await wreq.get("https://httpbin.io/stream/20") async with resp: async with resp.stream() as streamer: async for chunk in streamer: print(chunk)
|
7. 自定义重定向策略
可以用 Python 回调精细控制每一次重定向:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from wreq import Client, redirect from wreq.redirect import Attempt, Action
def custom_policy(attempt: Attempt) -> Action: print(f"Redirect to: {attempt.next} (status: {attempt.status})") if "example.com" in attempt.next: return attempt.stop() if len(attempt.previous) > 5: return attempt.error("Too many redirects") return attempt.follow()
client = Client(redirect=redirect.Policy.custom(custom_policy)) resp = await client.get("http://httpbin.io/redirect/3")
|
HeaderMap 支持同一个 key 多值,适合处理 Accept、Set-Cookie 这类:
1 2 3 4 5 6 7
| from wreq.header import HeaderMap
headers = HeaderMap() headers.insert("Content-Type", "application/json") headers.insert("Accept", "application/json") headers.insert("Accept", "text/html") print(list(headers.get_all("Accept")))
|
9. WebSocket
1 2 3 4 5 6 7 8 9 10
| import wreq from wreq import Message, WebSocket
client = wreq.Client() ws: WebSocket = await client.websocket("wss://echo.websocket.org") async with ws: if ws.status == 101: await ws.send(Message.from_text("hello")) msg = await ws.recv() print("Received:", msg)
|
六、进阶:手工定制 TLS / HTTP2 指纹
如果内置 profile 不够用,可以直接手搓 TLS 与 HTTP/2 参数(下面是官方 emulation.py 里的高级配置节选,模拟 Twitter Android 客户端):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| from wreq import Client from wreq.tls import TlsOptions, TlsVersion, AlpnProtocol from wreq.http2 import Http2Options, PseudoId, PseudoOrder from wreq.header import HeaderMap, OrigHeaderMap
tls_options = TlsOptions( grease_enabled=True, enable_ocsp_stapling=True, curves_list=":".join(["X25519", "P-256", "P-384"]), cipher_list=":".join([ "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", ]), alpn_protocols=[AlpnProtocol.HTTP2, AlpnProtocol.HTTP1], min_tls_version=TlsVersion.TLS_1_2, max_tls_version=TlsVersion.TLS_1_3, )
http2_options = Http2Options( initial_stream_id=3, initial_window_size=16777216, headers_pseudo_order=PseudoOrder( PseudoId.METHOD, PseudoId.PATH, PseudoId.AUTHORITY, PseudoId.SCHEME, PseudoId.PROTOCOL, ), )
orig_headers = OrigHeaderMap() orig_headers.insert("Cookie") orig_headers.insert("User-Agent")
client = Client( tls_options=tls_options, http2_options=http2_options, orig_headers=orig_headers, )
|
这里能看出 wreq 的设计哲学:JA3/JA4/Akamai 指纹不是靠一串字符串模拟,而是靠对 cipher 列表、曲线、ALPN、HTTP2 SETTINGS、伪头顺序、头部原始顺序等逐项还原——这也是它比”设个 ja3 字符串”的方案更贴近真实浏览器的原因。
七、和其他方案的对比
| 方案 |
语言/底层 |
TLS 指纹 |
备注 |
| requests / httpx / aiohttp |
Python ssl |
❌ 无伪装 |
指纹一眼假 |
| curl_cffi |
libcurl-impersonate |
✅ |
老牌方案,见本站 curl-impersonate 笔记 |
| wreq(原 rnet) |
Rust + BoringSSL |
✅ 细粒度 |
异步优先、性能强、profile 多 |
选型建议:需要异步 + 高并发 + 多浏览器 profile,wreq 是很好的选择;已有 curl_cffi 工作流、或需要 Python 3.7~3.10,可继续用 curl_cffi 或旧包 rnet。
参考
本文仅作技术学习记录。请在合法合规、遵守目标站点条款的前提下使用相关技术。