拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

网页自动点赞脚本原理与实现:DOM定位、事件模拟与稳定性优化

网页自动点赞脚本原理与实现:DOM定位、事件模拟与稳定性优化

刷抖音直播的时候,很多人都有过这种体验:蹲在直播间里,手闲着没事就想帮主播点亮爱心,点久了手指酸,不点又觉得没参与感。后来我试着在网页版抖音直播间的控制台里,用一段JS脚本把点赞这个动作自动化,效果还行。今天把这套东西从原理到调试完整记录下来,给还在手动点的朋友一个参考。先说清楚:这篇文章只讲技术实现,不鼓励用来刷量或者对抗平台规则,自己学习研究用。

1. 点赞按钮在页面里到底长什么样:从DOM结构说起

1.1 网页版直播间不是App,但DOM结构一直在变

抖音网页版直播间的地址是 live.douyin.com/xxx,在电脑浏览器里打开。很多人以为网页版跟手机App一样是个黑盒,实际上网页上所有元素都是HTML+JS渲染出来的,只要能打开开发者工具,就能看到点赞按钮的"真身"。这也是写自动点赞脚本最基础的一步:先认清目标。

我第一次写这个脚本时,想当然地猜测按钮上肯定有"点赞"两个字,于是用document.querySelector('button')去遍历。结果发现直播间里按钮太多,根本分不清哪个是点赞。后来在控制台的Elements面板里手动定位才明白,抖音网页版经过了很多次改版,点赞按钮不一定是button标签,更多时候是一个div,里面包着一个svg图标和一个点赞数字。

更重要的是,抖音前端的class名很多是带哈希后缀的,比如likeBtn__a3f9c2,这意味着如果你把某个class名写死,下次页面更新或者直播间开播状态不同,这个class可能就变了。所以脚本里的选择器一定要写成"模糊匹配",而不是"精确匹配"。

1.2 定位按钮的三层备选方案

找点赞按钮,我一般会按优先级试三种方案。

第一,看元素有没有data属性。抖音的运营体系中,很多关键交互元素会带>const selectors = [ '[data-e2e="like-button"]', '[class*="likeButton"]', '[class*="LikeButton"]', '[class*="like-btn"]', ]; function getLikeBtn() { for (const sel of selectors) { const el = document.querySelector(sel); if (el) return el; } return null; }

document.querySelector返回第一个匹配项,找到就返回。这里用的是[class*="..."],也就是"属性包含"匹配,可以避免哈希后缀问题。如果多个直播间结构不一样,可以把选择器数组写在配置里,方便随时改。

第三,如果前两种都找不到,那就用文本或结构特征。比如页面上某个区域有"点赞"文案,或者有一个svg图标,可以通过遍历按钮列表,检查里面是否包含特定的path或title。这个方法通用性更强,但性能稍差,适合手动调试,不适合高频循环。

注意:不要在循环里每秒钟执行几百次复杂的DOM遍历,否则CPU会涨得很快。使用简单的querySelector就够了。

定位方案的对比可以看表格:

方案优点缺点适用场景
data属性选择器精准、性能好依赖运营规范,可能不存在优先尝试
class模糊匹配稳定、通用可能匹配到无关元素日常脚本首选
文本/结构遍历最强兜底性能差、代码复杂页面改版后的临时方案

找到按钮后,我建议先手动执行getLikeBtn()并输出,如果能返回一个元素,就说明定位成功。如果返回null,不要急着写点击逻辑,先去Elements面板重新观察。

2. 写一个最小可用的自动点赞脚本:核心逻辑一次讲透

2.1 流程控制:setInterval 和递归 setTimeout 的取舍

自动点赞的本质很简单:找到按钮,点击,等一会儿,再重复。但"等一会儿"用什么方式实现,差别很大。

新手最容易写的是setInterval:

setInterval(() => { const btn = getLikeBtn(); if (btn) btn.click(); }, 300);

这个写法简单,缺点也很明显:如果你在控制台重复粘贴这段代码,会同时启动多个定时器,点赞速度瞬间翻倍,还不好停止。另外setInterval的间隔固定,点击节奏非常"机械",一眼看去就是机器行为。

所以我更推荐"递归setTimeout"的方式,每次执行完等待一段随机时间,再计划下一次:

let running = false; function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function likeLoop() { if (running) return; running = true; while (running) { const btn = getLikeBtn(); if (btn) fireClick(btn); await sleep(150 + Math.random() * 350); } }

