☰
B站消息清理助手升级记:从v0.1到v0.2的油猴脚本自动化实践
2026/10/5 9:08:59 网站建设 项目流程

我有时候觉得,B站网页端最磨人的不是找不到想看的视频,而是右上角那个小红点。私信几条未读、@提醒几条、系统通知又来了,于是你点进去,一条条处理,回到列表,再点下一条。如果只是偶尔一次还好,天天都这样,就会变成一种没办法忽略的重复劳动。

最近看到一个小型油猴脚本项目,标题是“B站消息清理助手 v0.2(多端免登陆)”。虽然这个项目的介绍文字很少,但这个标题已经足够引起人兴趣。不是因为“清理消息”多神奇,而是这里的 v0.2 这个版本号很有信息量。它意味着作者不是停在“能删消息”这一步,而是往前走了一版,处理了删得快、删得稳、换端也能用的问题。

我更愿意把这类工具理解成一句话:它真正提供的不是“一键删除”这个结果,而是把重复操作变成可复用、可控制、可回看的小型工作流。本文不宣称抄一段代码就能直接跑,也不替这个项目做保证,只展开聊这类脚本从零到一、从一到 0.2 背后要面对的工程问题,以及你安装一个陌生用户脚本之前到底该关心什么。

另外先把一个细节说清楚:原标题里的“免登陆”应该是“免登录”。“登陆”是登上海岛,不是登录网页。下面的讨论都按“登录”来写。

1. 先搞清楚它要处理的是哪一类B站消息,以及你该不该用脚本

1.1 B站的“消息”并不是同一种东西

很多人一说消息清理,脑子里只有“私信”。但B站的网页端里,能产生红点和未读的入口其实很杂。常见的大致有这么几类:

  • 私信会话:和 UP 主、亲友、群聊之间的站内信,通常有“进入会话”“删除会话”这类操作。
  • @提醒和回复:你在评论区被 @、被回复之后产生的提醒。
  • 系统通知:账号安全、活动、创作中心、充电等各类通知。
  • 动态和私信里的单向消息:有些消息只是通知性质,并不具备“会话”概念。

这些消息有一个关键差别:有的是“标记已读”,有的是“删除会话”。标记已读通常不可逆,但至少不会把聊天记录直接弄没;删除会话则完全不同,点了就没有了,大概率没有回收站。

所以一个批量清理脚本要考虑的不仅是“能不能找到未读按钮”,还要区分自己正在执行的动作属于哪一类。一个合格的脚本应该先告诉用户:我默认要处理的是哪种消息,使用哪种动作,误伤范围是什么。如果直接把所有按钮都当成同一个处理对象,那不是消息清理助手,那是网页拆弹器。

1.2 脚本适合谁,不适合谁

在花时间安装、测试甚至自己写脚本之前,先判断场景合不合适。

适合的情况大约是:

  • 这是你自己本人的B站账号,不是公司账号、家人账号、公共账号。
  • 消息记录本身不重要,清理掉不会影响你后续找资料。
  • 你面对的是大量重复操作,比如几百条广告私信,一条条点太浪费时间。
  • 你用的是桌面浏览器,或者脚本管理器能正常注入的移动端浏览器环境。
  • 你有能力在脚本出问题后看懂控制台日志,至少能刷新页面恢复状态。

不适合的情况也会很明确:

  • 消息里可能有重要资料,你还希望以后翻到。
  • 账号多人共用,你不知道清理会不会影响另一个人。
  • 你基本不用浏览器网页端,只在原生 App 里看消息。
  • 你要用的是一个黑盒脚本,无法审查代码来源,也不敢确认它到底访问了什么。
  • 你设备上的用户脚本管理器本身不可信,或者来自非官方渠道。

这里的核心判断是:便宜好用的批量清理工具有一个共同前提,就是操作对象足够低价值、重复度足够高、路径足够统一。一旦涉及“重要记录”或者“账号权限”,批量自动化带来的风险就会超过它节省的时间。

2. 从 v0.1 到 v0.2:一个小工具迭代时到底在改什么

2.1 页面是一张会变的网,而自动化需要状态机

如果只写一个“一次性脚本”,思路通常很直接:进入列表页,找到所有消息,逐个点击删除按钮。这种写法在 v0.1 里很常见,也确实能跑通,但跑通几次后你会遇到一些怪场景:

  • 只处理了列表可视区域内的几条,往下滚动后新加载的消息没被处理。
  • 因为点击过快,页面还没刷新,旧节点已经被移除,脚本拿到的选择器失效。
  • 删除到一半,某条消息的加载状态异常,后续代码直接报错退出。
  • 处理完一批,页面消息数已经变化,但脚本中的计数还停留在最初状态。

