File size: 4,840 Bytes
89419ea | 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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | """
浏览器指纹 Profile 工厂
提供多版本 Chrome 浏览器指纹配置,确保 TLS impersonate、User-Agent、
sec-ch-ua 三者版本一致,消除反检测信号。
"""
import random
from dataclasses import dataclass, field
from typing import Dict, Optional
# Chrome 版本指纹池 — impersonate 标识必须与 curl_cffi 支持的版本匹配
_CHROME_PROFILES = [
{
"major": 131, "impersonate": "chrome131",
"build": 6778, "patch_range": (69, 205),
"sec_ch_ua": '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
},
{
"major": 133, "impersonate": "chrome133a",
"build": 6943, "patch_range": (33, 153),
"sec_ch_ua": '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',
},
{
"major": 136, "impersonate": "chrome136",
"build": 7103, "patch_range": (48, 175),
"sec_ch_ua": '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
},
]
@dataclass(frozen=True)
class BrowserProfile:
"""单次会话绑定的浏览器指纹 Profile"""
impersonate: str
major: int
full_version: str
user_agent: str
sec_ch_ua: str
sec_ch_ua_mobile: str = "?0"
sec_ch_ua_platform: str = '"Windows"'
# ---- 通用 sec-ch-ua 三件套 ----
def _ch_ua_headers(self) -> Dict[str, str]:
"""返回 sec-ch-ua 三件套,Chrome 每个 HTTPS 请求默认发送。"""
return {
"sec-ch-ua": self.sec_ch_ua,
"sec-ch-ua-mobile": self.sec_ch_ua_mobile,
"sec-ch-ua-platform": self.sec_ch_ua_platform,
}
# ---- 页面导航请求 headers(GET HTML 页面)----
def navigation_headers(self, referer: Optional[str] = None) -> Dict[str, str]:
"""
浏览器导航到页面时的 headers(如 GET auth.openai.com/oauth/authorize)。
对应 Sec-Fetch-Mode: navigate。
"""
headers: Dict[str, str] = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"accept-language": "en-US,en;q=0.9",
"upgrade-insecure-requests": "1",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-user": "?1",
"user-agent": self.user_agent,
}
headers.update(self._ch_ua_headers())
if referer:
headers["referer"] = referer
headers["sec-fetch-site"] = "cross-site"
else:
headers["sec-fetch-site"] = "none"
return headers
# ---- JSON API 请求 headers(POST auth.openai.com/api/*)----
def json_api_headers(
self,
referer: str,
origin: str = "https://auth.openai.com",
oai_did: Optional[str] = None,
) -> Dict[str, str]:
"""
JSON API 请求的统一 headers(如 signup、password_verify、validate_otp、create_account)。
对应 Sec-Fetch-Mode: cors。
"""
headers: Dict[str, str] = {
"accept": "application/json",
"content-type": "application/json",
"origin": origin,
"referer": referer,
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
}
headers.update(self._ch_ua_headers())
if oai_did:
headers["oai-device-id"] = oai_did
return headers
# ---- Sentinel 请求 headers ----
def sentinel_headers(self) -> Dict[str, str]:
"""
Sentinel PoW 请求的 headers(POST sentinel.openai.com)。
对应跨域 CORS 请求。
"""
headers: Dict[str, str] = {
"origin": "https://sentinel.openai.com",
"referer": "https://sentinel.openai.com/backend-api/sentinel/frame.html?sv=20260219f9f6",
"content-type": "text/plain;charset=UTF-8",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
}
headers.update(self._ch_ua_headers())
return headers
def get_random_profile() -> BrowserProfile:
"""从 Chrome 版本池中随机选择一个 Profile,整个注册会话内保持一致。"""
cfg = random.choice(_CHROME_PROFILES)
major = cfg["major"]
build = cfg["build"]
patch = random.randint(*cfg["patch_range"])
full_ver = f"{major}.0.{build}.{patch}"
ua = (
f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
f"AppleWebKit/537.36 (KHTML, like Gecko) "
f"Chrome/{full_ver} Safari/537.36"
)
return BrowserProfile(
impersonate=cfg["impersonate"],
major=major,
full_version=full_ver,
user_agent=ua,
sec_ch_ua=cfg["sec_ch_ua"],
)
|