跳转至

客户端配置

x-api-rs 是 SDK,HTTP/性能/可用性相关参数都通过 RquestClientBuilder 暴露给上层使用方。 本文档列出所有可配置项的默认值、风险、何时该改。


设计原则

  • 安全默认 + 可覆盖:默认值对非幂等写操作(DM/Posts/Like/上传)安全,高级用户可通过 builder 覆盖
  • 协议常量不暴露:与 Twitter API 协议绑定的常量(如 MAX_MESSAGE_LENGTH = 10000、GraphQL queryId、Bearer Token)写死在 SDK 内,不可配置
  • 传输/性能参数暴露:timeout、retry、proxy、pool size、JA3 指纹等可调

配置项参考

tls_cert_verification: bool 🔴

是否校验服务端 TLS 证书。对应 wreq ClientBuilder::tls_cert_verification

项目
默认 true(与 wreq 库默认一致,校验证书)
风险等级 设为 false 时 🔴 高(接受任意无效证书,存在 MITM)

仅在必须使用自签/拦截式调试代理时,通过 RquestClientBuilder::tls_cert_verification(false) 显式关闭。 环境快照 network.tls_cert_verification 会导出/还原该选项;旧快照缺字段时按 true 反序列化。


http2_max_retry_count: usize 🔴

HTTP/2 protocol-nack 自动重试次数。控制 wreq 在收到 RST_STREAM(REFUSED_STREAM)GOAWAY(NO_ERROR) 时是否自动重发请求。

项目
本 SDK 默认 0(禁用)
wreq 默认 retry policy protocol-nack 最大重试 2
风险等级 🔴 高(影响数据正确性)

为什么默认禁用

wreq 默认 retry policy 把 H2 REFUSED_STREAM 视为"请求未到达 server"自动重发(RFC 9113 §8.7-3.2)。 但 Twitter 在限流时会先处理请求再发 RST_STREAM——server 已经收到并执行了请求, wreq 仍重发,导致同一条 DM/Tweet 被发送 2 次。

200 并发 DM 场景实测:11-19 条消息出现重复(5.5%-9.5% 重复率)。

何时可以改成 >0

  • ✅ 客户端用于幂等读操作:搜索、获取用户资料、获取 timeline、获取私信列表
  • 绝对不能用于:DM 发送、发帖、点赞、转发、关注、上传——任何写操作

如果同一个客户端实例会混用读写,保留默认 0

用法

use x_api_core::common::{CookieAuth, ClientTransaction, RquestClientBuilder};
use std::sync::Arc;

// ✅ 安全默认(推荐)
let client = RquestClientBuilder::new(auth, transaction).build()?;

// ⚠️ 仅在纯读场景下可考虑
let client = RquestClientBuilder::new(auth, transaction)
    .http2_max_retry_count(2)
    .build()?;

proxy_url: Option<String>

代理服务器 URL,支持 HTTP / HTTPS / SOCKS5。

项目
默认 None(直连)
风险等级 🟢 低

已知陷阱

  • Burp Suite 会破坏 JA3 指纹模拟(TLS MITM 终止连接)→ 触发 Twitter 226 反爬
  • 大文件 multipart 上传 (>~100KB) 通过 Burp 会失败:Stream failed to close correctly
  • 测试场景建议使用 mitmproxy 或直连

用法

RquestClientBuilder::new(auth, transaction)
    .proxy_url("http://127.0.0.1:8080".to_string())
    .build()?;

profile: Option<WebProfile>

浏览器指纹 profile —— 把 TLS 指纹(JA3/JA4 + HTTP/2 SETTINGS)与 HTTP 头部(User-Agent / Sec-Ch-Ua)绑成一个自洽身份。支持每账号独立配置

项目
默认(不传) WebProfile::Chrome { os: Windows, arch: X64, version: CHROME_MAJOR }(当前为 Chrome 149 Windows x64)
风险等级 🟢 低

为什么是一个参数而不是 enable_ja3 + fingerprint 两个:真实浏览器的 TLS 指纹和 HTTP 头是同一个身份,拆成两个独立开关会产生「UA 自称 Chrome 140、TLS 握手却是另一版本」这种错配——而 UA/JA3 错配本身就是反爬识别机器人的特征。WebProfile 保证两层永远一致。

默认为何固定 Chrome 基线 Windows x64(而非随机):指纹模拟的目标是混进最大的真实人群,不是变独特。Chrome Windows 是全球 Twitter Web 桌面占比最大的配置,淹没在真人流量里最安全。随机化反而可能制造唯一性或 UA/JA3 错配。

跨账号差异化:要让 A 账号和 B 账号在 JA3 层真正不同,必须跨浏览器家族(Chrome / Safari / Firefox / Edge / OkHttp 各自 TLS 不同);在同一 Chrome 家族内换 OS/版本不改变 JA3。

支持的 profile