likeLoop是一个异步函数,内部是一个while循环,running为true时一直跑。每次点击后随机等待150到500毫秒,这样整体节奏更接近真人。如果想让脚本停止,把running设为false,循环会在下一次等待结束后退出。

这里的核心思想是:不要让脚本自己跟自己打架。后面我会专门讲重复执行的问题。

2.2 模拟点击的几种方式:click()、dispatchEvent、mousedown

找到按钮之后,怎么"点"它?大部分人第一反应是element.click(),这个对原生事件没问题,但抖音直播间用的是前端框架,很多事件是合成事件,直接调用click()可能会无效,尤其是按钮绑定在React根节点上时。

更可靠的做法是构造一个可冒泡的MouseEvent并派发:

function fireClick(el) { if (!el) return; el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window, })); }

bubbles: true表示事件会向上冒泡,这样React、Vue这类框架就能在根节点监听到。说实话,我在实际测试中发现,抖音网页版的点赞按钮对dispatchEvent的响应比click()更稳定,所以后续所有脚本都统一用这个函数。

还有一种情况:按钮可能被一个透明遮罩层盖住,直接点击事件落到遮罩层上。此时可以检查该元素是否可点击,或者用document.elementFromPoint计算坐标。不过这种情况在直播间里比较少见,真遇到了优先看是不是点击位置被弹层遮挡。

2.3 随机延时与计数刷新:别把自己刷成"机器人"

自动点赞脚本如果不加随机延时,时间间隔完全一致,很容易被识别。更重要的是,抖音直播间的点赞有"连击特效",用户连续点击时,页面上的爱心会飞出。脚本模拟的关键不只是点击,而是让每次点击间隔有一定波动。

我用了一个简单的随机公式:

await sleep(150 + Math.floor(Math.random() * 350));

最小150毫秒保证点击不会过快,最大500毫秒保证整体频率不至于太低。实际观察下来,这个节奏一分钟大约能点80到120下,和真人高强度参与差不太多。

此外,还要考虑点赞数的刷新。点赞按钮旁边通常有个数字,点击后数字会增加。如果你想确认脚本真的生效,可以在循环里读取这个数字,看它是否变化。但要注意,抖音的数字可能是缩写,比如"1.2w",连续点击一万次才会跳一次,所以用文本变化判断不能太敏感。更好的方式是定期抓取数字并打印到控制台,人工确认。

2.4 一个可以直接跑的整合版脚本

把这些零散逻辑装进一个自执行函数里,挂在window上,方便随时启停。这个版本也是我日常调试用的基础版:

// 抖音直播间自动点赞(学习用途) (function () { const CONFIG = { minInterval: 150, maxInterval: 500, maxNotFound: 20, }; const selectors = [ '[data-e2e="like-button"]', '[class*="likeButton"]', '[class*="LikeButton"]', '[class*="like-btn"]', ]; function getLikeBtn() { for (const sel of selectors) { const el = document.querySelector(sel); if (el) return el; } return null; } function fireClick(el) { el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window, })); } window.__autoLike = window.__autoLike || { running: false, timer: null, notFound: 0, }; function loop() { if (!window.__autoLike.running) return; const btn = getLikeBtn(); if (btn) { fireClick(btn); window.__autoLike.notFound = 0; } else { window.__autoLike.notFound++; if (window.__autoLike.notFound >= CONFIG.maxNotFound) { stop(); console.log('未找到点赞按钮,自动停止'); return; } } const delay = CONFIG.minInterval + Math.random() * (CONFIG.maxInterval - CONFIG.minInterval); window.__autoLike.timer = setTimeout(loop, delay); } function start() { if (window.__autoLike.running) return; window.__autoLike.running = true; window.__autoLike.notFound = 0; loop(); console.log('自动点赞已启动'); } function stop() { window.__autoLike.running = false; if (window.__autoLike.timer) { clearTimeout(window.__autoLike.timer); window.__autoLike.timer = null; } console.log('自动点赞已停止'); } window.startAutoLike = start; window.stopAutoLike = stop; })();

在浏览器控制台执行一次后,输入startAutoLike()启动,输入stopAutoLike()停止。如果你用的是Tampermonkey,也可以把这段代码包在// ==UserScript==头部里,配合@match规则让它在直播页面自动加载。

3. 脚本写出来后必须过的三道坎:变量失效、动态加载和重复绑定

3.1 为什么页面一滚动或切Tab,选择器就失效了

这是我踩过的第一个坑。一开始我在循环外面先取了一次按钮,然后直接在循环里点击那个变量:

const btn = getLikeBtn(); setInterval(() => btn.click(), 300);

