commit b2878ae893e52f55893e2c3383b06a4e143f0740 Author: 3202307908 <3202307908@qq.com> Date: Fri May 9 14:26:18 2025 +0800 上传文件至 / diff --git a/直播弹幕获取.py b/直播弹幕获取.py new file mode 100644 index 0000000..9b507e5 --- /dev/null +++ b/直播弹幕获取.py @@ -0,0 +1,805 @@ +import os +import re +import time +import pickle +import requests +from datetime import datetime +import logging +import random +import websocket +import json +from urllib.parse import unquote +import gzip +from protobuf.douyin import * +import execjs +import string +import hashlib +import urllib.parse +from threading import Thread + + +session = requests.session() + + +def load_cookies_from_file(filename): + # 以二进制写入模式打开文件 + + with open(filename, "rb") as f: + return pickle.load(f) + + +# if os.path.isfile(f'{wjm}.pkl'): +# print(f'{wjm}.pkl文件存在') + + +def zhengchang_ua(i): + # return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.42' + if i == 0: + first_num = random.randint(55, 62) + third_num = random.randint(0, 3200) + fourth_num = random.randint(0, 140) + os_type = [ + "(Linux; Android 10; MI 9 Build/QKQ1.190825.002; wv)", + "(Linux; Android 8.1.0; PBCM30 Build/OPM1.171019.011; wv)", + "(Linux; Android 6.0; HUAWEI MLA-AL10 Build/HUAWEIMLA-AL10; wv)", + "(Linux; Android 6.0.1; OPPO A57t Build/MMB29M; wv)", + "(iPhone; CPU iPhone OS 10_3_3 like Mac OS X)", + "(Linux; U; Android 9; zh-cn; Redmi 7 Build/PKQ1.181021.001)", + "(Linux; Android 9; V1914A Build/PKQ1.181016.001; wv)", + "(Linux; U; Android 10; zh-CN; ALP-AL00 Build/HUAWEIALP-AL00)", + "(Linux; Android 10; VOG-TL00 Build/HUAWEIVOG-TL00; wv)", + "(Linux; Android 9; Nokia X7 Build/PPR1.180610.011; wv)", + "(Linux; Android 9.0; S5 Build/PKQ1.190319.001; wv)", + "(Linux; Android 10; PCT-AL10 Build/HUAWEIPCT-AL10; wv)", + "(Linux; Android 10; PCT-AL10 Build/HUAWEIPCT-AL10; wv)", + "(Linux; Android 9; Mi9 Pro 5G Build/PKQ1.190714.001; wv)", + "(iPhone; CPU iPhone OS 13_4 like Mac OS X)", + "(Linux; Android 10; OXF-AN00 Build/HUAWEIOXF-AN00; wv)", + "(Linux; U; Android 9; zh-cn; V1814A Build/PKQ1.180819.001)", + "(Linux; U; Android 7.0; zh-cn; Redmi Note 4X Build/NRD90M)", + "(Linux; Android 9; RMX1991 Build/PKQ1.190630.001; wv)", + "(Linux; Android 10; JNY-AL10 Build/HUAWEIJNY-AL10; wv)", + "(Linux; Android 10; ELE-AL00 Build/HUAWEIELE-AL00; wv)", + "(Linux; Android 9; RMX1991 Build/PKQ1.190630.001; wv)", + "(Linux; U; Android 10; zh-cn; MI 8 Build/QKQ1.190828.002)", + "(Linux; Android 10; VCE-AL00 Build/HUAWEIVCE-AL00; wv)", + "(Linux; Android 8.0.0; AUM-TL20 Build/HONORAUM-TL20; wv)", + "(Linux; Android 10; ALP-AL00 Build/HUAWEIALP-AL00; wv)", + "(Linux; U; Android 10; zh-cn; MI 8 SE Build/QKQ1.190828.002)", + "(Linux; Android 7.1.1; OPPO A83 Build/N6F26Q; wv)", + "(Linux; Android 10; VOG-AL10 Build/HUAWEIVOG-AL10; wv)", + "(iPhone; CPU iPhone OS 13_3_1 like Mac OS X)", + "(Linux; U; Android 10; zh-cn; MI 9 Build/QKQ1.190825.002)", + "(Linux; Android 9; INE-TL00 Build/HUAWEIINE-TL00; wv)", + "(Linux; Android 9; ONEPLUS A6000 Build/PKQ1.180716.001; wv)", + "(Linux; U; Android 9; zh-cn; Mi9 Pro 5G Build/PKQ1.190714.001)", + "(Linux; Android 8.1.0; OPPO R11 Plus Build/OPM1.171019.011; wv)", + "(Linux; Android 8.1.0; COR-AL00 Build/HUAWEICOR-AL00; wv)", + "(Linux; Android 7.1.2; M15 Build/N2G47H; wv)", + "(Linux; arm_64; Android 7.0; LG-H870DS)", + "(Linux; U; Android 9; zh-cn; MI CC9 Pro Build/PKQ1.190302.001)", + "(Linux; Android 10; SM-G9730 Build/QP1A.190711.020; wv)", + "(Linux; Android 7.1.2; Hisense A2T Build/N2G47H; wv)", + "(Linux; U; Android 8.1.0; zh-CN; 16th Plus Build/OPM1.171019.026)", + "(Linux; U; Android 9; zh-cn; MI 8 Lite Build/PKQ1.181007.001)", + "(Linux; Android 9; PAAM00 Build/PKQ1.190414.001; wv)", + "(iPhone; CPU iPhone OS 10_1_1 like Mac OS X)", + "(iPhone; CPU iPhone OS 11_4_1 like Mac OS X)", + "(Linux; Android 9; V1914A Build/PKQ1.181016.001; wv)", + "(Linux; Android 10; PCT-AL10 Build/HUAWEIPCT-AL10; wv)", + ] + chrome_version = "Chrome/{}.0.{}.{}".format(first_num, third_num, fourth_num) + + ua = " ".join( + [ + "Mozilla/5.0", + random.choice(os_type), + "AppleWebKit/537.36", + "(KHTML, like Gecko)", + chrome_version, + "Safari/537.36", + ] + ) + return ua + elif i == 1: + os_type = [ + "(Windows NT 6.1; WOW64)", + "(Windows NT 6.0; WOW64)", + "(Windows NT 6.2; WOW64)", + "(Windows NT 6.3; WOW64)", + "(Windows NT 5.1; WOW64)", + "(Windows NT 5.0; WOW64)", + "(Windows NT 5.2; WOW64)", + "(Windows NT 5.3; WOW64)", + "(Windows NT 10.1; WOW64)", + "(Windows NT 10.0; WOW64)", + "(Windows NT 10.2; WOW64)", + "(Windows NT 10.3; WOW64)", + "(Windows NT 6.1; WOW64; X64)", + "(Windows NT 6.0; WOW64; X64)", + "(Windows NT 6.2; WOW64; X64)", + "(Windows NT 6.3; WOW64; X64)", + "(Windows NT 5.1; WOW64; X64)", + "(Windows NT 5.0; WOW64; X64)", + "(Windows NT 5.2; WOW64; X64)", + "(Windows NT 5.3; WOW64; X64)", + "(Windows NT 10.1; WOW64; X64)", + "(Windows NT 10.0; WOW64; X64)", + "(Windows NT 10.2; WOW64; X64)", + "(Windows NT 10.3; WOW64; X64)", + "(Windows NT 6.1)", + "(Windows NT 6.0)", + "(Windows NT 6.2)", + "(Windows NT 6.3)", + "(Windows NT 5.1)", + "(Windows NT 5.0)", + "(Windows NT 5.2)", + "(Windows NT 5.3)", + "(Windows NT 10.1)", + "(Windows NT 10.0)", + "(Windows NT 10.2)", + "(Windows NT 10.3)", + ] + banben = ( + str(random.randint(47, 170)) + + ".0." + + str(random.randint(2000, 2755)) + + "." + + str(random.randint(0, 25)) + ) + + ua = " ".join( + [ + "Mozilla/5.0", + random.choice(os_type), + "AppleWebKit/537.36", + "(KHTML, like Gecko)", + "Chrome/" + banben, + "Safari/537.36", + ] + ) + return ua + elif i == 2: + os_type = [ + "(Macintosh; U; PPC Mac OS X 10_5_8; ja-jp)", + "(Windows; U; Windows NT 6.1; cs-CZ)", + "(Windows; U; Windows NT 6.0; tr-TR)", + "(Windows; U; Windows NT 5.1; ru-RU)", + "(Windows; U; Windows NT 6.0; tr-TR)", + "(Macintosh; Intel Mac OS X 10_6_8)", + "(Windows; U; Windows NT 6.1; zh-HK)", + "(Windows; U; Windows NT 6.0; ja-JP)", + "(Windows; U; Windows NT 6.0; hu-HU)", + "(Macintosh; U; Intel Mac OS X 10_6_6; de-de)", + "(Windows; U; Windows NT 6.1; ko-KR)", + "(Macintosh; Intel Mac OS X 10_7_3)", + "(Macintosh; U; PPC Mac OS X 10_5_8; ja-jp)", + "(Windows; U; Windows NT 6.1; sv-SE)", + "(Macintosh; U; Intel Mac OS X 10_6_6; fr-ch)", + "(Windows; U; Windows NT 6.1; de-DE)", + "(Windows; U; Windows NT 6.0; de-DE)", + "(Macintosh; U; Intel Mac OS X 10_5_8; zh-cn)", + "(Macintosh; U; Intel Mac OS X 10_6_6; ja-jp)", + "(Windows; U; Windows NT 6.0; en-US)", + ] + banben = ( + str(random.randint(533, 534)) + + "." + + str(random.randint(18, 21)) + + "." + + str(random.randint(1, 27)) + ) + chrome_version = [ + "Version/5.0.3", + "Version/5.0.4", + "Version/5.0.2", + "Version/7.0.3", + "Version/6.0 Mobile/10A5355d", + "Version/5.1.7", + "Version/5.1.6", + "Version/5.1.3", + "Version/5.1.4", + "Version/5.1.2", + "Version/5.1.3", + ] + + ua = " ".join( + [ + "Mozilla/5.0", + random.choice(os_type), + "AppleWebKit/" + banben, + "(KHTML, like Gecko)", + random.choice(chrome_version), + "Safari/" + banben, + ] + ) + return ua + elif i == 3: + os_type = [ + "(Windows NT 10.0; Win64; x64; rv:", + "(Windows NT 6.3; Win64; x64; rv:", + "(Windows NT 6.2; Win64; x64; rv:", + "(Windows NT 10; rv:", + "(Windows NT 6.3; rv:", + "(Windows NT 6.2; rv:", + "(Windows NT 6.1; rv:", + "(Windows NT 6.1; Win64; x64; rv:", + "(X11; Linux i586; rv:", + "(Windows NT 10.0; Win64; x64; rv:", + ] + banben = str(random.randint(20, 50)) + ".0" + chrome_version = [ + "Gecko/20100101 Firefox/", + "Gecko/20130405 Firefox/", + "Gecko/20130330 Firefox/", + "Gecko/20130430 Firefox/", + "Gecko/20140230 Firefox/", + "Gecko/20140630 Firefox/", + "Gecko/20140512 Firefox/", + "Gecko/20140712 Firefox/", + "Gecko/20100502 Firefox/", + ] + + ua = " ".join( + [ + "Mozilla/5.0", + random.choice(os_type) + banben + ")", + random.choice(chrome_version) + banben, + ] + ) + return ua + elif i == 4: + os_type = [ + "(Macintosh; U; Intel Mac OS X 10_6_7; ", + "(Macintosh; U; Intel Mac OS X 10_6_5; ", + "(Windows; U; Windows NT 6.1; ", + "(Macintosh; U; Intel Mac OS X 10_6_6; ", + "(Windows; U; Windows NT 6.0; ", + "(Windows; U; Windows NT 6.1; ", + "(Windows; U; Windows NT 5.1; ", + "(Macintosh; U; Intel Mac OS X 10_6_6; ", + "(Macintosh; U; PPC Mac OS X 10_5_8;", + "(Macintosh; U; Intel Mac OS X 10_6_7;", + "(Macintosh; U; Intel Mac OS X 10_6_5;", + "(Macintosh; U; Intel Mac OS X 10_6_6;", + ] + diqu = [ + "ja-jp)", + "de-de)", + "de-DE)", + "fr-ch)", + "en-US)", + "cs-CZ)", + "it-it)", + "ko-kr)", + "da-dk)", + "ar)", + "hu-HU)", + "ko-KR)", + "es-es)", + "zh-HK)", + "en-gb)", + ] + banben = ( + str(random.randint(532, 535)) + + "." + + str(random.randint(19, 55)) + + "." + + str(random.randint(1, 25)) + ) + + ua = " ".join( + [ + "Mozilla/5.0", + random.choice(os_type) + random.choice(diqu), + "AppleWebKit/" + banben, + "(KHTML, like Gecko)", + "Version/5.0." + str(random.randint(1, 5)), + "Safari/" + banben, + ] + ) + return ua + elif i == 5: + os_type = [ + "(Windows; U; Windows NT 5.1", + "(Macintosh; U; Intel Mac OS X 10_6_6", + "(Macintosh; U; PPC Mac OS X 10_5_8", + "(Macintosh; U; Intel Mac OS X 10_6_7", + "(Macintosh; U; Intel Mac OS X 10_6_5", + "(Macintosh; U; Intel Mac OS X 10_6_6", + "(Macintosh; AMD Mac OS X 10_8_2", + "(Macintosh; Intel Mac OS X 10_10_1", + "(Macintosh; Intel Mac OS X 10_5_8", + "(Macintosh; Intel Mac OS X 10_6_0", + "(Macintosh; Intel Mac OS X 10_6_2", + "(Macintosh; Intel Mac OS X 10_6_3", + "(Macintosh; Intel Mac OS X 10_6_4", + "(Macintosh; Intel Mac OS X 10_6_5", + "(Macintosh; Intel Mac OS X 10_6_6", + "(Macintosh; Intel Mac OS X 10_6_7", + "(Macintosh; Intel Mac OS X 10_6_8", + "(Macintosh; Intel Mac OS X 10_7_0", + "(Macintosh; Intel Mac OS X 10_7_1", + "(Macintosh; Intel Mac OS X 10_7_2", + "(Macintosh; Intel Mac OS X 10_7_3", + "(Macintosh; Intel Mac OS X 10_7_4", + "(Macintosh; Intel Mac OS X 10_7_5", + "(Macintosh; Intel Mac OS X 10_8_0", + "(Macintosh; Intel Mac OS X 10_8_1", + "(Macintosh; Intel Mac OS X 10_8_2", + "(Macintosh; Intel Mac OS X 10_8_3", + "(Macintosh; Intel Mac OS X 10_9_0", + "(Macintosh; Intel Mac OS X 10_9_1", + "(Macintosh; Intel Mac OS X 10_9_2", + "(Macintosh; Intel Mac OS X 10_9_3", + "(Macintosh; PPC Mac OS X 10_6_7", + "(Macintosh; U; Intel Mac OS X 10_5_6", + "(Macintosh; U; Intel Mac OS X 10_5_7", + "(Macintosh; U; Intel Mac OS X 10_5_8", + "(Macintosh; U; Intel Mac OS X 10_5_9", + "(Macintosh; U; Intel Mac OS X 10_6_0", + "(Macintosh; U; Intel Mac OS X 10_6_1", + "(Macintosh; U; Intel Mac OS X 10_6_2", + "(Macintosh; U; Intel Mac OS X 10_6_3", + "(Macintosh; U; Intel Mac OS X 10_6_4", + "(Macintosh; U; Intel Mac OS X 10_6_5", + "(Macintosh; U; Intel Mac OS X 10_6_6", + "(Macintosh; U; Intel Mac OS X 10_6_7", + "(Macintosh; U; Intel Mac OS X 10_6_8", + "(Macintosh; U; Intel Mac OS X 10_8", + ] + diqu = [ + "; ja-jp)", + "; e-de)", + "; de-DE)", + "; fr-ch)", + "; en-US)", + "; cs-CZ)", + "; it-it)", + "; ko-kr)", + "; da-dk)", + "; ar)", + "; hu-HU)", + "; ko-KR)", + "; es-es)", + "; zh-HK)", + "; en-gb)", + "; id-ID)", + ] + banben = ( + str(random.randint(500, 535)) + + "." + + str(random.randint(0, 55)) + + "." + + str(random.randint(0, 25)) + ) + + ua = " ".join( + [ + "Mozilla/5.0", + random.choice(os_type) + random.choice(diqu), + "AppleWebKit/" + banben, + "(KHTML, like Gecko)", + "Version/5.0." + str(random.randint(1, 9)), + "Safari/" + banben, + ] + ) + return ua + else: + os_type = [ + "(iPhone; U; CPU iPhone OS 4_3_1 like Mac OS X", + "(iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X", + "(iPhone; U; CPU iPhone OS 4_3 like Mac OS X", + "(iPhone; U; CPU iPhone OS 4_2_4 like Mac OS X", + "(iPhone; CPU iPhone OS 12_1_4 like Mac OS X", + "(iPhone; CPU iPhone OS 12_0 like Mac OS X", + "(iPhone; CPU iPhone OS 11_2 like Mac OS X", + "(iPhone 6s; CPU iPhone OS 11_4_1 like Mac OS X", + "(iPhone; CPU iPhone OS 10_1 like Mac OS X", + "(iPhone; CPU iPhone OS 11_0_2 like Mac OS X", + "(iPhone; CPU iPhone OS 11_4_1 like Mac OS X", + "(iPhone; CPU iPhone OS 10_1_1 like Mac OS X", + "(iPhone; CPU iPhone OS 13_5_1 like Mac OS X", + "(iPhone; CPU iPhone OS 8_3 like Mac OS X", + "(iPhone; CPU iPhone OS 14_4 like Mac OS X", + "(iPhone; CPU iPhone OS 13_6 like Mac OS X", + "(iPhone; CPU iPhone OS 14_1 like Mac OS X", + "(iPhone; CPU iPhone OS 13_6_1 like Mac OS X", + ] + diqu = [ + "; ja-jp)", + "; e-de)", + "; de-DE)", + "; fr-ch)", + "; en-US)", + "; cs-CZ)", + "; it-it)", + "; ko-kr)", + "; da-dk)", + "; ar)", + "; hu-HU)", + "; ko-KR)", + "; es-es)", + "; zh-HK)", + "; en-gb)", + "; id-ID)", + ] + banben = ( + str(random.randint(500, 535)) + + "." + + str(random.randint(0, 55)) + + "." + + str(random.randint(0, 25)) + ) + + mobi = [ + "Version/6.0 Mobile/10A5355d", + "Version/5.1 Mobile/9B176", + "Version/5.0.2 Mobile/8J2", + "Version/5.0.2 Mobile/8G4", + "Version/5.0.2 Mobile/8C148", + "Version/5.0.2 Mobile/8C148a", + "Version/5.0.2 Mobile/8G4", + "Version/5.0.2 Mobile/8F190", + "Version/4.0.5 Mobile/8B5097d", + "Version/4.0.5 Mobile/8B117", + "Version/4.0.4 Mobile/7B314", + "Version/4.0.4 Mobile/7B334b", + "Version/4.0.4 Mobile/7D11", + "Version/4.0.4 Mobile/7B500", + "Version/4.0.4 Mobile/7B360", + "Version/3.1.1 Mobile/5G77", + ] + + ua = " ".join( + [ + "Mozilla/5.0", + random.choice(os_type) + random.choice(diqu), + "AppleWebKit/" + banben, + "(KHTML, like Gecko)", + random.choice(mobi), + "Safari/" + banben, + ] + ) + return ua + + +def generateSignature(wss): + """ + 出现gbk编码问题则修改 python模块subprocess.py的源码中Popen类的__init__函数参数encoding值为 "utf-8" + """ + params = ( + "live_id,aid,version_code,webcast_sdk_version," + "room_id,sub_room_id,sub_channel_id,did_rule," + "user_unique_id,device_platform,device_type,ac," + "identity" + ).split(",") + wss_params = urllib.parse.urlparse(wss).query.split("&") + wss_maps = {i.split("=")[0]: i.split("=")[-1] for i in wss_params} + tpl_params = [f"{i}={wss_maps.get(i, '')}" for i in params] + param = ",".join(tpl_params) + md5 = hashlib.md5() + md5.update(param.encode()) + md5_param = md5.hexdigest() + + # with codecs.open('D:\job_demo\demo\day04\h_sign.js', 'r', encoding='utf8') as f: + # script = f.read() + + # ctx = MiniRacer() + # ctx.eval(script) + + with open("D:\job_demo\demo\day04\h_sign.js", "r", encoding="utf-8") as f: + js_code = f.read() + ctx = execjs.compile(js_code) + + try: + signature = ctx.call("get_sign", md5_param) + return signature + except Exception as e: + print(e) + + +def generateMsToken(length=107): + """ + 产生请求头部cookie中的msToken字段,其实为随机的107位字符 + :param length:字符位数 + :return:msToken + """ + random_str = "" + base_str = string.ascii_letters + string.digits + "=_" + _len = len(base_str) - 1 + for _ in range(length): + random_str += base_str[random.randint(0, _len)] + return random_str + + +class DouyinLiveWebFetcher: + def __init__(self, live_url, roomId, Cookie): + self.id_dict = {} + self.Cookies = Cookie + self.__ttwid = None + self.room_id = roomId + self.live_url = live_url + self.user_agent = zhengchang_ua(1) + + def start(self): + self._connectWebSocket() + + def _connectWebSocket(self): + """ + 连接抖音直播间websocket服务器,请求直播间数据 + """ + wss = ( + "wss://webcast5-ws-web-hl.douyin.com/webcast/im/push/v2/?app_name=douyin_web" + "&version_code=180800&webcast_sdk_version=1.0.14-beta.0" + "&update_version_code=1.0.14-beta.0&compress=gzip&device_platform=web&cookie_enabled=true" + "&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32" + "&browser_name=Mozilla" + "&browser_version=5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML," + "%20like%20Gecko)%20Chrome/126.0.0.0%20Safari/537.36" + "&browser_online=true&tz_name=Asia/Shanghai" + "&cursor=d-1_u-1_fh-7392091211001140287_t-1721106114633_r-1" + f"&internal_ext=internal_src:dim|wss_push_room_id:{self.room_id}|wss_push_did:7319483754668557238" + f"|first_req_ms:1721106114541|fetch_time:1721106114633|seq:1|wss_info:0-1721106114633-0-0|" + f"wrds_v:7392094459690748497" + f"&host=https://live.douyin.com&aid=6383&live_id=1&did_rule=3&endpoint=live_pc&support_wrds=1" + f"&user_unique_id=7319483754668557238&im_path=/webcast/im/fetch/&identity=audience" + f"&need_persist_msg_count=15&insert_task_id=&live_reason=&room_id={self.room_id}&heartbeatDuration=0" + ) + + signature = generateSignature(wss) + wss += f"&signature={signature}" + + headers = { + "cookie": f"ttwid={self.ttwid}", + "user-agent": self.user_agent, + } + self.ws = websocket.WebSocketApp( + wss, + header=headers, + on_open=self._wsOnOpen, + on_message=self._wsOnMessage, + on_error=self._wsOnError, + on_close=self._wsOnClose, + ) + # 运行websocket连接,并处理可能的异常 + try: + self.ws.run_forever() + except websocket.WebSocketConnectionClosedException as e: + logging.error("_connectWebSocket--WebSocket连接已关闭: %s", e) + self.stop() + except websocket.WebSocketException as e: + logging.error("_connectWebSocket--WebSocket异常: %s", e) + self.stop() + except Exception as e: + logging.error("_connectWebSocket--发生未知异常: %s", e) + self.stop() + raise # 重新抛出异常,以便上层处理 + + def _wsOnOpen(self, ws): + """ + 连接建立成功 + """ + logging.info( + f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}----直播间链接成功" + ) + + def heartbeat(ws: websocket.WebSocketApp): + """发送心跳消息的定时任务""" + while True: + if ws.keep_running: # 检查WebSocket连接是否仍在运行 + ws.send("hi") # 发送自定义的心跳消息 + time.sleep(15) # 等待指定的时间间隔 + else: + break + + heartbeat_thread = Thread(target=heartbeat, args=(ws,)) + heartbeat_thread.daemon = True # 设置为守护线程 + heartbeat_thread.start() + + def _wsOnMessage(self, ws: websocket.WebSocketApp, message: str): + """ + 接收到数据 + :param ws: websocket实例 + :param message: 数据 + """ + # while True: + # 根据proto结构体解析对象 + + if message: + try: + package = PushFrame().parse(message) + response = Response().parse(gzip.decompress(package.payload)) + + # 返回直播间服务器链接存活确认消息,便于持续获取数据 + if response.need_ack: + ack = PushFrame( + log_id=package.log_id, + payload_type="ack", + payload=response.internal_ext.encode("utf-8"), + ).SerializeToString() + ws.send(ack, websocket.ABNF.OPCODE_BINARY) + + # 根据消息类别解析消息体 + for msg in response.messages_list: + method = msg.method + try: + { + "WebcastChatMessage": self._parseChatMsg, # 聊天消息 + "WebcastGiftMessage": self._parseGiftMsg, # 礼物消息 + "WebcastLikeMessage": self._parseLikeMsg, # 点赞消息 + "WebcastMemberMessage": self._parseMemberMsg, # 进入直播间消息 + "WebcastSocialMessage": self._parseSocialMsg, # 关注消息 + "WebcastRoomUserSeqMessage": self._parseRoomUserSeqMsg, # 直播间统计 + "WebcastFansclubMessage": self._parseFansclubMsg, # 粉丝团消息 + "WebcastControlMessage": self._parseControlMsg, # 直播间状态消息 + "WebcastEmojiChatMessage": self._parseEmojiChatMsg, # 聊天表情包消息 + "WebcastRoomStatsMessage": self._parseRoomStatsMsg, # 直播间统计信息 + "WebcastRoomMessage": self._parseRoomMsg, # 直播间信息 + "WebcastRoomRankMessage": self._parseRankMsg, # 直播间排行榜信息 + }.get(method)(msg.payload) + except Exception as e: + pass + # logging.error(f"_wsOnMessage--error occurred: {e}") + + except Exception as e: + logging.error(f"_wsOnMessage--error occurred: {e}") + + def _wsOnError(self, ws, error): + # print("WebSocket error: ", error) + logging.info("WebSocket error: ", error) + + def _wsOnClose(self, ws: websocket.WebSocketApp, *args): + logging.info(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}----直播间断开") + + def _parseChatMsg(self, payload): + """聊天消息""" + message = ChatMessage().parse(payload) + user_name = message.user.nick_name # 用户名称 + user_id = message.user.id # 用户id + user_sid = message.user.sec_uid # 用户主页uid + display_id = message.user.display_id # 用户抖音ID + content = message.content # 用户发送的信息 + print( + f"聊天【msg】- 用户名称:{user_name} - 用户id:{user_id} - 用户主页uid:{user_sid} - 用户抖音ID:{display_id}-用户发送的信息:{content}" + ) + + def _parseGiftMsg(self, payload): + """礼物消息""" + message = GiftMessage().parse(payload) + user_name = message.user.nick_name # 用户名称 + gift_name = message.gift.name # 礼物信息 + gift_cnt = message.combo_count # 礼物数量 + print(f"【礼物msg】-{user_name} - 送出了 {gift_name}x{gift_cnt}") + + def _parseLikeMsg(self, payload): + """点赞消息""" + message = LikeMessage().parse(payload) + user_name = message.user.nick_name # 用户名称 + count = message.count # 点赞次数 + print(f"【点赞msg】- {user_name} - 点了{count}个赞") + + def _parseMemberMsg(self, payload): + """进入直播间信息""" + message = MemberMessage().parse(payload) + following_coun = message.user.follow_info.following_count # 用户关注数 + follower_count = message.user.follow_info.follower_count # 用户粉丝数 + user_name = message.user.nick_name # 用户名称 + user_sid = message.user.sec_uid # 用户主页uid + user_id = message.user.id # 用户id + display_id = message.user.display_id # 用户抖音ID + gender = ['未知',"男", "女"][message.user.gender] # 用户性别 + print( + f"【进场msg】- 用户名称:{user_name} - 用户id:{user_id} - 用户uid:{user_sid} - 性别:{gender} 进入了直播间" + ) + + def _parseSocialMsg(self, payload): + """关注消息""" + message = SocialMessage().parse(payload) + user_name = message.user.nick_name # 用户名称 + user_sid = message.user.sec_uid # 用户主页uid + user_id = message.user.id # 用户id + display_id = message.user.display_id # 用户抖音ID + # 分享 + if message.follow_count == 0: + print(f"【分享msg】- 用户名称:{user_name} - 用户id:{user_id} 分享了主播") + # 关注 + else: + print(f"【分享msg】- 用户名称:{user_name} - 用户id:{user_id} 关注了主播") + + def _parseRoomUserSeqMsg(self, payload): + """直播间统计""" + message = RoomUserSeqMessage().parse(payload) + current = message.total # 当前观看人数 + total = message.total_pv_for_anchor # 累计观看人数 + print(f"【统计msg】- 当前观看人数: {current} - 累计观看人数: {total}") + + def _parseFansclubMsg(self, payload): + """粉丝团消息""" + message = FansclubMessage().parse(payload) + content = message.content # 内容 + print(f"【粉丝团msg】 {content}") + + def _parseControlMsg(self, payload): + """直播间状态消息""" + message = ControlMessage().parse(payload) + + if message.status == 3: + print("直播间已结束") + print(f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}----直播间已结束') + self.stop() + + def _parseEmojiChatMsg(self, payload): + """聊天表情包消息""" + message = EmojiChatMessage().parse(payload) + emoji_id = message.emoji_id # 表情包id + user = message.user # 用户 + common = message.common # 通用信息 + default_content = message.default_content # 默认内容 + print( + f"【聊天表情包id】- {emoji_id} - user:{user} - common:{common} - default_content:{default_content}" + ) + + def _parseRoomStatsMsg(self, payload): + """直播间统计msg""" + message = RoomStatsMessage().parse(payload) + display_long = message.display_long # 直播间统计信息 + print(f"【直播间统计msg】- {display_long}") + + def _parseRoomMsg(self, payload): + """直播间msg""" + message = RoomMessage().parse(payload) + common = message.common + room_id = common.room_id # 直播间id + print(f"【直播间msg】- 直播间id:{room_id}") + + def _parseRankMsg(self, payload): + """直播间排行榜msg""" + message = RoomRankMessage().parse(payload) + ranks_list = message.ranks_list + print(f"【直播间排行榜msg】{ranks_list}") + + @property + def ttwid(self): + """ + 产生请求头部cookie中的ttwid字段,访问抖音网页版直播间首页可以获取到响应cookie中的ttwid + :return: ttwid + """ + if self.__ttwid: + return self.__ttwid + headers = { + "User-Agent": self.user_agent, + } + try: + response = requests.get( + "https://live.douyin.com/", headers=headers, verify=False + ) + response.raise_for_status() + except Exception as e: + logging.error(f"ttwid--error occurred: {e}") + + else: + self.__ttwid = response.cookies.get("ttwid") + return self.__ttwid + + +if __name__ == "__main__": + user_name = "" + with open(r"D:\job_demo\demo\测试\cookie_name.txt", "r", encoding="utf-8") as f: + + user_name = f.read().split("|")[1].strip() + Cookie = load_cookies_from_file( + r"D:\job_demo\demo\测试\MS4wLjABAAAAc9m4vmspNsSiYP348XRC-OLVqSXRn8Rdtl618fbzYag.pkl" + ) + # print(Cookie) + # print(type(Cookie)) + cookies_dict = {c.name: c.value for c in Cookie} + cookie_str = "; ".join(f"{k}={v}" for k, v in cookies_dict.items()) + + + + live_url = 'https://live.douyin.com/80017709309' + roomId = '7502218243671739173' + DouyinLiveWebFetcher(live_url,roomId,cookie_str).start() + + \ No newline at end of file