live_danmaku_Obtain/直播弹幕获取.py

805 lines
31 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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()