手动操作时,人脑会自动等待页面完成加载、确认按钮状态变化、再决定要不要继续。但脚本没有这种本能,它只会按照你写的顺序执行。如果没有“处理一批 -> 确认结果 -> 重新扫描 -> 再处理下一批”的状态推进,批量执行就会变成一连串不可控的点击。

这正是 v0.2 和 v0.1 最容易拉开差距的地方。v0.1 证明的是:能找到按钮,能点击,能完成一次手工替代。v0.2 要解决的则是:能不能在多轮处理中仍然保持稳定,能不能在异常发生时停下来。

2.2 v0.2 常见迭代方向

如果我没有拿到源码,我不敢说这个项目的具体 v0.2 改动是什么。但从同类脚本的经验看,从 v0.1 到 v0.2 往往不是增加花哨功能,而是修补那些单次跑通时不会被发现、批量运行时会暴露的问题。

v0.1 常见痛点v0.2 常见完善方向背后的本质原因
在列表页连续执行后卡死增加每轮数量上限和间隔页面渲染跟不上脚本点击速度
只在桌面端一个URL下有效增加移动端页面适配不同端的 DOM 结构并不是一套
换一台电脑就不会用了减少外部依赖,使用浏览器页面自带登录态用户脚本本身没有跨设备保存账号状态的能力
误删会话后没法恢复默认只做“标记已读”,不自动清理会话删除是不可逆的高风险动作
出错后不知道卡在哪一步给每一步操作写日志和状态自动化必须可以被回看,否则没法排查

这个表格能解释一个现象:很多脚本作者在第一版时认为难点是“定位元素”,但第二版时才会意识到,真正的难点是“在页面不断变化的前提下仍然稳定执行”。

2.3 一个脚本应该维护的四个状态

我在看或写这类工具时,如果它是批量执行而不是单次点击,会特别关注它有没有这四个状态:

  • 未启动:脚本已挂载,但没有开始批量操作。
  • 运行中:正在扫描与处理,当前轮次和进度必须可见。
  • 暂停/停止:用户能随时中断后续动作,而不是等着几千条消息跑完。
  • 结束:到达设定上限、没有更多目标,或者发生了需要人工介入的错误。

这四个状态如果能在日志里完整看到,那这个脚本是具备基本工程意识的。如果没有,你相当于在开一辆没有仪表盘和刹车的车,速度快慢全凭运气。

注意:判断一个消息清理脚本能不能长期用,不是看它的主页描述多漂亮,而是看它出错时会不会停下来告诉你:我现在停在哪一步,最后执行了什么。

3. “多端免登录”不是绕过登录,而是把登录态和操作解耦

3.1 最容易产生的误解

看到“免登录”三个字,容易有人兴奋:是不是不用登录B站账号,就能把消息清掉?

答案很直接:不是。一个合规的用户脚本,不应该也不需要绕过网站的登录逻辑。油猴脚本运行在你已经打开的网页环境里,它之所以能操作你的消息,是因为浏览器本身已经处在一个登录状态。脚本做不了“没登录就访问你的私人列表”这件事,如果它真的能做到,那它依赖的就不是公开接口,而是某种你本不该触碰的漏洞。

这里要把安全边界说清楚:不管叫“免登录”还是“自动登录”,都不应该变成“脚本帮你搞定账号状态”。你愿意把密码和手机验证码交给一个陌生脚本吗?当然不应该。对于这类页面内工具,最可靠的方式是:你正常登录B站,脚本只负责在你登录后的页面环境里做重复操作。

3.2 常规实现路径

从实现层面看,一个规规矩矩的消息清理脚本通常走这几条路线之一:

策略“免登录”的实际效果需要注意的边界
页面内按钮操作用户已经登录,脚本模拟点击网页按钮操作范围不会超过当前页面能做的事
调用页面自身请求链路请求携带浏览器已有的会话状态,不需要额外输入账号只应处理当前账号数据,不做改包、绕验证
使用本地配置存储用户换设备后只需同步脚本偏好,不暴露身份凭据脚本自身状态和登录状态分离

这里所谓“免登录”,更准确的表述应该是:免掉脚本自己的登录步骤,而不是免掉网站上那个正常登录步骤。用户不需要在油猴脚本里再输入一次账号或扫码,打开已经登录好的页面就能直接用。这个体验优化很实际,因为让你每台电脑都重新登录一遍B站已经够麻烦,再让你在脚本里配一套独立凭证,很多人会直接放弃。

3.3 多端到底是什么端

一个用户脚本要支持多端,尤其是桌面端和移动端时,并不是加一行 @match 就行。

桌面浏览器访问的页面结构、按钮位置、加载方式,和手机浏览器访问的页面可能完全不同。即使网址看起来接近,移动端页面可能更简化,操作入口也可能不是直接显示,而是藏在某个菜单里。

