1. 为什么要做这个脚本:从一次漏领激励说起
1.1 创作者激励这个事,很多人都会漏领
做B站的朋友应该都清楚,创作激励计划并不是每天自动到账的,而是按周期结算、按页面提示手动领取。你辛辛苦苦把视频发上去,等播放量、点赞、投币这些数据达标了,后台会给你生成一笔激励收益,这时候你必须在规定时间内去“创作中心-收益”或者对应的激励页面,点一下“领取”按钮,钱才会真正落到你的账户里。
问题就出在这个“手动领取”上。我身边有不少UP主,包括我自己,都有过错过领取周期的经历。有时候是视频数据正好卡在凌晨更新,有时候是页面入口藏在二级菜单里根本找不到,更多时候就是单纯忘了。等想起来再去看,本期奖励已经过期,那种感觉真的跟丢钱一样难受。
所以我一直在想,能不能写一段脚本,让浏览器自己帮我把“领取”按钮点了。于是就有了这个项目:一段纯控制台JS脚本,不需要安装任何东西,打开浏览器F12,粘贴进去回车,脚本会自动扫描页面上所有可领取的按钮,逐个点击,把该领的奖励全部领完。而且这个脚本不只针对B站某一个页面,它按文案特征去匹配按钮,所以只要是长得差不多的领奖页面,基本都能直接通用。
1.2 为什么用纯控制台,不用油猴脚本
可能有人会问,同类需求用油猴脚本不是更常见吗?后台挂着脚本,一进页面自动运行,体验确实更好。那我为什么偏偏要写成“纯控制台”版本?这里有几个很现实的考虑。
第一,油猴脚本有安装门槛。你得先装Tampermonkey扩展,再找脚本地址,再安装启用,这一套流程对不熟悉浏览器扩展的UP主来说并不友好。而且有些浏览器、有些公司电脑、有些学校机房,扩展安装权限是被锁死的,你连应用商店都进不去。控制台就不一样了,任何主流浏览器都有F12开发者工具,只要你打开网页就能用,零安装零依赖。
第二,这类“领奖励”操作本身就是低频行为。创作激励不是每天都要领的,通常一个周期领一次就够了。为了一周一次甚至一个月一次的点击,专门装一个长期驻留的油猴脚本,属于杀鸡用牛刀。控制台脚本用完即走,不占内存,不留在后台,心理上也更干净。
第三,控制台脚本在调试和临时修改上更方便。你可以在控制台里直接改代码、直接跑、直接看报错,整个过程是可交互的。油猴脚本出了问题,还得去脚本管理页面查日志、重新加载,流程长了一截。
当然,油猴脚本也有它的价值。它适合需要长期、自动、无感知运行的场景。事件驱动式地检测DOM变化、自动重试、后台值守,这些都是纯控制台脚本不太好做的事情。所以在文末我会单独讲一下,怎么把这段控制台脚本改造成油猴脚本,两头通吃。
简单总结:高频周期任务选油猴,低频一次性任务选控制台。我这个脚本解决的就是“偶尔打开后台发现有一排奖励忘了领”的典型场景,控制台方案是最合身的选择。
1.3 控制台脚本的原理一点都不神秘
在写代码之前,先把这个东西的原理说透。浏览器控制台为什么能自动点击页面上的按钮?其实没有任何黑魔法。你在控制台里输入的每一行JS,都是在这个当前页面的上下文里执行的,也就是说,你等于在B站自己的脚本环境里跑了一段代码。你写的document.querySelector能拿到页面元素,你写的btn.click()能触发按钮点击,是因为控制台被浏览器赋予了当前页面级别的权限,其他网页脚本能做的事,你在控制台里也能做。
这就解释了为什么纯控制台脚本不需要任何插件。油猴脚本的本质,其实也就是借助扩展机制,往页面里注入一段JS,最后执行的还是浏览器原生API。控制台省掉的是中间这层注入壳子,直接把代码交到开发者工具里跑。理解了这一层,后面所有代码看起来就都不神秘了。
2. 脚本整体设计:别写死按钮,要“全页面通用”
2.1 通用匹配思路:按文案识别而不是按ID识别
标题里有个关键词特别值得注意——“全游戏领奖页面通用”。如果我是写死某个按钮的ID或者class,那这个脚本换个页面就废了,谈何通用?
所以设计脚本的第一步,就是放弃“精确寻址”,改用“语义识别”。什么意思?就是我不关心这个“领取”按钮具体长什么样,叫什么ID,属于哪个div,我只关心它的文字内容是不是“领取”“立即领取”“领取奖励”之类。用IT行业的话说,前者是强耦合实现,后者是面向行为编程。
实际做法很简单:遍历页面上所有的button元素,以及长得像按钮的a标签、div标签,把它们的textContent取出来,去掉首尾空格,再用正则去匹配“领取”相关的关键词。匹配成功,说明这个元素大概率就是一个可点的领奖按钮,然后我再判断一下它是不是可点击状态,是的话就直接执行click。
这套逻辑的好处是显而易见的:不管B站后台怎么改版,只要按钮文案里还带着“领取”两个字,脚本就还能用。事实上我用它在B站的游戏激励页、活动奖励页、任务中心页都跑过,表现都很稳定,这就是文案匹配的通用性带来的收益。
这里引出一个比较常见的点:在JS里判断字符串是否包含某个关键词,最经典的方式就是用正则或者includes方法。像我这里因为要同时匹配“领取”“立即领取”“领取奖励”多个词,所以用正则最方便,一个表达式全搞定。热搜词里也有人问“js判断字符串是否包含”,这里顺便解释一下,includes只适合精确判断单个字符串,正则适合批量模糊匹配,按需选择就行。
2.2 设计取舍:为什么不锁死固定类名
有人可能疑惑,B站页面上那些领取按钮明明是有固定类名的,直接选类名不是更精准吗?确实更精准,但也更脆弱。B站前端上线新版本之后改类名是常有的事,今天叫reward-btn,明天可能就改成income-btn-2025,你看上去只是页面换了个皮肤,脚本却直接失联了。
更重要的原因是,B站后台其实是一个很大的系统,“创作者激励”只是其中一个模块,不同分区、不同活动的领奖页面很可能由不同团队开发。有的页面用el-button,有的页面用原生button,有的甚至是一个div被JS绑定了点击事件。这种页面之间共同的唯一公约数是什么?只有用户能看到的那个文案。所以文案匹配在这个场景下不是偷懒,而是经过取舍之后最稳的方案。
当然,文案匹配也不是完全没有误触风险。比如页面上可能有一个“领取规则”的弹窗按钮,“领取记录”的Tab页,这些文案里也含有“领取”两个字,但它们不是领奖按钮。为了解决这个问题,我在代码里做了两个处理:一是正则要求“领取”后面的字不能是“规”“记”“说”之类,降低误命中率;二是把“立即领取”“领取奖励”这类高置信度词放在优先匹配的前面。对付这种多场景页面,宁可少点一个,也不要乱点一个,所以脚本整体上遵循“宁缺毋滥”的原则。
2.3 完整核心代码(基础版)
下面这段就是脚本的核心部分,我加了不少注释,常规情况下直接能用。
(function () { // 最大点击次数,防止意外循环导致页面卡死 const MAX_CLICK = 20; // 匹配文案:优先精确词组,其次才用宽松正则 const HIGH_CONFIDENCE = ['立即领取', '领取奖励', '一键领取']; const NORMAL_RULE = /领取(?!规则|记录|说明|详情)/; // 找出页面里所有可点击元素:button、带role的标签、a标签 const candidates = Array.from( document.querySelectorAll('button, a, [role="button"], [class*="btn"]') ); let clickedCount = 0; const clickedTexts = []; for (const el of candidates) { if (clickedCount >= MAX_CLICK) break; const text = (el.textContent || '').trim(); if (!text) continue; // disabled 状态直接跳过 if (el.disabled || el.getAttribute('aria-disabled') === 'true') continue; // 高置信度词组优先 const isHigh = HIGH_CONFIDENCE.some(word => text.includes(word)); // 普通正则兜底 const isNormal = NORMAL_RULE.test(text) && text.length <= 20; if (isHigh || isNormal) { el.click(); clickedCount++; clickedTexts.push(text); console.log('[已点击]', text); } } if (clickedCount === 0) { console.log('没有找到可领取的按钮,请确认当前页面是否为领奖页面'); } else { console.log(`执行完毕,共点击 ${clickedCount} 个按钮:`, clickedTexts.join('、')); } return clickedCount; })();需要说明的是,这段代码是“一次性扫描”的执行模型,适合页面加载完毕之后手动运行。如果你打开页面时奖励还没完全渲染出来,建议先等两三秒再执行,或者下面我给的轮询加强版会更合适。
2.4 轮询加强版:自动等待动态加载的内容
很多领奖页面并不是一次性把按钮全部渲染出来的,而是异步请求、分段加载。尤其是游戏激励页,往往先显示一部分,往下滚动才加载下一批。面对这种情况,一次性的扫描就不够用了。
加强版的做法是做一个带轮询的循环:每2秒自动扫描一次,发现新的可领取按钮就点击,直到连续几轮都扫描不到新按钮才自动停止。这样你贴完代码,脚本就自己在后台“干活”,完全不用管。
(function autoClaim() { const MAX_ROUNDS = 30; const INTERVAL_MS = 2000; let round = 0; let lastClickCount = -1; const CLICK_KEYWORDS = ['立即领取', '领取奖励', '一键领取']; function scanAndClick() { round++; const buttons = Array.from(document.querySelectorAll('button, a, [role="button"]')); let clicked = 0; for (const btn of buttons) { const text = (btn.textContent || '').trim(); if (!text) continue; if (btn.disabled) continue; const matched = CLICK_KEYWORDS.some(k => text.includes(k)); if (matched) { btn.click(); clicked++; console.log('[轮询自动点击]', text); } } console.log(`第 ${round} 轮扫描完成,本轮点击 ${clicked} 个按钮`); // 连续两轮没有新点击,或达到轮次上限,自动停止 if (clicked === 0 && lastClickCount === 0) { console.log('连续两轮无新按钮,自动停止'); return; } lastClickCount = clicked; if (round >= MAX_ROUNDS) { console.log('达到轮次上限,自动停止'); return; } setTimeout(scanAndClick, INTERVAL_MS); } scanAndClick(); })();这里有个细节:我用setTimeout而不是setInterval。如果用setInterval,上一次扫描还没跑完下一轮就开始了,可能造成重复点击。setTimeout只在上一轮完全执行完之后才安排下一轮,避免了这种并发冲突,这也是一个我在实际调试中踩过的坑。
3. 实操过程:从打开控制台到一键跑完
3.1 第一步:找到正确的领奖页面
脚本写好了,关键是怎么用。第一步不是开控制台,而是先找到正确的页面。以B站为例,创作者激励计划的领取入口一般藏在“创作中心”的收益相关模块里,不同时期的入口位置略有不同,但大体路径是:点头像进入创作中心,在左侧菜单找到“收益”或“服务中心”,再找到“激励计划”或“任务中心”相关的子页面。
更省事的办法是直接走B站官方的个人中心链接。你登录之后,在地址栏输入创作中心的网址,回车就能直达。这里我先卖个关子,不写具体域名,以免以后链接路径变动误导大家,但记住一个规律:能用个人中心直达的页面,优先用个人中心,不要从首页一层层点进去,层级越深越容易迷路。
还有一点,如果你在某个页面已经看到了“可领取”的红色角标或者数字提示,那恭喜你,这个页面基本就是脚本的目标页。你可以先手动确认一下页面上确实存在“领取”按钮,再运行脚本,这样成功率最高。
3.2 第二步:打开控制台并粘贴脚本
页面准备好了,接下来就是打开控制台。Windows用户按F12,苹果用户按Option+Command+I,都能直接调出开发者工具。如果某些笔记本需要按Fn+F12,那就顺手按一下。打开之后点击面板顶部的“Console”标签,切到控制台页。
这里有一个很多新手会遇到的坑:第一次打开控制台时,页面会提示“按回车继续”,或者自动定位到一个像输入框的位置。请不要担心,这是正常的,你直接在里面粘贴代码就行。但要注意,有些浏览器为了防止复制粘贴,会在粘贴时弹出确认框,这时候选“允许粘贴”就好。
粘贴完代码之后,按回车执行。正常情况下,你会立刻在控制台里看到脚本输出的日志,比如“已点击 立即领取”“第1轮扫描完成”之类的信息。如果没有日志,多半是脚本被页面本身的过滤规则拦截了,或者你粘贴时不小心贴到了“Sources”面板里,切回Console重新来一遍即可。
3.3 第三步:运行结果观察与二次确认
脚本跑完不代表结束,你还要人工确认一下,不然容易白干。怎么确认?两个维度。
第一个维度是看页面上的按钮状态是否发生了变化。领取成功的按钮通常会变成“已领取”“已完成”之类的灰色不可点状态,或者干脆从页面上消失。你如果看到一排按钮都变成了灰色,说明脚本是真的干活了。
第二个维度是看B站后台的收益记录或激励账单。进入收益明细页面,确认刚才那一笔奖励已经进入账户。这里我特别建议养成“脚本跑完必看账单”的习惯,因为偶尔会遇到点击事件触发了,但后端接口校验失败的情况,这时按钮确实变了,钱却没到账,只有看账单才能发现问题。
3.4 进阶:在iframe嵌套页面里怎么处理
B站后台有一部分页面,尤其是活动页和游戏激励页,采用的是iframe嵌套结构。你看到的“领取”按钮其实并不在顶层页面的DOM里,而在某个iframe内部。这时候直接运行基础版脚本,扫描的是顶层document,当然什么都找不到。
解决办法也很直观:先获取iframe,再进入iframe的contentDocument里去找按钮。同源iframe是可以直接访问的,下面这段代码演示了如何遍历所有iframe并执行点击:
const totalClick = []; const frames = Array.from(document.querySelectorAll('iframe')); for (const frame of frames) { try { const doc = frame.contentDocument; if (!doc) continue; const buttons = Array.from(doc.querySelectorAll('button')); for (const btn of buttons) { const text = (btn.textContent || '').trim(); if (/立即领取|领取奖励/.test(text)) { btn.click(); totalClick.push(text); console.log('[iframe内点击]', text); } } } catch (e) { console.warn('跨域iframe无法访问,跳过', e); } } console.log('iframe内共点击', totalClick.length, '个按钮');遇到跨域iframe时会报SecurityError,这属于浏览器的安全机制,正常情况B站自己的页面都是同域的,不太会遇到。如果真的遇到了,说明你的脚本被限制在了一个隔离的框架里,那就只能手动处理了。
4. 踩坑实录:常见问题与排查技巧
4.1 元素找不到,脚本没反应
这是最高频的问题。脚本贴进去,回车,控制台安静如鸡,一个日志都没有。或者日志确实输出了“没有找到可领取的按钮”,这时候怎么办?
先检查页面状态。是不是按钮还没渲染出来?很多领奖页面的数据请求是异步的,打开页面那一刻按钮并不存在,需要等一两秒。解决方法就是手动等几秒再运行,或者直接用前面写的轮询版脚本,让脚本自己等。
其次是检查匹配文案是否对得上。虽然B站后台大部分领取按钮都叫“领取”,但也有个别页面写的是“领取奖励”的变体,比如“领取贝壳”“领取金瓜子”“确认领取”,这时候基础版的正则可能会漏掉。你可以临时在控制台执行一行代码,把页面所有按钮文字打印出来:
Array.from(document.querySelectorAll('button')).map(b => b.textContent.trim())这样你就能看到页面上到底有哪些按钮文案,然后对症下药修改正则。
4.2 点击没效果,按钮状态不变
比找不到更让人抓狂的是点完了没反应。点击事件触发了,console日志也打印了,但页面没有任何变化。这种情况大概率是按钮绑定的不是原生click事件,而是mousedown或者pointerdown事件。
原因是很多现代前端框架(比如Vue、React)在高阶组件里为了更快的响应速度,会监听pointerdown而不是click。你用el.click()触发的是click事件,但按钮真正监听的事件压根没被触发,自然就没反应。
解决思路有三种。第一种是改用dispatchEvent派发完整的事件序列:先mousedown,再mousedown对应的mouseup,最后click。第二种是直接触发pointerdown事件:
const event = new Event('pointerdown', { bubbles: true }); btn.dispatchEvent(event);第三种最实用,与其模拟点击,不如直接调用按钮绑定的JS方法。如果页面是用Vue写的,你通常可以通过btn.__vue__来访问组件实例,然后调用它的方法。不过我一般不建议普通用户走到这一步,优先试第一种和第二种就好。
4.3 页面刷新导致脚本中断
B站有些领奖按钮点击之后会触发一次页面刷新,脚本一刷新就断掉了,后面的奖励还没来得及领。这个问题在多个连续领取的场景下特别常见。
我的处理办法是分两段走。第一段,先把所有可领取按钮的标识记录下来(比如按钮在DOM里的索引位置或文案),刷新完成后,第二段脚本针对剩下的按钮继续执行。简单粗暴的版本就是过一会儿再手动重跑一次脚本,不追求一次全领完。
还有一种变体问题是点击之后跳到了新的Tab或者新的页面,原页面被关闭。这种情况基本不可能保持脚本运行了,只能重新打开页面再跑一次。反正这种操作本质上是低频手动任务,多跑一次也不麻烦。
4.4 被风控或验证码拦住了怎么办
这里要先提醒一句:自动领取自己的平台奖励,本身是合理需求,但如果脚本在极短时间内高频点击、一天之内反复触发几十次,平台的风控系统确实有可能介入,弹验证码甚至暂时限制领取。我在实测中也遇到过两次。
我的态度是:脚本是效率工具,不是刷量外挂。正常的使用节奏是“一周一次到两次,每次把当前该领的领完”,千万不要做成定时器全自动每5分钟跑一次。如果遇到验证码,老老实实手动验证,验证完之后减少操作频率,一般不会有后续问题。
另外提醒一点,控制台里不要去做任何涉及修改接口参数、篡改数据、绕过支付之类的操作,那是明确的违规行为,轻则封号,重则承担法律责任。我们写自动领奖脚本的目的,是为了把该领的钱顺利领回来,不是研究怎么薅平台。
4.5 常见问题速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 脚本运行无日志 | 页面未加载完成或选择器不匹配 | 等待后重跑,先打印按钮文案核对 |
| 点击后无变化 | 按钮监听pointerdown而非click | 派发pointerdown事件 |
| 只领到一部分 | 页面动态分页加载 | 使用轮询加强版脚本 |
| iframe里的按钮点不到 | 按钮在iframe内部 | 遍历contentDocument |
| 连续执行弹出验证码 | 触发风控 | 降低频率,手动验证 |
| 刷新后脚本中断 | 按钮点击触发了跳转 | 重开页面重新执行 |
5. 扩展玩法与最后的心得
5.1 从控制台脚本到油猴脚本的迁移
虽然标题主打“无需油猴脚本”,但如果你确实觉得这个功能好用,想让它以后自动运行,迁移到油猴也就是几分钟的事。油猴脚本的核心就是一个用户脚本声明块,把控制台代码再包一层就行。
// ==UserScript== // @name B站自动领取创作激励 // @namespace https://your-site.example/ // @version 1.0 // @description 自动点击B站领奖页面的领取按钮 // @match https://member.bilibili.com/* // @match https://*.bilibili.com/* // @grant none // ==/UserScript== (function () { 'use strict'; // 把之前的轮询脚本原样放到这里即可 // 建议加上延迟执行,等页面完全加载 window.addEventListener('load', function () { setTimeout(autoClaim, 2000); }); })();注意@match的匹配规则,要覆盖你常用的后台地址。设置好之后,每次进入对应页面,脚本会在2秒后自动开始扫描。这样你就同时拿到了“控制台免安装”和“油猴自动化”两种姿势,按场景换着用。
5.2 还可以自动化的B站后台操作
顺着这个思路,B站后台其实还有很多类似的“机械性点击”操作可以自动化。比如定期清空消息通知、一键批量领取任务奖励、自动完成每日签到,这些本质上都是“在合适的页面找到合适的按钮点一下”。你只需要把脚本里的正则关键词换一换,比如把“领取”换成“签到”,一个全新的自动化脚本就诞生了。
我个人的习惯是维护一个本地脚本库,每个站点一个文件,里面分门别类放着各种小工具。用到的时候打开控制台,复制粘贴,完事。这种方式比到处找现成油猴脚本靠谱得多,因为你最懂自己的需求,出了问题也知道怎么改。
5.3 关于自动领取,我个人的几点体会
最后再分享一点我自己踩过坑之后的感悟。第一,写自动化脚本之前,一定要先手动把流程完整走一遍,亲眼看清楚按钮长什么样、点击之后有什么反应,再开始写代码。跳过这一步的人,一半以上的时间都花在“为什么脚本没反应”的排查上。
第二,脚本要尽量做得“克制”。能匹配一个按钮就匹配一个按钮,能不轮询就不轮询,避免搞出一套失控的自动机器。自动化的意义是节省你的时间,不是让你提心吊胆地盯着它跑,所以稳定和安全始终是第一位的。
第三,也是最重要的一点:这类自动领奖脚本本质上是“锦上添花”,它不能帮你提升内容质量,也不能让不达标的激励额变多。真正让创作者激励计划有价值的,永远是你的视频本身。把脚本当作效率工具用就好,别指望它能改变创作的底层逻辑。
脚本写出来就是为了让你省心的。如果你在用的过程中遇到页面改版导致匹配失效,或者发现了更好的通用匹配规则,欢迎按这个思路自己迭代一版。控制台里的世界很自由,折腾本身就是乐趣。