never_fox
发出去的请求在字节层面就是 Firefox 152 —— 不是模拟,是真的。
never_fox 是一个 requests 风格的 Python HTTP 客户端,底层链接 Firefox 真正的 TLS 引擎 NSS,所以它的 TLS ClientHello 与真 Firefox 152 逐字节相同(含 ECH、X25519MLKEM768 后量子组),HTTP/2 帧与 header 也与真火狐一致。专为高并发爬虫 / 风控对抗设计。
一句话原理:Chrome 有 Cronet、Firefox 的网络栈是 NSS(TLS)+ Necko。NSS 开源可独立链接 —— 我们直接链接真 NSS,用逆向出来的 Firefox 152 配置驱动它。这是"模拟"做不到的:curl_cffi 这类能对齐 JA3/JA4,但 ECH 等细节对不上,因为它们用的不是 NSS。
为什么它是"真的"
在本地受控 HTTPS 服务器上,让真 Firefox 152 和 never_fox 都访问,逐字段对比握手数据,并在 4 个指纹站(peet.ws / browserleaks / scrapfly / howsmyssl,算法各不同) 交叉验证:
| 维度 | 与真 Firefox 152 |
|---|---|
| TLS ClientHello 结构字节(屏蔽随机数/临时密钥/扩展排列) | ✅ 一致 |
| JA3 / JA3N / JA4 / JA4_o / JA4_r | ✅ 全部一致 |
| HTTP/2 Akamai 指纹(SETTINGS/WINDOW_UPDATE/优先级/伪头序) | ✅ 一致 |
| 所有 HTTP header(名 / 顺序 / 值) | ✅ 一致 |
| ECH、record_size_limit、MLKEM、delegated_credentials | ✅ 一致 |
特性
| 真指纹 | 真 NSS 引擎,ClientHello 字节级 == Firefox 152(含 ECH) |
| requests 兼容 | get/post/put/patch/delete/head/options;默认 raw_headers=True,传入头的值优先并按 Firefox 请求形状纠序,缺失项才兜底(raw_headers=False 可启用 Session 合并);Response.headers 大小写不敏感、.elapsed(timedelta)、.request/.links/.iter_lines/.is_redirect/bool(resp);异常层级 RequestException/HTTPError/ConnectionError/Timeout/TooManyRedirects;auth=(u,p)、files=(multipart)、hooks=、按请求 verify |
| 异步 | AsyncSession,future 驱动,线程数≈连接数而非请求数 |
| 高并发 | 冷启动单飞建连(一个 origin 收敛到 1 条 h2,像浏览器)+ 多路复用 + 引用计数防崩 |
| Cookie | RFC 6265/6265bis cookie jar:Expires+Max-Age、Public Suffix、Secure 来源、SameSite、host-only、default-path、__Host-/__Secure-;H2 按 Firefox 拆 cookie 字段,H1 自动合并;支持完整记录 get_records/set_record |
| 稳健 | 仅幂等方法自动重试/退避、取消/超时/超限发 RST_STREAM、H2 上传双层流控与 CONTINUATION、H1 严格分帧、原始/解压响应上限、跨 origin 重定向剥离凭据与实体头 |
| 代理 | HTTP CONNECT + SOCKS5(可认证);目标看到真 FF152,代理只见加密隧道 |
| 限流 | 每 host 限速(带抖动)+ 429/503 指数退避(尊重 Retry-After) |
| HTTP/3 | 默认 h3="auto":通过 DNS HTTPS/SVCB 或 Alt-Svc 自动发现 H3,复用常驻 neqo QUIC 会话;失败快速回退 H2 |
环境要求与构建
native 引擎是编译产物,按平台分发(像 Cronet)。重依赖 NSS/NSPR/brotli/zstd 不用我们编译 —— 由各平台包管理器提供预编译版,我们只编 ~250 行的引擎(几秒)。
# 1) 系统依赖(预编译的 NSS 等)
brew install nss nspr brotli zstd # macOS
sudo apt-get install libnss3-dev libnspr4-dev libbrotli-dev libzstd-dev zlib1g-dev patchelf # Linux
# Windows: MSYS2 装 mingw-w64-x86_64-{nss,nspr,brotli,zstd,zlib,gcc,pkg-config}
# 2) Python 依赖
pip install hpack brotli zstandard publicsuffixlist
# 3) 跨平台编译(自动找 NSS,产出 libfxtls.{dylib,so,dll})
python native/build.py
# 4) 自检指纹 == Firefox 152(NSS 版本漂移会在这里报错)
python native/verify.py
# 5)(可选)打包自包含,拷到同 OS+架构机器免依赖运行
python native/bundle.py # -> native/vendor/
然后把仓库目录加入 PYTHONPATH,import never_fox 即可。
多平台预编译(GitHub CI)
.github/workflows/build.yml 用矩阵在 Linux x86_64 / Linux arm64 / macOS arm64 / Windows x86_64 原生 runner 上自动:装预编译 NSS → build.py → verify.py 指纹门禁 → 上传各平台产物;打 vX.Y.Z tag 会把四平台产物附到 GitHub Release。NSS 由包管理器缓存,只在升版本时重拉。
快速开始
import never_fox as nf
r = nf.get("https://example.com/", params={"q": "x"})
print(r.status_code, r.ok, r.http_version, r.text[:200])
r.raise_for_status()
r = nf.post("https://httpbin.org/post", json={"hello": "firefox152"}) # 或 data={...} 表单
print(r.json())
Session(Cookie / 重定向 / 连接池)
s = nf.Session() # verify=True, h3="auto"
s.get("https://site/login") # Set-Cookie 自动保存
s.post("https://site/api", data={"a": 1}) # 自动带 Cookie、自动重定向(r.history)
print(s.cookies.as_dict())
s.put(...); s.delete(...); s.patch(...); s.head(...); s.options(...)
s.close()
Session 默认启用自动资源兜底:响应体上限 32 MiB、响应头上限 256 KiB、最多缓存 64 个 H2 host、每 host 最多 6 条连接、最多 8 个 H3 worker、连接空闲 60 秒回收、DNS/Alt-Svc 各最多 4096 项、Cookie 最多 3000 个且单域名最多 180 个(单条 4 KiB)。后台 reaper 每 5 秒清扫一次,无需业务代码定期清理;即使 Session 被遗忘也会由析构兜底关闭。显式传 max_response_bytes=None 可恢复无限响应,但不建议爬虫使用。
异步(高并发)
import asyncio, never_fox as nf
async def main():
async with nf.AsyncSession() as s:
# 上千并发共享连接池中的多路复用连接;响应通过 future 等待,
# 不为每个请求占一个线程(线程数≈连接数,不随请求数增长)。
rs = await asyncio.gather(*[s.get(f"https://site/p/{i}") for i in range(1000)])
print(sum(r.ok for r in rs), "ok")
asyncio.run(main())
爬虫调优(代理 / 限速 / 退避)
s = nf.Session(
max_connections_per_host=16, # 每 host 最多连接数
max_connecting=8, # 每 Session 同时冷建连上限(跨 host)
max_pool_hosts=64, # 全局最多保留的 H2 host
max_h3_workers=8, # 常驻 H3/QUIC worker 上限
connection_idle_timeout=60, # H2/H3 空闲回收秒数
max_response_bytes=32 << 20, # 原始及解压后响应上限
rate_limit=5, # 每 host <= 5 请求/秒(0=不限)
backoff_retries=3, # 429/503 指数退避重试,尊重 Retry-After
retries=3, # 连接级重试(短指数抖动;已发送请求仅安全方法重放)
verify=True, # 用 Firefox 同款 Mozilla 根证书校验
)
# 代理:HTTP CONNECT 或 SOCKS5(可认证)。同一个代理 session id 应复用同一个
# Session/H2 隧道;不要在请求前连续覆盖 proxy 变量。
s = nf.Session(proxy="http://user:pass@proxy:8080", retries=3,
connection_idle_timeout=60)
r = nf.get(url, proxy="socks5://user:pass@10.0.0.1:1080") # 或 proxies={"https": "..."}
Response:.status_code .ok .reason .url .text .content .json() .headers .cookies .history .elapsed .encoding .raise_for_status() .iter_content()
工作原理
never_fox/ Python 层
client.py Session / Response / 连接池 / Cookie / 重定向 / 限速 / 代理解析
aio.py AsyncSession(future 驱动的异步)
h2conn.py HTTP/2 多路复用(复刻 Firefox 的 SETTINGS/优先级/伪头序)
http1.py HTTP/1.1 回退
h3.py HTTP/3(常驻 neqo 会话与多路复用)
httpssvc.py DNS HTTPS/SVCB 发现与 TTL 缓存
cookies.py CookieJar
_native.py ctypes 绑定到原生引擎
native/ 原生引擎(C,链接真 NSS)
fxtls_config.h Firefox 152 的 ClientHello 配置(密码套件/组/签名算法/ECH/证书压缩…)
fxtls_lib.c 连接 + TLS 握手 + 收发 + 代理(CONNECT/SOCKS5)-> libfxtls.dylib
bundle.py 把依赖 dylib 收进 vendor/ 并改 @loader_path,便于跨机
harness/ 指纹验证脚本(本地抓包对比 + 多站交叉验证)
证书用 NSS 内置的 Mozilla 根证书列表(libnssckbi) 校验 —— 和 Firefox 同款信任库。
已知限制
- 原生库是平台相关二进制:跨 OS / 架构需在目标机重新
build.sh(像 Cronet 按平台分发)。Firefox/Linux 是 OS 自洽目标,适合做服务端。 - TLS 会话:连接池复用已建立的 H2 连接;新建 TCP 连接目前固定使用 Firefox 152 首次连接的完整握手形状,不会发送恢复握手的
pre_shared_key。 - HTTP/3:需运行环境允许目标 UDP 端口出网。默认同时支持 RFC 9460 HTTPS/SVCB(AliasMode、优先级、
mandatory、alpn、port、地址 hint、TTL/负缓存)和 Alt-Svc(ma/clear、备用 host/port);每个Session复用常驻 QUIC 连接并并发承载多个 stream。HTTPS RR 有 50ms 优先窗口,Alt-Svc H3 建连 100ms 后准备 TCP/H2 后备,失败端点会临时熔断。为避免重复副作用,非安全方法只有在取消确认表明尚未创建 H3 请求流时才自动回退。Windows wheel 当前不含 neqo worker,会自动使用 H2。可用NEVER_FOX_DNS_SERVERS=1.1.1.1,8.8.8.8覆盖系统 DNS 服务器。 - requests 差异:
stream=True收下但 body 始终全量缓冲;cert=(客户端证书)不支持(NSS 用 Mozilla 根),会抛NotImplementedError;verify='/ca.pem'自定义 CA 包不认(走 NSS 根)。
验证复现
# 本地起 HTTPS/h2 服务,真 Firefox + never_fox 都访问,逐字段对比
python harness/localcap/diff_h2cap.py # 看 harness/localcap/FULL_DIFF.md
# 构建本地 H3 对照服务,实抓 Firefox 152 与公共 Session 的
# QPACK、QUIC transport parameters、ClientHello 并和 golden 比较
H3_SERVER_OUT=$PWD/native/h3/neqo-server python native/h3/build_h3.py
python harness/capture_h3.py
# 不启动浏览器,仅验证 never_fox 当前 H3 输出与已提交 golden
python harness/capture_h3.py --skip-firefox
# 多站交叉验证报告
cat harness/localcap/MULTISITE.md
H3 实抓需要 openssl、NSS 的 certutil/pk12util,以及 macOS 上的 Firefox 152。
参考数据位于 harness/golden/firefox152_h3.json;只有确认浏览器版本和线级变化后才应使用
python harness/capture_h3.py --update-golden 更新。
详细逆向与对比过程见 REPORT.md。
免责声明
仅用于授权范围内的安全研究、风控对抗测试与合规数据采集。请遵守目标站点的条款与当地法律。
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file never_fox-0.3.15-py3-none-win_amd64.whl.
File metadata
- Download URL: never_fox-0.3.15-py3-none-win_amd64.whl
- Upload date:
- Size: 4.0 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4052b1501ca217028d3105e8de369b88f473db36cc2db1ca348a151cda822284
|
|
| MD5 |
d9ef283bf6bf598507fa82e69aa2f2c1
|
|
| BLAKE2b-256 |
1821fd26115706e8c09f0bfec92d69a0bebe8f3d04f4f15856e6b9ff5b35ea0c
|
File details
Details for the file never_fox-0.3.15-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: never_fox-0.3.15-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 6.2 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
924ba0dda7b209b3a6fecd3d587c48597778e1b7142221e218cd577da0e11fea
|
|
| MD5 |
e1a01c5f06e00b935727f01454e3df79
|
|
| BLAKE2b-256 |
bc368ea5fafcb48add545f6a5edd3e4ac2d5df7bb253fbee42c22639bc87cddd
|
File details
Details for the file never_fox-0.3.15-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.
File metadata
- Download URL: never_fox-0.3.15-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
- Upload date:
- Size: 6.1 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f6e2b1a8254685986712cb1272b30efe3ddf28eeb8a2d20eaa50c9f8b560db3
|
|
| MD5 |
47be016581906732e752538f90e835ef
|
|
| BLAKE2b-256 |
b1e602f73dba55f254442cfbe748622cbafaf0a2442bd15e0e5d7332774ac08e
|
File details
Details for the file never_fox-0.3.15-py3-none-macosx_14_0_arm64.whl.
File metadata
- Download URL: never_fox-0.3.15-py3-none-macosx_14_0_arm64.whl
- Upload date:
- Size: 5.4 MB
- Tags: Python 3, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92a24dfe759c03fbd7add32e63f771f46f3ea136e757ed87b66801d4aa6a75e4
|
|
| MD5 |
402489c03304ca3dd6e8be6fc6db526e
|
|
| BLAKE2b-256 |
88fbda99e78b6834b33b20f3990c996d4612d5cc6a29dd8f9a57791ff4fcd842
|