刚开始还好,过了几秒就完全没反应了。原因很简单:直播间的页面是动态更新的,比如点赞数变化、弹幕出现、礼物特效,都会引起局部DOM重建。你保存的旧按钮早就被浏览器移除了,再点击一个不再存在于页面上的节点,自然没效果。

正确的做法是:每次点击前都重新获取按钮。也就是把getLikeBtn()放在循环体里面,不要提前缓存。这会多消耗一点查找时间,但换来的是稳定性。

调试的时候,可以用一个很有用的属性来判断节点是否还连着页面:

btn.isConnected

如果返回false,说明这个节点已经从DOM树里移除了,继续点击没有任何意义。看到false,不要怀疑脚本写错了,先检查是不是缓存了旧节点。

3.2 抖音直播间使用WebSocket推送状态,点赞区域会不断刷新

抖音直播间的数据不是静态的。你打开浏览器开发者工具的网络面板,能看到有一条WebSocket连接,不停地推送直播间状态。我自己观察到的现象是:每当点赞数变化或者弹幕更新,DOM会被部分替换。每次点赞按钮数字更新时,外层容器也会刷新。

这带来一个连锁问题:如果你的脚本把按钮元素存起来,只会在刷新后的第一瞬间失效。所以脚本设计上一定要"每次点击都现找",甚至可以把它封装成一个独立的函数,方便替换。这里我还会在找不到按钮时做个降级处理:

let notFoundCount = 0; async function likeLoop() { while (running) { const btn = getLikeBtn(); if (btn) { fireClick(btn); notFoundCount = 0; } else { notFoundCount++; } await sleep(300); } }

如果连续很多次找不到按钮,说明页面结构可能变化了,或者直播间已经关闭。这时候可以让脚本自动停止,避免空转。

有人可能会问:为什么不用MutationObserver监听DOM变化,然后再找出按钮?对于点赞这种高频操作,MutationObserver反而会把简单问题复杂化。你监听整个页面,回调函数会被弹幕、礼物、聊天频繁触发,而你的真正需求只是"每间隔几毫秒点一下",所以轮询就够了。

3.3 脚本入口重复执行导致多个实例,怎么处理

这是另一个常见问题:在控制台里粘贴一次代码启动了点赞,后面又粘贴一次,结果两个循环同时在跑。点赞速度翻倍,页面开始卡顿,却不知道停哪个。

解决办法是加"锁"。"锁"的核心思想是:启动新循环前,先尝试停止旧的。同时用全局变量作为运行标志。

一个实用的写法:

window.__like = window.__like || { running: false, timer: null }; function startAutoLike() { stopAutoLike(); window.__like.running = true; function loop() { if (!window.__like.running) return; const btn = getLikeBtn(); if (btn) fireClick(btn); window.__like.timer = setTimeout(loop, 150 + Math.random() * 350); } loop(); } function stopAutoLike() { window.__like.running = false; if (window.__like.timer) { clearTimeout(window.__like.timer); window.__like.timer = null; } }

window.__like挂载在全局,重复调用startAutoLike()时,会先执行stopAutoLike()停掉旧循环。这样无论粘贴多少遍,最终只会有一个循环在跑。这个过程其实和并发控制里的"单例模式"很像,理解之后,以后写别的自动化脚本也能用上。

3.4 调试小技巧:给脚本加一个运行状态面板

跑了一段时间后,你可能想知道脚本现在还活着没。最简单的方式是在控制台里定时输出状态:

setInterval(() => { console.log(`运行中: ${window.__autoLike.running}, 已连续找不到按钮: ${window.__autoLike.notFound}`); }, 5000);

不过频繁输出日志会拖慢页面,尤其是当你开着开发者工具的Console面板时。更好的做法是每隔一段时间把状态写到页面标题上,比如document.title = '点赞中' + count,这样抬头看浏览器标签页就知道脚本状态。这个小技巧在长时间挂机的时候特别实用。

4. 我把脚本挂到实际直播间的调试记录:性能与稳定性优化

4.1 长时间运行时的CPU和内存问题

脚本能跑起来只是第一步,真正让它稳定运行一小时,又是另一回事。我自己的测试是在一个普通笔记本浏览器上,开着开发者工具,脚本运行了大概半小时,发现风扇开始转,内存占用在涨。

排查下来,问题出在两个地方。第一,控制台频繁打印日志,而日志里的对象一直会被引用;第二,循环里执行了一些较重的DOM操作,比如每轮都查询多个选择器,还遍历数组。优化后,我只保留一个最可能命中的选择器,并把日志改成每十次打印一次,CPU占用立刻降下来了。

