dy直播间评论保存插件

实时检测dy直播间所有用户评论内容,支持转发到服务器

server.py

python 复制代码
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional
from datetime import datetime

# 初始化应用
app = FastAPI(title="抖音弹幕接收服务")

# === 1. 配置跨域 (CORS) ===
# 必须配置,否则油猴脚本跨域请求会被拦截
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许所有来源
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# === 2. 定义数据模型 (Dict结构) ===
# 对应油猴脚本发送的单条数据结构: { "username": "xxx", "content": "xxx" }
class CommentSchema(BaseModel):
    username: str
    content: str
    ts: Optional[int] = None  # 接收时间戳(可选)

# === 3. 接收接口 ===
@app.post("/api/receive_comments")
async def receive_comments(comments: List[CommentSchema]):
    """
    接收评论列表,FastAPI 会自动将 JSON 解析为对象列表
    """
    now = datetime.now().strftime("%H:%M:%S")
    
    if not comments:
        return {"status": "empty", "count": 0}

    print(f"\n[{now}] 收到 {len(comments)} 条新弹幕:")
    
    # 遍历打印,这里已经自动解析为对象了
    for item in comments:
        # 这里实现了你要的"分开处理用户名和内容"
        print(f"  用户: [{item.username}] 说: {item.content}")

    return {
        "status": "success",
        "received_count": len(comments),
        "timestamp": now
    }

# === 4. 启动入口 ===
if __name__ == "__main__":
    # 端口设置为 5000,与脚本对应
    uvicorn.run(app, host="192.168.2.114", port=8000)

油候脚本

python 复制代码
// ==UserScript==
// @name         抖音直播弹幕采集(Class定位修复版)
// @namespace    http://tampermonkey.net/
// @version      5.0
// @description  使用CSS Class精准定位用户名和内容,彻底修复用户名为空的问题
// @author       You
// @match        https://live.douyin.com/*
// @grant        GM_xmlhttpRequest
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    // === 配置区域 ===
    // 确保这里的 IP 和端口与你的 Python 服务端一致
    const API_URL = "http://192.168.2.114:8000/api/receive_comments";

    // 抖音弹幕容器的 class (最外层)
    const CONTAINER_SELECTOR = '.NkS2Invn';

    // === 关键修改:根据 HTML 源码提取的精准 Class ===
    // 用户名所在的 span class
    const USER_NAME_SELECTOR = '.v8LY0gZF';
    // 评论内容所在的 span class
    const CONTENT_SELECTOR = '.cL385mHb';

    // 去重缓存池
    const dedupCache = new Map();

    console.log(`%c[系统] 采集脚本启动 (Class定位模式)...`, "color: green; font-weight: bold");

    setInterval(() => {
        const batchData = [];
        const now = Date.now();

        // 1. 获取所有弹幕行 (使用 querySelectorAll 更快)
        let messageDivs = document.querySelectorAll(CONTAINER_SELECTOR);

        // 2. 遍历处理
        messageDivs.forEach(div => {
            // DOM级去重:如果该行已经发送过,直接跳过
            if (div.getAttribute('data-sent')) return;

            try {
                // === 核心修复逻辑 ===
                // 直接通过 class 查找用户名和内容,不再依赖位置顺序
                let nameEl = div.querySelector(USER_NAME_SELECTOR);
                let contentEl = div.querySelector(CONTENT_SELECTOR);

                if (nameEl && contentEl) {
                    let uName = nameEl.innerText.trim();
                    let uContent = contentEl.innerText.trim();

                    // === 数据清洗 ===
                    // 1. 去除用户名末尾的中文冒号 ":" 或英文冒号 ":"
                    uName = uName.replace(/[::]$/, '').trim();

                    // 2. 过滤掉无意义的空数据
                    if (!uName || !uContent) return;

                    // === 去重逻辑 ===
                    let key = uName + "|" + uContent;

                    // 检查缓存 (30秒内重复则跳过)
                    if (dedupCache.has(key) && (now - dedupCache.get(key) < 30000)) {
                        div.setAttribute('data-sent', 'true');
                        return;
                    }

                    // === 加入发送队列 ===
                    batchData.push({
                        "username": uName,
                        "content": uContent,
                        "ts": now
                    });

                    // 更新缓存
                    dedupCache.set(key, now);
                    div.setAttribute('data-sent', 'true');
                }
            } catch (e) {
                console.error("解析错误:", e);
            }
        });

        // 3. 发送数据
        if (batchData.length > 0) {
            console.log(`%c[发送] 推送 ${batchData.length} 条数据`, "color: blue", batchData);

            GM_xmlhttpRequest({
                method: "POST",
                url: API_URL,
                headers: { "Content-Type": "application/json" },
                data: JSON.stringify(batchData),
                onload: (res) => {
                    if (res.status !== 200) {
                        console.log("%c[失败] 状态码: " + res.status, "color: red");
                    }
                },
                onerror: (err) => {
                    console.log("%c[网络错误] 连接被拒绝", "color: red", err);
                }
            });
        }

        // 定期清理过期的去重缓存
        for (let [k, t] of dedupCache) {
            if (now - t > 35000) dedupCache.delete(k);
        }

    }, 3000); // 3秒轮询一次

})();

本程序仅供学习参考,不涉及任何版权问题,不对任何平台构成侵权

相关推荐
SuperherRo2 小时前
JAVA攻防-FastJson专题&面试不出网利用&BCEL字节码&C3P0二次&Impl链&延时判断
java·fastjson·不出网
MarkHD2 小时前
智能体在车联网中的应用:第12天 Python科学计算双雄:掌握NumPy与Pandas,筑牢AI与自动驾驶数据基石
人工智能·python·numpy
TH_12 小时前
18、删除WPSOfficeWord文档中的空白页
java
一雨方知深秋2 小时前
数组定义及访问
java·数组·二维数组·for·length·定义访问
alanesnape2 小时前
Java异常处理详解:Exception、ArithmeticException、FileNotFoundException
java·开发语言
while(1){yan}2 小时前
数据链路层与物理层
java·网络·网络协议
野蛮人6号2 小时前
黑马微服务 p23Docker02 docker的安装 如何正确安装docker,黑马微服务给的文档不行了,如何正确找到解决方法
java·docker·微服务·架构
hhhh明2 小时前
日志重定向
python
再__努力1点2 小时前
【78】HOG+SVM行人检测实践指南:从算法原理到python实现
开发语言·人工智能·python·算法·机器学习·支持向量机·计算机视觉