2024 年最新基于 Python 实现抖音直播间逆向弹幕评论消息监听

博客仅供学习研究特别声明

特别声明:本博客【2024 年最新基于 Python 实现抖音直播间逆向弹幕评论消息监听(仅供学习研究)】的所有源码和内容均仅供学习研究,严禁用于包括但不限于商业谋利、破坏系统、盗取个人信息等不良不法行为,违反此声明使用所产生的一切后果均由违反声明使用者承担。

博主有话说:博客的所有源码和内容均仅供学习研究,若是博客学习上有问题通过 CSDN 私信博主即可。完整数据帧解包 Protobuf 通过 CSDN 私信博主获取(仅供学习研究)。

特别注意:附加内容是通过 websocket-client 连接(待更新)

弹幕监听项目结构

douyin_proto.py 	解析消息
main.py 			主函数(监听 websocket 数据帧)

解析 websocket 数据帧(解包)

定义了一组用于解析二进制协议消息的 dataclass,通过 betterproto 库将协议消息映射为 Python 数据类。

HeadersList 类表示头部键值对,PushFrame 类封装了一个二进制有效载荷,Message 类包含消息的基本信息如方法、ID 和类型,Response 类表示响应消息,包含多个 Message 对象,User 类表示用户的基本信息(如 ID 和昵称),而 ChatMessage 类表示聊天消息,包含用户信息和消息内容。
douyin_proto.py

from dataclasses import dataclass
from typing import Dict, List

import betterproto


@dataclass
class HeadersList(betterproto.Message):
    key: str = betterproto.string_field(1)
    value: str = betterproto.string_field(2)


@dataclass
class PushFrame(betterproto.Message):
    payload: bytes = betterproto.bytes_field(8)


@dataclass
class Message(betterproto.Message):
    method: str = betterproto.string_field(1)
    payload: bytes = betterproto.bytes_field(2)
    msg_id: int = betterproto.int64_field(3)
    msg_type: int = betterproto.int32_field(4)


@dataclass
class Response(betterproto.Message):
    messages_list: List["Message"] = betterproto.message_field(1)


@dataclass
class User(betterproto.Message):
    id: int = betterproto.uint64_field(1)
    nick_name: str = betterproto.string_field(3)


@dataclass
class ChatMessage(betterproto.Message):
    user: "User" = betterproto.message_field(2)
    content: str = betterproto.string_field(3)

安装 pyppeteer 第三方库

pyppeteer 是一个 Python 库,提供了对 Puppeteer 的 Python 绑定,Puppeteer 是一个用于控制无头 Chrome 浏览器的 Node.js 库。pyppeteer 使得我们能够在 Python 中使用类似的功能来控制浏览器,进行自动化测试、抓取网页内容、截图、生成 PDF 等操作。

pyppeteer 安装方法

pip install pyppeteer

常见问题

在安装 pyppeteer 时,pyppeteer 会自动下载 Chrome 浏览器的版本(大约 100MB+)。但是博主发现 pyppeteer 自动下载 Chrome 浏览器会经常发生报错,所以我们可以手动下载谷歌浏览器,手动配置 executablePath 即可。

安装 chrome 浏览器

安装 chrome 浏览器(包含 chrome.exe 可执行文件)

chrome.exe:C:\Program Files\Google\Chrome\Application\chrome.exe

在这里插入图片描述

抖音直播间弹幕监听

使用 pyppeteer 库启动一个 Chrome 浏览器实例,打开指定的抖音直播间页面,并监听 WebSocket 数据帧。当接收到 WebSocket 帧时,脚本会解码并分析其中的消息内容,特别是聊天消息(WebcastChatMessage),然后提取消息发送者的昵称、UID(隐藏中间 8 位)和消息内容,并打印在控制台中。通过不断等待直到 stop_listening 为 True,脚本保持监听状态,直到满足停止条件后关闭浏览器。

import gzip
import base64
import asyncio
import traceback

from pyppeteer import launch
from douyin_proto import *

executable_path = r'C:\Program Files\Google\Chrome\Application\chrome.exe'
stop_listening = False


def hide_uid(uid: int):
    uid_str = str(uid)
    if len(uid_str) > 8:
        masked_uid = uid_str[:4] + "********" + uid_str[-4:]
    else:
        masked_uid = uid_str
    return masked_uid


def analysis(decoded_bytes):
    try:
        package = PushFrame().parse(decoded_bytes)
        response = Response().parse(gzip.decompress(package.payload))
        for msg in response.messages_list:
            if msg.method == 'WebcastChatMessage':
                ret = ChatMessage().parse(msg.payload)
                user_name = ret.user.nick_name
                user_id = ret.user.id
                content = ret.content
                masked_user_id = hide_uid(user_id)
                print(f"【弹幕】[UID: {masked_user_id}]{user_name}: {content}")
    except Exception as err:
        print(f"Error: {err}")
        traceback.print_exc(err)


async def listen_debugger():
    browser = await launch(executablePath=executable_path, headless=False)
    page = await browser.newPage()
    session = await page.target.createCDPSession()

    await session.send('Network.enable')

    def on_request(request):
        decoded_data = base64.b64decode(request['response']['payloadData'])
        analysis(decoded_data)

    session.on('Network.webSocketFrameReceived', on_request)
    
	room_id = "【直播间ID】"
	
    await page.goto(f'https://live.douyin.com/{room_id}')

    task = asyncio.create_task(wait_for_condition(stop_listening))
    await task

    print("Condition met, closing browser...")
    await browser.close()