如果只是图省事,把console.log去掉,就足够应对大多数场景。对于想持续跑很久的朋友,建议把脚本放到Tampermonkey这类用户脚本管理器里,并且设置好停止条件,而不是一直开着控制台。

4.2 页面网络断开、直播间切换后脚本还在空转

直播间可能断线重连,或者主播下播后跳转到了别的主页。这时候点赞按钮不存在了,但脚本还在循环查找。虽然没实际点击,但每几百毫秒做一次DOM查询也是一种浪费。

我的处理办法是监听页面URL的变化。直播间地址里的/live/后面通常会跟一个房间号,房间号变了,说明切换了直播间。可以在脚本里保存当前房间号,每次循环时对比一下,发现变化就自动停止,或者重新初始化。

const roomIdMatch = location.pathname.match(/\/live\/(\d+)/); let currentRoomId = roomIdMatch ? roomIdMatch[1] : '';

然后每次循环检查一次currentRoomId,如果对不上就return。这个思路同样适用于判断直播间是否仍在直播状态。如果主播下播后页面自动跳转到个人主页,路径变了,脚本也能敏锐地发现。

4.3 停止条件与自动停止

如果你在一个大主播的直播间里开自动点赞,十几分钟后点赞数可能就不再增加了。因为抖音对单个用户每天的点赞数量有限制,达到上限后,按钮看起来可以点,但实际上后台已经不计数。此时脚本继续跑纯属浪费资源。

可以写一个简单的"数字不变化"检测。比如每20次点击后,读取点赞计数器文本,如果连续5次都没变,就自动停止。要注意数字可能是缩写,比如"1.2w",连续很多次点击才会跳0.1w,所以阈值不要设得太死。

更好的方式是看按钮本身的状态。有些版本的点赞按钮在达到当日上限后会变成灰色,或者不再有飞心特效。如果你发现按钮状态异常,直接停止脚本并提示自己。

我做过一个小规模的长时间测试,数据大概是这样:

运行时间平均点击间隔点赞数字变化CPU占用备注
5分钟500ms1.2w -> 1.3w3%正常
30分钟500ms10w -> 10.2w8%页面开始有少量卡顿
60分钟500ms10.2w不再增加15%大概率触发当日上限

从测试结果能明显看出,长时间运行后即使脚本还在点击,点赞数也不再变化。所以不要迷信"挂得越久越好",一定要配合状态检测。

5. 关于自动点赞脚本的边界:能做什么,不该做什么

5.1 平台规则与账号风险

这部分不是套话,是真的需要大家知道:抖音用户协议里明确写了,用户不得利用技术手段批量操作、自动生成或刷量。自动点赞脚本如果被平台风控识别,轻则点赞功能被临时限制,重则整个账号被标记为异常账号。

我自己写这个脚本的初衷,是想研究网页端事件绑定和DOM更新机制,纯属技术学习。试的时候用的是一个专门用来测试的小号,频率也控制在人手动能达成的范围内。我真心不建议拿大号去刷,尤其不要在别人直播间里开脚本,不然好心帮忙反而坏菜。

5.2 把脚本改造成"手动辅助"而不是"全自动"

如果你想在直播间里偶尔帮主播助推,又不想一直点点点,可以考虑把脚本改成手动开关模式。比如监听一个快捷键:

window.addEventListener('keydown', (e) => { if (e.key === 'p') { if (window.__autoLike.running) { stopAutoLike(); } else { startAutoLike(); } } });

按一下p键启动,再按一下停止。这种"半自动"方式,主动权始终在你手上,既省力气,又不容易触发风控。同理,你还可以把速度调慢一点,比如平均每秒2次左右,看起来更像真人。

5.3 后续可以扩展的方向

学会了点击元素和动态DOM处理,能玩的东西其实不少。比如在直播间里自动发送弹幕、监听礼物特效、统计某位主播的点赞变化趋势,核心思路都一样:找到对应的DOM节点,绑定事件或者定时执行。

不过有一点我始终提醒自己:脚本能做,不代表应该做。做技术研究的时候,尽量选择自己的测试账号或者公开的Demo页面,不要去干扰正常用户,更不要去破坏平台的数据可信度。把这些边界守住,写脚本才是一件有意思的事。

最后再分享一个小技巧:如果你发现直播间改版后之前的JS脚本全部失效,不要急着写复杂逻辑,先打开控制台重新点一遍按钮,把新元素的结构记录下来。前端再怎么变,点赞这个交互总归会有个节点,只要定位到它,脚本的核心逻辑基本不用动。

返回列表