# -*- coding: utf-8 -*- # @Time: 2023/12/26 import json import time from base64 import b64encode from functools import reduce from hashlib import md5 from random import choice, randint from typing import Any, Dict, List, Optional from urllib.parse import urlencode class DouYinHelper(object): ttwid_list = [ '1|G3wy_-RdLJnfG5P9zAcP54OM8_nTLZVrJxNi1lPzdmg|1693558867|5e43c47a424e939aaf7193b096e3c6f2274982ee64e9608c99c54d2a43982aca' ] @classmethod def _0x30492c(cls, x: bytes, y: bytes, f: Optional[List[int]] = None) -> bytes: """RC4加密, 可以用Crypto.Cipher.ARC4替代""" c = 0 d = [i for i in range(256)] for b in range(256): c = (c + d[b] + x[b % len(x)]) % 256 e = d[b] d[b] = d[c] d[c] = e t, c = 0, 0 if not f: f = [] for i in range(len(y)): t = (t + 1) % 256 c = (c + d[t]) % 256 e = d[t] d[t] = d[c] d[c] = e f.append(y[i] ^ d[(d[t] + d[c]) % 256]) return bytes(f) @classmethod def _0x485470(cls, a: str) -> List[int]: _0x583e81 = [0] * 103 for i in range(10): _0x583e81[i + 48] = i for j in range(10, 16): _0x583e81[j + 87] = j b = len(a) >> 1 e = b << 1 d = [0] * b c = 0 for f in range(0, e, 2): d[c] = _0x583e81[ord(a[f])] << 4 | _0x583e81[ord(a[f + 1])] c += 1 return d @classmethod def calc_x_bogus(cls, ua: str, query: str, data: Optional[Dict[str, Any]] = None) -> str: """计算X_Bogus参数""" query = query.encode() for _ in range(2): query = md5(query).hexdigest() query = bytes([int(query[i:i + 2], 16) for i in range(0, len(query), 2)]) data = json.dumps(data, separators=(',', ':'), ensure_ascii=False).encode() if data else b'' for _ in range(2): data = md5(data).hexdigest() data = bytes([int(data[i:i + 2], 16) for i in range(0, len(data), 2)]) a = b'\x00\x01\x0e' ua = b64encode(cls._0x30492c(a, ua.encode())).decode() ua = md5(ua.encode()).hexdigest() ua = cls._0x485470(ua) t = int(time.time()) fp = 2421646185 # 真实的canvas指纹 arr1 = [ 64, 1 / 256, 1 % 256, 14, query[14], query[15], data[14], data[15], ua[14], ua[15], t >> 24 & 255, t >> 16 & 255, t >> 8 & 255, t >> 0 & 255, fp >> 24 & 255, fp >> 16 & 255, fp >> 8 & 255, fp >> 0 & 255, ] reduce_num = reduce(lambda x, y: int(x) ^ int(y), arr1) arr1.append(reduce_num) arr2 = [int(arr1[i]) for i in range(len(arr1))] garble = cls._0x30492c(b'\xff', bytes(arr2), [2, 255]) m = 'Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=' xb = '' for i in range(0, len(garble), 3): a, b, c = garble[i], garble[i + 1], garble[i + 2] base_num = c | b << 8 | a << 16 c1 = m[(base_num & 16515072) >> 18] c2 = m[(base_num & 258048) >> 12] c3 = m[(base_num & 4032) >> 6] c4 = m[(base_num & 63)] xb += ''.join([c1, c2, c3, c4]) return xb @classmethod def get_full_query(cls, ua: str, extra_data: Dict[str, Any]) -> Dict[str, Any]: ms_token = b64encode(bytes([randint(0, 255) for _ in range(94)])).decode() ms_token = ms_token.replace('+', '-').replace('/', '_').rstrip('=') data = { 'device_platform': 'webapp', 'aid': '6383', 'channel': 'channel_pc_web', 'pc_client_type': '1', 'version_code': '190500', 'version_name': '19.5.0', 'cookie_enabled': 'true', 'platform': 'PC', 'msToken': ms_token, } data.update(extra_data) query = urlencode(data, safe='=') x_bogus = cls.calc_x_bogus(ua=ua, query=query, data=None) data.update({'X-Bogus': x_bogus}) return data @classmethod def get_cookie(cls): ttwid = choice(cls.ttwid_list) return f'ttwid={ttwid}'