wreq(原 rnet):带 TLS/JA3/JA4 指纹的 Python HTTP 客户端使用教程

本文最后更新于: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 rnetfrom wreq import X 换成 from rnet import X 即可,API 基本一致。

⚠️ 版本注意:新包 wreq 要求 Python ≥ 3.11;旧包 rnet 支持 Python ≥ 3.7。低版本环境暂时只能用 rnet。

二、它解决什么问题?

一句话:它是一个自带浏览器 TLS / HTTP2 指纹伪装的 HTTP 客户端,用来对抗基于指纹的反爬。

普通的 requestshttpxaiohttp 用的是 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 来精确还原真实浏览器行为。

三、安装

1
pip install wreq

旧包(Python 3.7~3.10 环境):

1
pip install rnet

预编译 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():
# 构建一个客户端,模拟 Chrome 149 的指纹
client = Client(emulation=Emulation.Chrome149)

# 用你已经熟悉的 API 发请求
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) # 能拿到远端 IP
print("set-cookie:", resp.headers["set-cookie"])

# headers 可迭代
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

# 直接用预设:Firefox 135
client = Client(emulation=Emulation.Firefox135)

# 指定 profile + 平台:Chrome 134 on Android
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/allhttps://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

# JSON body
resp = await wreq.post("https://httpbin.io/anything", json={"key": "value"})
print(await resp.json())

# 表单(dict 或 list of tuples 都行)
resp = await wreq.post(
"https://httpbin.io/anything",
form={"keyA": "valueA", "number": 789, "flag": False},
)

# query 查询参数
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"},
),
)

# Unix socket 代理(例如直接怼 docker.sock)
resp = await wreq.get(
"http://localhost/v1.41/containers/json",
proxy=Proxy.unix("/var/run/docker.sock"),
)

5. 认证

1
2
# Bearer / token
resp = await wreq.get("https://httpbin.io/anything", auth="token")

(另有 examples/basic_auth.pybearer_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() # 阻止跳到 example.com
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")

8. HeaderMap(同名多值头)

HeaderMap 支持同一个 key 多值,适合处理 AcceptSet-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"))) # ['application/json', 'text/html']

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,
),
)

# OrigHeaderMap 用来保留请求头的原始大小写与顺序(指纹的一部分)
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。

参考


本文仅作技术学习记录。请在合法合规、遵守目标站点条款的前提下使用相关技术。


wreq(原 rnet):带 TLS/JA3/JA4 指纹的 Python HTTP 客户端使用教程
https://kingjem.github.io/2026/08/14/逆向/wreq-rnet-带TLS指纹的Python-HTTP客户端使用教程/
作者
Ruhai
发布于
2026年8月14日
许可协议