"""虎牙网页匿名设备态协议生成器(纯 HTTP,middle + anonymousLogin)。 生成 4 个网页设备态字段(来源见 docs/HUYA_COOKIE合并审计与来源矩阵.md): - ``udb_deviceid`` <- GET /web/middle/2.4/{rand8}/https/{hex32} 的 Set-Cookie - ``udb_guiddata`` <- middle URL 第三段 hex32(客户端自选,服务端不校验) - ``udb_anobiztoken``<- POST /web/anonymousLogin 响应 data.biztoken(344 位) - ``udb_anouid`` <- anonymousLogin 响应 data.uid 用法:: python -m core.huya.anon_device # 打印 4 字段 JSON python -m core.huya.anon_device --json # 同上,紧凑 JSON(供脚本消费) 注意: 200 / returnCode=0 只证明接口受理,不证明风控认可这些值的真实性。 ``guid`` / ``_qimei_uuid42`` / ``__yamid_new`` / ``game_did`` 仍无服务端 签发接口,本模块不生成、不伪造它们。 """ from __future__ import annotations import argparse import json import random import re import secrets import sys from collections.abc import Mapping import requests from loguru import logger MIDDLE_URL = "https://udblgn.huya.com/web/middle/2.4/{rand8}/https/{guiddata}" ANON_LOGIN_URL = "https://udblgn.huya.com/web/anonymousLogin" DESKTOP_UA = ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36" ) UDBN_DEVICEID_RE = re.compile(r"udb_deviceid=([^;]+)") class AnonymousDeviceError(RuntimeError): """匿名设备态获取失败。""" def _new_session(*, proxies: Mapping[str, str] | None = None) -> requests.Session: session = requests.Session() session.trust_env = False if proxies: session.proxies.update(dict(proxies)) return session def _parse_set_cookie_deviceid(set_cookie: str) -> str: match = UDBN_DEVICEID_RE.search(set_cookie or "") if not match or not re.fullmatch(r"w_\d{19}", match.group(1)): raise AnonymousDeviceError("middle 响应未下发合法 udb_deviceid") return match.group(1) def fetch_anonymous_device_state( *, proxies: Mapping[str, str] | None = None, timeout: float = 15.0, session: requests.Session | None = None, ) -> dict[str, str]: """换取匿名设备态四字段:udb_deviceid / udb_guiddata / udb_anobiztoken / udb_anouid。 与网页 SDK 相同的两步:middle 引导页签发 deviceid,anonymousLogin 换取 biztoken/uid。所有参数客户端随机生成,无需任何既有 Cookie。 """ own_session = session is None s = session or _new_session(proxies=proxies) try: guiddata = secrets.token_hex(16) rand8 = str(random.randint(10_000_000, 99_999_999)) middle_url = MIDDLE_URL.format(rand8=rand8, guiddata=guiddata) # 1) middle:签发 udb_deviceid resp = s.get( middle_url, headers={"User-Agent": DESKTOP_UA, "Referer": "https://www.huya.com/"}, timeout=timeout, allow_redirects=False, ) if resp.status_code != 200: raise AnonymousDeviceError(f"middle 返回 {resp.status_code}") deviceid = _parse_set_cookie_deviceid(resp.headers.get("Set-Cookie", "")) # 2) anonymousLogin:发放匿名 uid + biztoken(必须带 middle Referer) context = ( "WB-" + secrets.token_hex(16) + "-" + secrets.token_hex(16) + "-" + secrets.token_hex(16) ) payload = { "uri": "10013", "version": "2.4", "context": context, "appId": 5002, "sdid": "csid_" + secrets.token_hex(16), "lcid": 2052, "byPass": 3, "requestId": random.randint(10_000_000, 99_999_999), "authId": "", "data": {"domainList": ""}, } anon = s.post( ANON_LOGIN_URL, headers={ "User-Agent": DESKTOP_UA, "Referer": middle_url, "Content-Type": "application/json;charset=UTF-8", }, data=json.dumps(payload), timeout=timeout, allow_redirects=False, ) if anon.status_code != 200: raise AnonymousDeviceError(f"anonymousLogin 返回 {anon.status_code}") try: body = anon.json() except ValueError as exc: raise AnonymousDeviceError( f"anonymousLogin 响应非 JSON: {anon.text[:80]!r}" ) from exc if body.get("returnCode") != 0: raise AnonymousDeviceError( f"anonymousLogin returnCode={body.get('returnCode')} " f"message={body.get('message')!r}" ) data = body.get("data") or {} anouid = str(data.get("uid") or "") biztoken = str(data.get("biztoken") or "") if not anouid or not biztoken: raise AnonymousDeviceError("anonymousLogin 响应缺少 uid/biztoken") if not 300 <= len(biztoken) <= 400: logger.warning("[huya-anon] biztoken 长度异常: {}", len(biztoken)) logger.info( "[huya-anon] 匿名设备态 OK: deviceid={}... guiddata={}... " "anouid={} biztoken={}...", deviceid[:14], guiddata[:8], anouid, biztoken[:10], ) return { "udb_deviceid": deviceid, "udb_guiddata": guiddata, "udb_anobiztoken": biztoken, "udb_anouid": anouid, } finally: if own_session: s.close() def main() -> int: ap = argparse.ArgumentParser(description="虎牙匿名设备态四字段生成(纯协议)") ap.add_argument("--json", action="store_true", help="输出紧凑 JSON") args = ap.parse_args() try: state = fetch_anonymous_device_state() except AnonymousDeviceError as exc: print(f"❌ {exc}", file=sys.stderr) return 2 if args.json: print(json.dumps(state, ensure_ascii=False)) else: for key, value in state.items(): print(f"{key} = {value}") print("\n(4/7 字段;guid/_qimei_uuid42/__yamid_new/game_did 无服务端签发,不生成)") return 0 if __name__ == "__main__": raise SystemExit(main())