啦la啦 发表于 5 天前

简易脚本开发过程:自动查询B站弹幕点赞数并显示在弹幕旁

本帖最后由 啦la啦 于 2026-1-4 02:07 编辑

此笔记记录脚本(https://scriptcat.org/zh-CN/script-show-page/5016) 开发过程

### B站弹幕DOM探索

F12 → 元素 → 查找 搜索弹幕文本,找到弹幕元素

```html
<!--典型弹幕元素-->
<div aria-live="polite" role="comment"
class="bili-danmaku-x-dm bili-danmaku-x-roll bili-danmaku-x-show"
style="--opacity: 0.8; --fontSize: 25px; --fontFamily: SimHei, 'Microsoft JhengHei', Arial, Helvetica, sans-serif; --fontWeight: bold; --color: #ffffff; --textShadow: 1px 0 1px #000000,0 1px 1px #000000,0 -1px 1px #000000,-1px 0 1px #000000; --offset: 730.5388359130914px; --translateX: -861px; --duration: 8.21s; --top: 33.125px;"
>
哈哈哈
</div>
```

经研究得到以下结论:

1. 所有弹幕都有`bili-danmaku-x-dm`类
2. 显示中的弹幕有`bili-danmaku-x-show`类,显示结束后不会把弹幕元素从DOM移除,而是移除`bili-danmaku-x-show`类,等待复用
3. 外层弹幕容器的类名为`bpx-player-row-dm-wrap`

### B站弹幕加工探索

由以上DOM探索可知弹幕DOM元素并不含弹幕的id等信息,得找到弹幕是如何加工成DOM元素的。

在DevTools(F12)中右键弹幕容器(`bpx-player-row-dm-wrap`) → 中断于 → 子树修改

播放视频,结果中断于ndp.223.xxxxxx.js文件的一个名为`render`方法中(有时中断于名为`init`的方法,F8继续运行一下),此方法根据弹幕信息,为弹幕DOM元素添加各种style属性。

```javascript
e.prototype.render = function(t, e) {
var n, r, o, i = this.textData, a = i.color, s = i.border, c = i.borderColor, u = i.colorfulImg, l = i.isHighLike, f = i.likes, p = this.config.setting, h = p.bold, d = p.fontFamily, m = p.fontBorder, y = p.opacity, g = Ft(this.outlineColor(a)), v = "".concat(H, "-dm ").concat(H, "-roll"), b = "";
s && "transparent" !== c && !(null === (n = this.modeInfo) || void 0 === n ? void 0 : n.isUpSlogan) ? (b += "border: 1px solid ".concat(Ft(c), ";"),
this.width += 4,
this.height += 4) : (null === (r = this.modeInfo) || void 0 === r ? void 0 : r.isUpSlogan) && (v += " ".concat(H, "-upslogan"),
this.width += 12),
this.font = (h ? "bold" : "normal") + " " + 2 * this.fontSize + "px " + d,
b += "--opacity: ".concat(Math.max(y, .1) + "", ";"),
b += "--fontSize: ".concat(this.fontSize, "px;"),
b += "--fontFamily: ".concat(d, ", Arial, Helvetica, sans-serif;"),
b += "--fontWeight: ".concat(h ? "bold" : "normal", ";"),
b += "--color: ".concat(Ft(a), ";"),
this.element.setAttribute("aria-live", "polite"),
this.element.setAttribute("role", "comment"),
u || (b += "--textShadow: ".concat(this.getShadow(g, m), ";")),
b += this.renderRealTimeOptimization(null === (o = this.textData) || void 0 === o ? void 0 : o.isRealTime),
l || f || u ? this.renderSpecialType(e || v, b) : (this.hasEmoji || this.textData.prefix || this.textData.suffix ? this.element.innerHTML = lt(this.text) : this.element.textContent = lt(this.text),
this.element.className = e || v,
this.element.style.cssText = b),
this.textData.prefix && this.textData.prefix instanceof HTMLElement && (this.textData.prefix.style.height = "".concat(1.2 * this.fontSize, "px"),
this.textData.prefix.style.width = "auto",
this.element.insertBefore(this.textData.prefix, this.element.firstChild)),
this.textData.suffix && this.textData.suffix instanceof HTMLElement && (this.textData.suffix.style.height = "".concat(1.2 * this.fontSize, "px"),
this.textData.suffix.style.width = "auto",
this.element.appendChild(this.textData.suffix))
}
```

this.textData包含各种弹幕信息。具体含义参考 (https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/danmaku_proto.md) DanmakuElem

```javascript
{
    "stime": 884.647,   // 发送时进度条时间
    "mode": 5,
    "size": 25,
    "color": 16646914,
    "uhash": "6a3439b",
    "text": "哈哈哈",
    "date": 1766993886,
    "weight": 10,
    "dmid": "2012449591689219328",// 弹幕唯一id(查询点赞数需要)
    "attr": 1048576,
    "oid": 35045576155, // 视频唯一chatid(查询点赞数需要)
    "dmFrom": 1,
    "likes": undefine,
    "rawMode": 5,
    "modeInfo": {
      "cid": 35045576155
    },
    "pool": 0,
    "uname": "",
    "id_str": "2012449591689219328",    // 弹幕id通常超出Number范围
    "border": false,
    "borderColor": 6750207,
    "isHighLike": false,
    "isMine": false,
    "prefix": null,
    "suffix": null,
    "on": true
}
```

到此为止就很简单了,~~交给AI即可~~。hook `render` 函数,把需要的弹幕信息塞入弹幕DOM元素的dataset里;然后MutationObserver监控弹幕的DOM元素,有新弹幕出现就从dataset里获取oid、dmid查询点赞数,显示在弹幕旁。

### 其他

***

**点赞数查询API**:(https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/thumbup.md)

***

**角标使用的SVG**:

1. 发送图标:(https://www.iconfont.cn/search/index?searchType=icon&q=%E5%8F%91%E9%80%81)找了一个
2. 点赞图标:B站自带点赞弹幕图标(悬浮于弹幕上,右键点赞图标 → 检查 → svg复制下来)

***

**角标按热度增亮公式**:

高亮角标即角标添加背景色,亮度即背景色不透明度(0~1),选用公式 $1-e^{-kx}$

由于合并数与点赞数不在一个数量级,热度为合并数与点赞数的加权和:

$hot=(sendCount-1)\\cdot sendMul+likes\\cdot likeMul$

> 发送次数为1相当于合并数为0,因此sendCount需减1

热度为0时也要有一定的亮度,设为b0,公式修正为:

$brightness=1-(1-b0)e^{-k \\cdot hot}$

还剩参数k决定曲线上升速率,设参数h90表示亮度达到90%时的热度值,计算k:

$$
\\begin{aligned}
0.9 &= 1-(1-b\_0)e^{-k \\cdot h\_{90}} \\
e^{-k \\cdot h\_{90}} &= \\frac{0.1}{1-b\_0} \\
k &= -\\frac{1}{h\_{90}}\\ln\\left(\\frac{0.1}{1-b\_0}\\right) \\
k &= \\frac{1}{h\_{90}}\\ln(10-10\\cdot b\_0)
\\end{aligned}
$$

最终四个参数决定亮度曲线:

| 参数 | 含义 | 默认 |
| --- | --- | --- |
| b0 | 热度为0时的亮度 | 0.2 |
| h90 | 亮度为90%时的热度 | 500 |
| sendMul | 合并数倍率 | 5 |
| likeMul | 点赞数倍率 | 1 |

这几个参数没给UI,可通过控制台 `__DM_ADAPT__.setBadgeHighlightCurve(0.2, 500, 5, 1)` 控制。
页: [1]
查看完整版本: 简易脚本开发过程:自动查询B站弹幕点赞数并显示在弹幕旁