async def wait_for_condition(stop_listening):
    while not stop_listening:
        await asyncio.sleep(1)


asyncio.run(listen_debugger())

弹幕监听:程序运行结果

设置 main.py 脚本 room_id = "777528469683" 执行运行 main.py 程序

python main.py

运行截图

自动打开浏览器(显示 Chrome 正受到自动测试软件的控制)

在这里插入图片描述

pycharm 控制台截图

注意:博主进行了 UID 数据隐藏(在非匿名的直播间可以获取用户的 UID 数据)

在这里插入图片描述

附加:消息事件监听

演示了弹幕消息类型的监听,事件上直播间互动消息还包括其他数据类型,这边只是简化提取了弹幕类型的数据。其他消息事件类型需要相应 proto 进行解包操作。(待更新·····)

附加:websocket-client 监听消息

通过 websocket-client 直接连接直播间地址同样能获取直播间的数据帧,同样进行数据帧的解包处理,可以获取解析真实数据。

在这里插入图片描述

signature 签名数据

在这里插入图片描述

附加:定位 new Websocket

通过开发者工具查询 websocket 连接启动器

在这里插入图片描述

定位【创建 new Websocket 连接】源码

在这里插入图片描述

需要创建 websocket 连接,通过查看源码,我们可以知道连接 websocket 需要的数据是通过 getSocketParams 返回的,所以需要定位【getSocketParams 函数】源码。

在这里插入图片描述

定位【getSocketParams 函数】源码

在这里插入图片描述

在这里插入图片描述

定位【创建 signature 函数】源码

在这里插入图片描述

附加:MD5 算法解析 X-MS-STUB

在这里插入图片描述

o = ",live_id=1,aid=6383,version_code=180800,webcast_sdk_version=1.0.14-beta.0,room_id=7444732087904635711,sub_room_id=,sub_channel_id=,did_rule=3,user_unique_id=7223996956878915084,device_platform=web,device_type=,ac=,identity=audience"

o.substring(1) = "live_id=1,aid=6383,version_code=180800,webcast_sdk_version=1.0.14-beta.0,room_id=7444732087904635711,sub_room_id=,sub_channel_id=,did_rule=3,user_unique_id=7223996956878915084,device_platform=web,device_type=,ac=,identity=audience"

MD5 验证

import hashlib
input_string = "live_id=1,aid=6383,version_code=180800,webcast_sdk_version=1.0.14-beta.0,room_id=7444732087904635711,sub_room_id=,sub_channel_id=,did_rule=3,user_unique_id=7223996956878915084,device_platform=web,device_type=,ac=,identity=audience"

md5_hash = hashlib.md5(input_string.encode('utf-8'))
md5_result = md5_hash.hexdigest()

print(md5_result)
MD5 验证 65b92687ae0c7cfd888e5a57b7ab99f2

附加:解析 signature 创建函数

在这里插入图片描述
选择在【新标签页打开】获取 webmssdk.es5.js 脚本地址

下载 webmssdk.es5.js 地址:https://lf-c-flwb.bytetos.com/obj/rc-client-security/c-webmssdk/1.0.0.53/webmssdk.es5.js
我们进行 webmssdk.es5.js 脚本处理,通过 ctrl + F 查询,定位注释删除 setTimeout 函数,绑定 create_sign 函数 => function _0x5c2014

注释删除 setTimeout 函数

setTimeout(function () {
	var _0x36b72e = _0x5af634;
	_0x4e6ec1(_0x5aac87[_0x36b72e(0x24f)]());
}, 0x2205 * 0x1 + 0x1d4 * -0x10 + -0x67 * 0x7);

绑定 create_sign 函数 => function _0x5c2014

function _0x5c2014(_0x1fa689) {
    return w_0x5c3140('484e4f4a403f524300362d0a5f00233c0000000029b6a730000000630214000103001400020700001400030700011400041101031100031347000d11010311000313140001450023110103110004134700130211010011010311000413430114000145000607000214000102110101110002021100014303140005110005420003096b1e7e601e606766710c6b1e7e601e63726a7f7c7277200303030303030303030303030303030303030303030303030303030303030303', {
        get 0x0() {
            return _0x5dd467;
        },
        get 0x1() {
            return _0x34c70a;
        },
        0x2: arguments,
        0x3: _0x1fa689
    }, this);
}

create_sign = _0x5c2014

添加 document、windows、naviagator

注意:需要手动进行添加 document、windows、naviagator(通过 node 本地环境执行没有 document、windows、naviagator)

var document = {}
var window = {}
var navigator = {'userAgent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'}
var create_sign

······
已调整 webmssdk.es5.js 源码
······

function get_sign(md5) {
    let data = {
        "X-MS-STUB": md5
    }
    return create_sign(data)["X-Bogus"];
}

console.log(get_sign('65b92687ae0c7cfd888e5a57b7ab99f2'))

node 执行获取签名

node  webmssdk.es5_update.js

附加:待更新······

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唤醒手腕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值