如果你拿到一个号称支持多端的脚本,建议在两种环境里分别验证而不是只检查安装页。更要紧的是,如果手机端没有可靠的脚本管理器,那就先别强行使用。油猴脚本依赖的注入机制,并不是所有移动端浏览器都提供;在没有可靠运行环境的情况下,讨论多端免登录没有意义。

4. 按这个最小流程写一个消息清理助手:先扫清消息,再谈批量

4.1 先搭一个稳定、可见、可停止的脚本骨架

不管是要写自己的脚本,还是改造一个现成脚本来学习,我建议从最基础的骨架开始,不要一上来就写删除逻辑。

在油猴脚本管理器里新建脚本时,用户脚本头部是关键。一个适合做验证的骨架类似于:

// ==UserScript== // @name B站消息清理助手示例 // @namespace bilibili-message-cleaner-demo // @version 0.2.0 // @description 一个用于演示B站消息批量管理思路的脚本骨架 // @match https://www.bilibili.com/* // @match https://m.bilibili.com/* // @grant none // @run-at document-idle // ==/UserScript== (() => { 'use strict'; const config = { autoMarkRead: true, autoDeleteSession: false, maxPerRound: 20, delayMs: 400, stopOnError: true, }; function log(action, result) { console.log(`[消息清理助手] ${new Date().toLocaleTimeString()} | ${action}`, result); } log('脚本已挂载,当前页面', location.href); })();

这段代码不算成品,只是脚本骨架。从这里可以看出,脚本默认并不打算执行任何删除,也把autoDeleteSession默认关掉了。先让脚本只输出日志,确认它能稳定注入到目标页面,再继续往下写,是可靠的顺序。

4.2 先做扫描,不要一上来就执行

真正动手写批量逻辑前,先在浏览器控制台做一次人工验证。打开消息列表,找到网页上某条消息对应的元素,确认它的可识别属性。不同版本页面上结构并不一致,所以不存在一成不变的选择器。

一个示例性的扫描逻辑是:

// 示例:先只统计,不做任何删除操作 function collectItems(root) { return Array.from(root.querySelectorAll('[data-message-item]')); } const items = collectItems(document); log('当前列表里能拿到的消息项数量', items.length);