家族 构造 TLS 来源 HTTP 头
Chrome(默认 149) WebProfile::Chrome { os, arch, version } Emulation::ChromeNNN(与 version 对齐) SDK 自建完整 Sec-Ch-Ua-*
Safari macOS WebProfile::SafariMac(v) wreq-util Safari emulation emulation 自带(Safari 不发 Sec-Ch-Ua)
Safari iOS WebProfile::SafariIos(v) 同上 同上
Safari iPad WebProfile::SafariIpad 同上 同上
Firefox WebProfile::Firefox(v) wreq-util Firefox emulation 同上
Edge WebProfile::Edge(v) wreq-util Edge emulation 同上
OkHttp(Android app) WebProfile::OkHttp(v) wreq-util OkHttp emulation 同上
禁用 TLS 模拟(调试) WebProfile::None wreq 默认 无模拟

HTTP/2:始终开启,h2 SETTINGS 帧指纹随 profile 一起模拟(WebProfile::None 除外)。

TLS 版本锚定:Chrome 默认版本由 CHROME_MAJOR(当前 149)决定,且必须与 wreq-utilEmulation::ChromeNNN 同步;Sec-Ch-Ua/User-Agent 由 SDK 按同一版本自建。

Rust 用法

use x_api_core::web::{Architecture, OsType, WebClient, WebProfile};

// 默认(Chrome 149 Windows x64)
let client = WebClient::create(cookies, None, None).await?;

// 指定 Chrome macOS arm64
let p = WebProfile::Chrome { os: OsType::MacOS, arch: Architecture::Arm64 };
let client = WebClient::create(cookies, None, Some(p)).await?;

// 查看当前身份
let info = client.fingerprint_info();
println!("{} {} {} {}", info.browser, info.version, info.platform, info.user_agent);

完整示例

use x_api_core::common::fingerprint::types::{Architecture, OsType};
use x_api_core::web::{WebClient, WebProfile};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let cookies = "ct0=xxx; auth_token=yyy; twid=u%3D123".to_string();

    // 默认安全配置(Chrome 149 Windows x64,TLS 证书校验开启,H2 重试禁用)
    let client = WebClient::create(cookies.clone(), None, None).await?;

    // 带代理 + 指定 profile
    let p = WebProfile::Chrome { os: OsType::MacOS, arch: Architecture::Arm64 };
    let client = WebClient::create(cookies, Some("http://127.0.0.1:8080".into()), Some(p)).await?;

    Ok(())
}

需要细粒度控制 http2_max_retry_count 等传输参数时,优先使用 WebClient::builder() 的 transport/profile 配置;若需要更底层控制,再在 core 内部通过 web::transport 组合 WebClient


Python 用户

from x_api_rs.web import Client, WebProfile

# 默认(Chrome 149 Windows x64,对齐当前 wreq-util 最新桌面模板)
client = await Client.create(cookies)

# 带代理
client = await Client.create(cookies, proxy_url="http://127.0.0.1:8080")

# 每账号独立身份——跨家族才有真实 JA3/H2 差异
a = await Client.create(cookies_a, profile=WebProfile.chrome("macos", "arm64"))
b = await Client.create(cookies_b, profile=WebProfile.safari_ios("18.1.1"))
c = await Client.create(cookies_c, profile=WebProfile.okhttp("5"))

# 禁用 TLS 模拟(仅调试)
d = await Client.create(cookies, profile=WebProfile.none())

# 查看实际身份
info = a.fingerprint_info()
print(info.browser, info.version, info.platform)
print(info.user_agent)

WebProfile 工厂方法

方法 说明
WebProfile.default_latest() 当前推荐默认画像,等价于 Chrome 149 Windows x64(wreq-util 最新桌面模板)
WebProfile.chrome(os, arch, version=None) Chrome 基线 149;version 可选,仅允许当前库有 TLS 模板的产品版本(120–149,排除 121/122/125)。os: windows/macos/linux;arch: x64/x86_64/arm64/aarch64
WebProfile.safari_mac(version) Safari macOS。17.5/18/18.2/18.3/18.3.1
WebProfile.safari_ios(version) Safari iOS。17.4.1/18.1.1
WebProfile.safari_ipad() Safari iPadOS 18
WebProfile.firefox(version) Firefox。133/135/136/private136/android135
WebProfile.edge(version) Edge。131/134
WebProfile.okhttp(version) OkHttp。5/4.12
WebProfile.none() 禁用 TLS 模拟,仅用于调试
WebProfile.from_spec(spec) 从持久化字符串还原,如 chrome:macos:arm64

profile.spec() 返回可持久化的规范字符串,可和环境快照或自管数据库一起保存。

fingerprint_info() 返回 ProfileInfo,字段:browser / version / platform / user_agent

http2_max_retry_count 当前未在 Python 层暴露——因为 Python 用户场景几乎都涉及写操作, 强制使用安全默认(0)。如需读专用客户端,请联系 issue 反馈。


历史

  • 2026-05-09:发现底层 HTTP 客户端默认 H2 protocol-nack retry 会导致 DM 重复发送,本 SDK 默认改为 0,并通过 RquestClientBuilder::http2_max_retry_count 暴露为可配置(v1.0.15);迁移到 wreq 6 后该配置映射为 wreq::retry::Policy