使用Python发送PDD直播间弹幕(协议算法分析)

文章目录

  • [1. 写在前面](#1. 写在前面)
  • [2. 接口分析](#2. 接口分析)
  • [3. 算法还原](#3. 算法还原)

【🏠作者主页】:吴秋霖
【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作!
【🌟作者推荐】:对爬虫领域以及JS逆向分析感兴趣的朋友可以关注《爬虫JS逆向实战》《深耕爬虫领域》
未来作者会持续更新所用到、学到、看到的技术知识!包括但不限于:各类验证码突防、爬虫APP与JS逆向分析、RPA自动化、分布式爬虫、Python领域等相关文章

作者声明:文章仅供学习交流与参考!严禁用于任何商业与非法用途!否则由此产生的一切后果均与作者无关!如有侵权,请联系作者本人进行删除!

1. 写在前面

研究分析的缘由则是一个读者找到作者。大致的一个情况就是他的朋友做直播带货但是前期小主播的话流量会比较少,而游客或者用户的浏览点击进入到直播间如果能够发送评论弹幕消息的话,在某种程度来说是可以增加一个热度的。而且最重要的就是对于新手主播来说,如果没有用户观众的互动是非常枯燥的(也会尴尬),毕竟一直对着空气介绍是需要情绪价值助力的~

2. 接口分析

这里我们直接抓包分析**/proxy/api/api/fanta/chat/push**接口,这个接口是发送弹幕消息的接口,接口的请求参数如下所示:

python 复制代码
comment_params = {
    "pdduid": "5061621833"
}
data = {
    "show_id": "20240716_71745644_01",
    "anchor_id": 71745644,
    "message": "我来啦~~",
    "room_id": "71745644",
    "roomId": "71745644",
    "_live_share_token": "CRQ46EEGIEMXATU2NPB2SDCKSMTR6KUGMHUCO7CSBFRYQO4KQY3GMYAWO7RVISCC4ZETPSP2PYHEVHBFONKUNTBXA7K52H4SM7PUGX56BOTUOYYVZTU6FYKYXFQLPQI6",
    "anti_content": "" # 动态加密
}

想要发送弹幕,主要需要解决的一个参数就是anti_content,开头是0as的,但是如果你是加了轨迹的0aq也是可以发送的,差不多600多位吧!

下图是作者使用多个Token通过加密算法调用接口发送的弹幕消息测试。Cookie信息的话其实是不需要放全的,验证只需要PDDAccessToken字段即可,但是必须是登录状态的!

加密算法需要传递必要的参数生成,这里可以直接使用Py封装一个动态替换的方法,如下:

python 复制代码
import execjs

def generate_anti_content(self):
	try:
	    with open("./anti_content_v2.0.js", "rb") as f:
	        js_code = f.read()
	    node = execjs.get()
	    js_code = js_code.decode("gbk", 'ignore').replace("{cookie}", self.cookie_str).replace("{href}", self.referer).replace("{ua}", self.user_agent)
	    if self.cookie_str not in js_code or self.user_agent not in js_code:
	        raise Exception("未替换成功")
	    ctx = node.compile(js_code)
	    return ctx.call('get_anti_content')
	except Exception as e:
	    logger.error(f"生成Anti-Content失败: {e}")
	    raise

其实有时候对于算法的使用场景很多人需求都不一样,Py直接调用的活着HTML内甚至是易语言调用,通用最好的方案就是起一个API服务,这样在使用场景上不需要瘦到语言的限制,作者也是使用JS实现了一个API服务,直接传参拿到加密参数,如下所示:

javascript 复制代码
const fs = require('fs');
const http = require('http');
const url = require('url');

const server = http.createServer((req, res) => {
    const { query } = url.parse(req.url, true);
    const cookie = query.cookie || '';
    const href = query.href || '';
    const ua = query.ua || '';

    fs.readFile('./anti_content_v2.0.js', 'utf-8', (err, data) => {
        if (err) {
            res.writeHead(500, { 'Content-Type': 'text/plain' });
            res.end('Internal Server Error');
            return;
        }

        const replacedCode = data.replace("{cookie}", cookie).replace("{href}", href).replace("{ua}", ua);

        eval(replacedCode);

        const result = get_anti_content();

        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(result));
    });
});

const PORT = 3000;
server.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}/`);
});

3. 算法还原

这里我们采用的补环境的方式去快速实现算法,通过上面Py动态传递cookie、ua、refer到JS算法中,补环境头如下所示:

javascript 复制代码
window = global;
const random = function (min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min)
};
window._event_map = new Map;
window.DeviceMotionEvent = class DeviceMotionEvent {
}
window.DeviceOrientationEvent = class DeviceOrientationEvent {
}
window.chrome = {}
window.DeviceMotionEvent = class {
    constructor() {
        debugger
    }
}
window.document = new class {
    constructor() {
	    this.cookie = "{cookie}";
        this.onmousewheel = "";

    }

    get referrer() {
	    return ""
    }

    addEventListener(e, t) {
        window._event_map.set(e, t)
    }

    getElementById() {

    }

    get ontouchstart() {
        return null
    }

    get documentElement() {
        return {
            scrollTop: 12
        }
    }

}
window.history = new class {
    back() {
    };
}
history.back.toString = e => "function back() { [native code] }"
document.getElementById.toString = e => "native code"
window.localStorage = new class {
    getItem() {
        return null
    }

    setItem() {

    }
}
window.location = new class {
    get href() {
        return '{href}'
    }

    get port() {
        return ""
    }
}
window.navigator = new class {
    hasOwnProperty() {
        return false
    }

    get languages() {
        return ["zh-CN", "zh"]
    }

    get plugins() {
        return {
            length: 4
        }
    }

    get userAgent() {
        return "{ua}"
    }
}
window.outerHeight = 1040;
window.outerWidth = 1920;
window.screen = new class {
    get availHeight() {
        return 1040
    }

    get availWidth() {
        return 1920
    }
}

接下来扣取完整的JS算法,JS代码的话还是Webpack打包的。依赖eDaA模块。它重新导出了fbeZ模块!然后再8oxB模块导出了一些异步任务调度相关的功能。完整的JS算法结构如下:

javascript 复制代码
!function(e) {
    function t(t) {
        for (var n, o, c = t[0], u = t[1], i = t[2], l = 0, d = []; l < c.length; l++)
            o = c[l],
            Object.prototype.hasOwnProperty.call(a, o) && a[o] && d.push(a[o][0]),
                a[o] = 0;
        for (n in u)
            Object.prototype.hasOwnProperty.call(u, n) && (e[n] = u[n]);
        for (s && s(t); d.length; )
            d.shift()();
        return f.push.apply(f, i || [])
    }
    function r() {
        for (var e, t = 0; t < f.length; t++) {
            for (var r = f[t], n = !0, o = 1; o < r.length; o++) {
                var u = r[o];
                0 !== a[u] && (n = !1)
            }
            n && (f.splice(t--, 1),
                e = c(c.s = r[0]))
        }
        return e
    }
    var n = {}
        , o = {
        0: 0
    }
        , a = {
        0: 0
    }
        , f = [];
    function c(t) {
        if (n[t])
            return n[t].exports;
        var r = n[t] = {
            i: t,
            l: !1,
            exports: {}
        }
            , o = !0;
        try {
            e[t].call(r.exports, r, r.exports, c),
                o = !1
        } finally {
            o && delete n[t]
        }
        return r.l = !0,
            r.exports
    }
    window._require_ = c;
    c.e = function(e) {
        var t = [];
        o[e] ? t.push(o[e]) : 0 !== o[e] && {
            8: 1
        }[e] && t.push(o[e] = new Promise((function(t, r) {
                for (var n = "static/css/" + ({
                    8: "styles",
                    58: "9f4ea93f",
                    59: "a21cddf3",
                    60: "flvjs"
                }[e] || e) + "." + {
                    8: "f47ac1ea",
                    58: "31d6cfe0",
                    59: "31d6cfe0",
                    60: "31d6cfe0",
                    139: "31d6cfe0",
                    140: "31d6cfe0"
                }[e] + ".chunk.css", o = c.p + n, a = document.getElementsByTagName("link"), f = 0; f < a.length; f++) {
                    var u = (l = a[f]).getAttribute("data-href") || l.getAttribute("href");
                    if ("stylesheet" === l.rel && (u === n || u === o))
                        return t()
                }
                var i = document.getElementsByTagName("style");
                for (f = 0; f < i.length; f++) {
                    var l;
                    if ((u = (l = i[f]).getAttribute("data-href")) === n || u === o)
                        return t()
                }
                var s = document.createElement("link");
                s.rel = "stylesheet",
                    s.type = "text/css",
                    s.onload = t,
                    s.onerror = function(t) {
                        var n = t && t.target && t.target.src || o
                            , a = new Error("Loading CSS chunk " + e + " failed.\n(" + n + ")");
                        a.request = n,
                            r(a)
                    }
                    ,
                    s.href = o,
                    document.getElementsByTagName("head")[0].appendChild(s)
            }
        )).then((function() {
                o[e] = 0
            }
        )));
        var r = a[e];
        if (0 !== r)
            if (r)
                t.push(r[2]);
            else {
                var n = new Promise((function(t, n) {
                        r = a[e] = [t, n]
                    }
                ));
                t.push(r[2] = n);
                var f, u = document.createElement("script");
                u.charset = "utf-8",
                    u.timeout = 120,
                c.nc && u.setAttribute("nonce", c.nc),
                    u.src = function(e) {
                        return c.p + "static/chunks/" + ({
                            8: "styles",
                            58: "9f4ea93f",
                            59: "a21cddf3",
                            60: "flvjs"
                        }[e] || e) + "." + {
                            8: "4738d83962c38e9c33b5",
                            58: "a953a84ef62f54e12779",
                            59: "4fc845ca7e3eb5a22c62",
                            60: "320dd134ef7f119c4f58",
                            139: "f14f12c553fb100c3cde",
                            140: "82aa74f0d0b2e38b0dbe"
                        }[e] + ".js"
                    }(e);
                var i = new Error;
                f = function(t) {
                    u.onerror = u.onload = null,
                        clearTimeout(l);
                    var r = a[e];
                    if (0 !== r) {
                        if (r) {
                            var n = t && ("load" === t.type ? "missing" : t.type)
                                , o = t && t.target && t.target.src;
                            i.message = "Loading chunk " + e + " failed.\n(" + n + ": " + o + ")",
                                i.name = "ChunkLoadError",
                                i.type = n,
                                i.request = o,
                                r[1](i)
                        }
                        a[e] = void 0
                    }
                }
                ;
                var l = setTimeout((function() {
                        f({
                            type: "timeout",
                            target: u
                        })
                    }
                ), 12e4);
                u.onerror = u.onload = f,
                    document.head.appendChild(u)
            }
        return Promise.all(t)
    }
        ,
        c.m = e,
        c.c = n,
        c.d = function(e, t, r) {
            c.o(e, t) || Object.defineProperty(e, t, {
                enumerable: !0,
                get: r
            })
        }
        ,
        c.r = function(e) {
            "undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {
                value: "Module"
            }),
                Object.defineProperty(e, "__esModule", {
                    value: !0
                })
        }
        ,
        c.t = function(e, t) {
            if (1 & t && (e = c(e)),
            8 & t)
                return e;
            if (4 & t && "object" === typeof e && e && e.__esModule)
                return e;
            var r = Object.create(null);
            if (c.r(r),
                Object.defineProperty(r, "default", {
                    enumerable: !0,
                    value: e
                }),
            2 & t && "string" != typeof e)
                for (var n in e)
                    c.d(r, n, function(t) {
                        return e[t]
                    }
                        .bind(null, n));
            return r
        }
        ,
        c.n = function(e) {
            var t = e && e.__esModule ? function() {
                    return e.default
                }
                : function() {
                    return e
                }
            ;
            return c.d(t, "a", t),
                t
        }
        ,
        c.o = function(e, t) {
            return Object.prototype.hasOwnProperty.call(e, t)
        }
        ,
        c.p = "",
        c.oe = function(e) {
            throw console.error(e),
                e
        }
    ;
    var u = window.webpackJsonp = window.webpackJsonp || []
        , i = u.push.bind(u);
    u.push = t,
        u = u.slice();
    for (var l = 0; l < u.length; l++)
        t(u[l]);
    var s = i;
    "undefined" === typeof window ? global.APP_VERSION = "0b8a7369" : "undefined" === typeof global && (window.APP_VERSION = "0b8a7369")
}({
    "eDaA": function(t, e, n) {
        t.exports = n("fbeZ")
    },
    "fbeZ": function(t, e, n) {...}.call(this, n("8oxB")) // 算法小千行
    "8oxB": function(t, e) {
        var n, r, o = t.exports = {};
        function i() {
            throw new Error("setTimeout has not been defined")
        }
        function a() {
            throw new Error("clearTimeout has not been defined")
        }
        function s(t) {
            if (n === setTimeout)
                return setTimeout(t, 0);
            if ((n === i || !n) && setTimeout)
                return n = setTimeout,
                    setTimeout(t, 0);
            try {
                return n(t, 0)
            } catch (e) {
                try {
                    return n.call(null, t, 0)
                } catch (e) {
                    return n.call(this, t, 0)
                }
            }
        }
        !function() {
            try {
                n = "function" === typeof setTimeout ? setTimeout : i
            } catch (t) {
                n = i
            }
            try {
                r = "function" === typeof clearTimeout ? clearTimeout : a
            } catch (t) {
                r = a
            }
        }();
        var l, c = [], u = !1, d = -1;
        function f() {
            u && l && (u = !1,
                l.length ? c = l.concat(c) : d = -1,
            c.length && p())
        }
        function p() {
            if (!u) {
                var t = s(f);
                u = !0;
                for (var e = c.length; e; ) {
                    for (l = c,
                             c = []; ++d < e; )
                        l && l[d].run();
                    d = -1,
                        e = c.length
                }
                l = null,
                    u = !1,
                    function(t) {
                        if (r === clearTimeout)
                            return clearTimeout(t);
                        if ((r === a || !r) && clearTimeout)
                            return r = clearTimeout,
                                clearTimeout(t);
                        try {
                            r(t)
                        } catch (e) {
                            try {
                                return r.call(null, t)
                            } catch (e) {
                                return r.call(this, t)
                            }
                        }
                    }(t)
            }
        }
        function h(t, e) {
            this.fun = t,
                this.array = e
        }
        function g() {}
        o.nextTick = function(t) {
            var e = new Array(arguments.length - 1);
            if (arguments.length > 1)
                for (var n = 1; n < arguments.length; n++)
                    e[n - 1] = arguments[n];
            c.push(new h(t,e)),
            1 !== c.length || u || s(p)
        }
            ,
            h.prototype.run = function() {
                this.fun.apply(null, this.array)
            }
            ,
            o.title = "browser",
            o.browser = !0,
            o.env = {},
            o.argv = [],
            o.version = "",
            o.versions = {},
            o.on = g,
            o.addListener = g,
            o.once = g,
            o.off = g,
            o.removeListener = g,
            o.removeAllListeners = g,
            o.emit = g,
            o.prependListener = g,
            o.prependOnceListener = g,
            o.listeners = function(t) {
                return []
            }
            ,
            o.binding = function(t) {
                throw new Error("process.binding is not supported")
            }
            ,
            o.cwd = function() {
                return "/"
            }
            ,
            o.chdir = function(t) {
                throw new Error("process.chdir is not supported")
            }
            ,
            o.umask = function() {
                return 0
            }
    },
})

JS代码基于动态加载模块的机制。可以通过封装下面的入口函数,window.require被用作模块加载器,require函数的实现是通过上面的**c(t)**函数完成,它主要负责根据模块的ID加载模块,如下所示:

javascript 复制代码
function get_anti_content() {
    var j = window._require_.n(window._require_("eDaA"))
    var e = {
        serverTime: new Date().getTime()
    };
    return new j.a(e).messagePack()
}
相关推荐
_.Switch16 分钟前
Python 自动化运维持续优化与性能调优
运维·开发语言·python·缓存·自动化·运维开发
1 9 J19 分钟前
Java 上机实践4(类与对象)
java·开发语言·算法
J不A秃V头A22 分钟前
Python爬虫:获取国家货币编码、货币名称
开发语言·爬虫·python
阿斯卡码2 小时前
jupyter添加、删除、查看内核
ide·python·jupyter
passer__jw7672 小时前
【LeetCode】【算法】3. 无重复字符的最长子串
算法·leetcode
passer__jw7672 小时前
【LeetCode】【算法】21. 合并两个有序链表
算法·leetcode·链表
sweetheart7-72 小时前
LeetCode22. 括号生成(2024冬季每日一题 2)
算法·深度优先·力扣·dfs·左右括号匹配
埃菲尔铁塔_CV算法4 小时前
图像算法之 OCR 识别算法:原理与应用场景
图像处理·python·计算机视觉
封步宇AIGC5 小时前
量化交易系统开发-实时行情自动化交易-3.4.2.Okex行情交易数据
人工智能·python·机器学习·数据挖掘
封步宇AIGC5 小时前
量化交易系统开发-实时行情自动化交易-2.技术栈
人工智能·python·机器学习·数据挖掘