如果页面不存在>function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function runRound(round) { if (round > config.maxPerRound) { log('到达本轮处理上限,停止'); return; } const items = collectItems(document); if (!items.length) { log('当前没有可处理项'); return; } const target = items[0]; // 这里只处理“标记已读”这类低风险动作 if (config.autoMarkRead) { target.querySelector('.read-btn')?.click(); } await sleep(config.delayMs); if (config.stopOnError) { // 在真实实现里,这里应该检查上一轮请求是否成功 // 如果失败,直接停下,不要继续处理下一条 } runRound(round + 1); }

这个思路的关键不是代码技巧,而是它承认了页面是活的、请求需要时间、异常需要中断。处理完一条,不急着往下一条冲,而是重新确认一遍当前列表状态,再决定下一步。

4.4 不可逆操作必须默认关闭

我建议任何消息清理脚本把两类动作分开:

  • 低风险动作:标记已读、展开通知、忽略消息。这些动作不影响会话记录本身。
  • 高风险动作:删除会话、清空消息记录。这些动作不可恢复。

在这类脚本里,高风险动作应该是一个显式开启的配置,而不是默认执行项。把“删除会话”放在默认行为里,等于把一个只能在特定场景使用的武器绑在腿上。一旦选择器误匹配,几条重要会话就会瞬间消失。

好的脚本应该像这样区分:

if (!config.autoDeleteSession) { // 当前脚本不允许删除会话 // 那么这里会跳过所有包含“删除”关键词的按钮 return; }

如果拿到的一个脚本默认就把删除按钮一起点了,也没有二次确认和停止按钮,我建议不要在生产环境用。它或许能跑得快,但快不意味着安全。

常见经验是:先在小号上制造少量测试消息,用单轮方式跑通一次,观察只处理了目标消息;再逐步加大轮次;确认无误后,才轮到真正账号里的历史消息。任何跳过这一步的人,都是在拿自己的历史记录做测试。

5. 多端上线前必须想清楚的四件事

5.1 URL 命名空间没有你想象的那么宽

一个脚本如果同时匹配桌面网页和移动端网页,需要在脚本头部认真设计 @match 范围。全站匹配看似方便,实际会让你在浏览普通视频页、稍后再看、收藏夹等无关页面时也触发脚本逻辑,干扰正常浏览。

比较合理的做法是尽量让脚本只在消息中心或目标列表相关的 URL 范围内运行。具体包含哪些子目录,需要打开 DevTools 看实际页面地址。不同阶段的页面可能调整过路由,所以“上一个教程里的匹配规则”不一定永远有效。

5.2 功能开关必须独立且默认保守

在多端场景里,最容易出现的问题是:桌面端配置了“自动删除会话”,移动端却默认关闭,结果用户在两台设备上得到的体验完全不一致。

这种问题不一定是 bug,而是缺少“功能开关同步”的意识。脚本可以把配置分成两类:

  • 安全设置:如是否允许删除会话、是否从外部更新代码、是否启用日志。
  • 使用偏好:如每轮处理数量、间隔时间、是否自动滚动。

安全设置在每台设备上都应该默认采用最保守值,不能为了体验一致就强制同步。你要的是多端都能用,不是多端一起去执行一个高风险配置。

5.3 权限边界必须保持最小

油猴脚本头部里的 @grant 字段决定了脚本能调用哪些能力。一个只用页面 DOM 的脚本,使用@grant none就够了。如果脚本申请了跨域请求、访问剪贴板、下载文件等能力,你需要想清楚:它为什么要这些东西?

尤其在安装别人写的脚本时,我会先看两点:

  • 是否引用了远程地址,在运行时动态拉取新代码。
  • 是否存在混淆严重的代码,无法判断它到底发送了什么请求。

用户脚本不是越底层越厉害,反而是越容易审计越值得信任。一个开源、可读、只做一件事的脚本,好过一个功能丰富但读不懂的脚本。这不是保守,是必要的风险管理。

5.4 上线前先在小号上做灰度

脚本开发里也应该有“灰度”意识。

第一次安装,只观察不执行。 第二次执行,只处理一条消息。 第三次执行,处理少量消息并检查日志。 第四次执行,才把轮次上限提高。

每一步都要确认:这次操作有没有产生预期中的请求?有没有把不该删的会话删掉?有没有出现未读数已经清零但页面仍然显示旧状态的情况?

6. 消息没清掉、误清了、脚本不跑了?按这个顺序排查

6.1 先看现象是哪一层的问题

脚本不生效时,不要急着改代码。第一步先确定问题出在脚本层、页面层,还是网络请求层。

  • 打开浏览器控制台,看脚本有没有输出日志。
  • 如果脚本完全没运行,检查脚本管理器是否开启、@match 是否覆盖当前 URL、页面有没有正常登录。
  • 如果脚本运行了,但没有找到目标元素,先手动在 DevTools 里执行选择器,确认元素路径是否存在。
  • 如果按钮点击了但消息没变,看 Network 面板里请求是否发出,有没有返回错误状态码。

一个很常见的问题是:页面上按钮被点击后,只是把未读数字从 UI 上隐藏,后台请求并没有成功。UI 成功不等于业务成功,这也是脚本需要记录网络状态和响应结果的原因。

6.2 执行到一半停下来,绝大多数是列表或请求状态异常

脚本处理几十条后停止,可能是出现了以下几种情况之一:

  • 上一轮点击已经让 DOM 节点变化,下一轮还引用旧节点,导致代码报错。
  • 网络请求被限流,返回了异常状态。
  • 页面的虚拟列表还没有滚动到下一区域,脚本找不到可处理的消息。
  • 某条消息的界面加载失败,按钮位置和预期不同。

这时应该看脚本最后一条日志和 Network 面板里最近一次请求的状态。不要直接改大间隔、改小间隔,先找到停住的真正原因。

6.3 节流参数不是越大越好

很多初学者认为处理消息时延时要越长越安全,于是设置成 3 秒甚至 5 秒一条。这确实安全,但几百条消息就会等得失去耐心。真正合理的做法是先从 300 到 800 毫秒之间开始测试,观察请求是否稳定,再找到一个既不触发页面异常、又能接受的速度。

比单条延迟更关键的是“轮次上限”和“失败中断”。将每轮处理数量限制在一个合理范围,比如 20 到 50 条,比无限循环更可控。如果第一轮出现问题,你还可以在几十条内停住,而不是等几百条删完才发现错误。

6.4 多端排查的顺序不太一样

桌面端脚本不跑了,检查顺序通常是:脚本开关 -> @match -> 登录态 -> 元素选择器。

移动端脚本不跑了,还要多检查一步:脚本管理器在移动端浏览器里到底有没有真正注入。如果它只是把脚本文件装上了,但页面没有加载,那么问题不在代码,而在运行环境。

排查时可以直接在目标页面开一个脚本内部的调试模式,把页面地址、当前元素数量和最后执行状态输出到 localStorage。这样即使你关掉页面,也能在下一轮启动时读到最后一次日志,知道问题出在哪一步。

| 排查对象 | 第一眼要看什么 | 需要避免的误区 | |

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询