JavaScript前端自动化脚本:Bilibili自动化批量取消关注
环境准备1.游览器-隐私-跟踪-跟踪防护-基本2.关闭广告拦截插件程序和核心function startUnfollowProcess(nextButtonName 下一页) { console.log(开始执行取消关注流程“下一页”按钮文本为: ${nextButtonName}); function unfollowUsers() { const btns document.querySelectorAll(.gray); console.log(找到 ${btns.length} 个已关注按钮准备开始取消关注。); if (btns.length 0) { console.log(当前页面没有可取消关注的用户准备翻页。); goToNextPage(); return; } btns.forEach((btn, index) { setTimeout(() { btn.click(); console.log(已取消关注第 ${index 1} 个用户。); if (index btns.length - 1) { setTimeout(goToNextPage, 1500); } }, index * 1500); }); } function goToNextPage() { console.log(正在寻找“下一页”按钮...); let nextButton Array.from(document.querySelectorAll(button, a)).find( el el.innerText el.innerText.includes(nextButtonName) ); if (nextButton) { console.log(成功找到“${nextButtonName}”按钮准备点击。); nextButton.click(); console.log(页面已跳转等待内容加载...); setTimeout(unfollowUsers, 3000); } else { console.log(未找到“下一页”按钮任务完成或已到达最后一页。); } } unfollowUsers(); } startUnfollowProcess(下一页);运行和使用关注者-检查-开发者工具-控制台-输入js脚本代码-回车开始运行程序逻辑探讨选择要研究的动作本次js脚本共研究3个主要动作取消关注当前页用户找到所有 “已关注” 按钮并点击自动翻页完成当前页操作后找到并点击 “下一页” 按钮循环执行重复以上步骤直到无法找到 “下一页” 按钮1.找到“已关注”按钮——CSS Selector 是网页的 XPath可以看到页面元素的 HTML 标签为div该元素的类名有follow-btn__trigger和gray而document.querySelectorAll(.gray)的作用是选择所有包含gray类名的元素不管它还有没有其他类名很好我们知道了类名是什么了现在可以开始第一步构造负责在当前页面上查找并点击所有的 “取消关注” 按钮。我们可以在一开始声明一个名为unfollowUsers的函数这个函数封装了在单页上取消关注所有用户的逻辑。// 定义取消关注用户的函数 function unfollowUsers() {然后使用document.querySelectorAll(.gray)在整个网页的文档树查找所有匹配指定 CSS 选择器的元素它会匹配所有 class 属性中包含 gray 的 HTML 元素如果在其他网站使用你需要根据实际情况修改这个选择器比如可能是 .unfollow-btn 或 .followed 等。// 查找所有 class 包含 gray 的元素假设这是“已关注”按钮的样式 const btns document.querySelectorAll(.gray);使用 const 关键字声明一个常量 btns 来存储找到的所有元素而querySelectorAll 返回的是一个 NodeList节点列表可以把它想象成一个包含了所有匹配元素的数组。因为 btns 这个变量本身它的引用不会改变它永远指向那个 NodeList。虽然 NodeList 里的内容可能会变但变量的引用没变。接下来我们需要构思施行点击取消关注并且遍历列表功能btn.click(); // 模拟点击按钮btn.click()这行代码会模拟用户在浏览器中点击btn这个元素。当它执行时就相当于你用鼠标左键点击了那个 “已关注” 按钮从而触发取消关注的操作。然后我们需要遍历最简单是使用forEach()#例子 var fruits [apple, banana, cherry]; fruits.forEach(function(item, index) { console.log(index, item); }); #等同于 fruits.forEach((item, index) console.log(index, item));forEach()中index可以读取索引意味着不用再指定参数但在调用console.log时我们需要将索引值1.到这一步我们构建了最关键的功能// 定义取消关注用户的函数 function unfollowUsers() { // 查找所有 class 包含 gray 的元素假设这是“已关注”按钮的样式 const btns document.querySelectorAll(.gray); // 打印找到的按钮数量 console.log(找到 ${btns.length} 个已关注按钮准备开始取消关注。); // 遍历所有找到的按钮 btns.forEach((btn, index) { // 使用 setTimeout 创建延迟避免操作太快 // 每个按钮的点击时间间隔为 1.5 秒 setTimeout(() { btn.click(); // 模拟点击按钮 console.log(已取消关注第 ${index 1} 个用户。);完成了第一页循环我们需要解决更多页数可以看到“下一页”包含button、vui_button、vui_pagenation--btn、vui_pagenation--btn-side等类表明这是一个分页组件相关的按钮。现在我们确定了下一页是执行循环的最后一个范围为此我们要在最后调用startUnfollowProcess(下一页)放在整个函数的最外层。// 定义主函数接收一个参数 nextButtonName默认为 下一页 function startUnfollowProcess(nextButtonName 下一页) { //主程序 } // 启动函数开始自动取消关注 startUnfollowProcess(下一页);步骤 1选择候选元素document.querySelectorAll(button, a)作用查找页面中所有button标签和所有a标签。原因“下一页” 通常以「按钮button」或「链接a」的形式存在所以先把这两类元素都列为候选。步骤 2转换为数组Array.from(document.querySelectorAll(button, a))作用把querySelectorAll返回的「NodeList节点列表」转换成「数组」。原因NodeList本身没有数组的.find()方法转换后才能用数组的查找能力。步骤 3查找符合条件的元素.find(el el.innerText el.innerText.includes(nextButtonName))逻辑在数组中找到第一个满足条件的元素条件分两部分el.innerText确保元素有「文本内容」避免空元素或无文字的元素。el.innerText.includes(nextButtonName)确保元素的文本内容包含nextButtonName这里nextButtonName是 “下一页”。使用let声明一个变量nextButton用来存储找到的那个 “下一页” 元素。如果没找到它的值会是undefined。// 在所有 button 和 a 标签中查找包含指定文本的元素 let nextButton Array.from(document.querySelectorAll(button, a)).find( el el.innerText el.innerText.includes(nextButtonName) );完成整个程序核心后需要加入合适的延时这是确保自动化最关键的一步为什么需要延迟点击 “下一页” 后浏览器需要一些时间来加载新的页面内容。如果立即执行unfollowUsers新页面的用户列表可能还没加载出来导致unfollowUsers什么也找不到。设置 3 秒的延迟是为了给页面留出足够的加载时间。// 遍历所有找到的按钮 btns.forEach((btn, index) { // 使用 setTimeout 创建延迟避免操作太快 // 每个按钮的点击时间间隔为 1.5 秒 setTimeout(() { btn.click(); // 模拟点击按钮 console.log(已取消关注第 ${index 1} 个用户。); // 如果是最后一个按钮 if (index btns.length - 1) { // 等待 1.5 秒后执行翻页 setTimeout(goToNextPage, 1500); } }, index * 1500); // 第一个按钮立即执行第二个1.5秒后以此类推 }); }setTimeout(...)设置一个定时器。unfollowUsers这是一个函数名它告诉定时器在时间到了之后要执行哪个函数。注意这里没有加括号()因为我们不是要立即执行它而是要把它作为一个 “任务” 交给定时器。3000延迟时间单位是毫秒这里代表 3 秒。这个setTimeout调用在 3 秒后会再次调用unfollowUsers函数而unfollowUsers函数执行完毕后又会调用goToNextPage函数。这样就形成了一个无限循环unfollowUsers→goToNextPage→unfollowUsers→goToNextPage...直到goToNextPage函数再也找不到 “下一页” 按钮循环才会自然终止。问题和矛盾为什么不能使用第一页-第二页-第一页-第二页....循环逻辑进行更完美的自动化每次回到第一页都是加载一个全新的页面旧页面的脚本会被销毁“刷新销毁” 是浏览器的正常行为它这样做是为了保证每个页面的独立性和安全性防止旧页面的脚本影响新页面。我们可以尝试使用一下这个方案function startUnfollowProcess(nextButtonName 下一页, firstPageUrl) { if (!firstPageUrl) { console.error(错误请提供关注列表第一页的完整URL作为第二个参数); console.log(例如: startUnfollowProcess(下一页, https://example.com/following)); return; } console.log(开始执行取消关注流程将在第一页和第二页之间循环。); console.log(“下一页”按钮文本为: ${nextButtonName}); console.log(第一页URL为: ${firstPageUrl}); let currentPage 1; function unfollowUsers() { const btns document.querySelectorAll(.gray); console.log(在第 ${currentPage} 页找到 ${btns.length} 个已关注按钮准备开始取消关注。); if (btns.length 0) { console.log(第 ${currentPage} 页没有可取消关注的用户准备${currentPage 1 ? 前往第二页 : 返回第一页}。); goToNextPage(); return; } btns.forEach((btn, index) { setTimeout(() { btn.click(); console.log(已取消关注第 ${index 1} 个用户。); if (index btns.length - 1) { setTimeout(goToNextPage, 1500); } }, index * 1500); }); } function goToNextPage() { if (currentPage 1) { console.log(正在寻找“下一页”按钮准备前往第2页...); let nextButton Array.from(document.querySelectorAll(button, a)).find( el el.innerText el.innerText.includes(nextButtonName) ); if (nextButton) { console.log(成功找到“${nextButtonName}”按钮准备点击。); nextButton.click(); currentPage 2; console.log(正在前往第2页等待内容加载...); setTimeout(unfollowUsers, 3000); } else { console.log(未找到“下一页”按钮无法前往第2页。任务终止。); } } else { console.log(第 ${currentPage} 页操作完成准备返回第一页。); currentPage 1; console.log(正在跳转回第一页: ${firstPageUrl}); window.location.href firstPageUrl; } } unfollowUsers(); } startUnfollowProcess(下一页, YOUR_FIRST_PAGE_URL);完成键入后还需使用命令【123456替换为你的mid】startUnfollowProcess(下一页, https://space.bilibili.com/123456/fans/follow);