简介这是一份面向微博重度用户与前端脚本爱好者的浏览器端工具资源用于在网页版微博中批量隐藏自己发布的全部微博将内容转为仅自己可见适合需要快速清理主页、保护隐私或整理历史动态的场景。资源包共3个文件以JavaScript脚本为核心辅以README说明文档与LICENSE授权文件整体约6KB体积轻量便于直接阅读源码并按说明在Chrome控制台执行。已有768人学习下载说明该方案在同类需求中具备一定参考价值。脚本通过fetch加载远程代码并实例化隐藏对象逐步执行读者可从中了解浏览器控制台调试、远程脚本加载与批量DOM操作的基本思路也可依据说明文档自行调整执行范围适合具备基础JavaScript认知的用户参考使用。1. 一条脚本把微博主页清空weibo-hide-all 到底解决什么问题你有没有过这种时刻翻到自己三年前发的碎碎念脚趾抠地想一条条删又下不去手几百上千条手动点“设为仅自己可见”能点到怀疑人生。weibo-hide-all 就是冲着这个场景来的——一个纯 JavaScript 写的浏览器端脚本跑起来之后自动把你主页时间线上的微博逐条切换成“自己可见”不用删、不用备份、不碰账号密码全程在你自己的浏览器里完成。它适合两类人一是想快速清理历史发言、又不想丢数据的普通用户二是想学 DOM 自动化、滚动加载处理、异步节流这套前端实战套路的开发者。整个项目没有后端、没有数据库、没有构建链一个脚本文件就是全部读起来门槛低改起来也顺手。2. 技术选型与运行方式为什么是纯前端脚本而不是爬虫2.1 选型逻辑浏览器里跑天然带登录态很多人第一反应是写个 Python 爬虫带上 cookie 去调接口批量改可见性。这条路我踩过坑接口有签名参数、有频控、有风控校验改一个字段就可能触发验证而且 cookie 过期就得重新抓维护成本高得离谱。weibo-hide-all 走的是另一条路——把脚本注入到你已登录的页面里用页面自己的会话去点页面自己的按钮。登录态、CSRF token、请求签名全部由页面自己处理脚本只负责“找到那条微博的菜单按钮点开点‘设为仅自己可见’”。这是典型的“用界面驱动代替接口驱动”牺牲一点速度换来极高的稳定性和极低的维护成本。技术栈因此非常克制原生 JavaScriptES6不依赖 jQuery、不依赖任何框架DOM 操作用querySelector/querySelectorAll异步用async/await配setTimeout做节流滚动加载用IntersectionObserver或监听scroll事件触发。没有 npm、没有 webpack你甚至不需要 Node 环境打开浏览器控制台就能跑。2.2 三种运行方式从临时试跑到长期使用最直接的方式是打开微博主页按 F12 进控制台把脚本内容整段粘进去回车。适合先小范围验证效果跑几十条看看行为对不对。第二种是把脚本存成本地.js文件用浏览器开发者工具的 Sources 面板里的 Snippets 功能保存以后每次打开页面右键运行即可不用重复粘贴。第三种是装一个用户脚本管理器比如 Tampermonkey 这类浏览器扩展把脚本包成用户脚本配置match匹配微博域名实现打开页面自动注入。项目本身是脚本形态包一层元数据头就能变成用户脚本// UserScript // name weibo-hide-all // namespace local.weibo.hide // version 1.0 // description 将主页微博逐条设为仅自己可见 // match https://weibo.com/* // grant none // run-at document-idle // /UserScript // 脚本主体直接放在这里document-idle 保证 DOM 已就绪match决定脚本在哪些页面生效写宽一点覆盖个人主页和详情页run-at document-idle让脚本等页面骨架渲染完再执行避免一上来就找不到节点grant none表示不需要任何特权 API纯页面上下文运行这也是它能保持轻量的原因。提示第一次跑建议先把脚本里的“执行条数上限”改成 5 或 10确认每条都正确变成“自己可见”再放开别一上来就全量跑。2.3 目录与文件结构一个文件也要有清晰分层项目虽小但脚本内部按职责分了几个逻辑区块读源码时按这个顺序看不会乱区块职责关键函数/常量配置区节流间隔、单次上限、选择器集中管理CONFIG、SELECTORS工具区等待元素、随机延时、日志输出sleep、waitFor、log采集区抓取当前可见的微博卡片collectCards操作区打开菜单、点击隐藏项、确认结果hideOne、hideAll调度区滚动加载 循环消费队列main、autoScroll把选择器抽成常量是这类脚本的血泪经验——微博前端改版频繁class 名一变动散落在各处的选择器要改十几处集中到SELECTORS里只改一个对象就行。3. 核心代码拆解从找到卡片到点下“自己可见”3.1 配置与选择器把易变的东西关进笼子const CONFIG { minDelay: 800, // 每条之间最小间隔毫秒 maxDelay: 2000, // 最大间隔制造随机性 maxPerRun: 200, // 单次运行最多处理条数防止跑飞 scrollStep: 1200, // 每次滚动距离 scrollWait: 1500 // 滚动后等待新内容加载的时间 }; const SELECTORS { card: article, // 微博卡片容器 moreBtn: [aria-label更多], // 卡片右上角“更多”按钮 menuHide: li:has-text(设为仅自己可见), // 菜单项示意实际用文本匹配 cardText: .woo-box-flex // 卡片正文区域用于日志标识 };CONFIG里两个延时参数是核心minDelay和maxDelay之间取随机值而不是固定间隔。固定间隔的请求节奏太规律容易被识别为自动化行为随机化之后更像人手操作。maxPerRun是后悔药——万一选择器写错、误点了别的菜单项上限能保证损失可控。SELECTORS里的menuHide用了伪代码式的文本匹配写法实际实现中因为 CSS 选择器不支持:has-text通常改成先取菜单容器再遍历子项比对textContent这一点在 3.3 节展开。3.2 采集卡片处理滚动加载的两种姿势微博主页是无限滚动DOM 里永远只有当前视口附近的十几条所以不能一次性querySelectorAll拿全量必须“滚动—采集—消费”循环。async function collectCards() { const nodes document.querySelectorAll(SELECTORS.card); const cards []; nodes.forEach((node) { // 用卡片内的时间戳或正文片段做去重键避免重复处理 const key node.innerText.slice(0, 40); if (key !seen.has(key)) { seen.add(key); cards.push(node); } }); return cards; } async function autoScroll() { const before document.body.scrollHeight; window.scrollBy(0, CONFIG.scrollStep); await sleep(CONFIG.scrollWait); const after document.body.scrollHeight; // 高度不再增长说明到底了 return after before; }collectCards的关键在去重滚动过程中同一条微博可能被多次采集用正文前 40 个字符做键存进seen集合简单但够用。autoScroll用scrollHeight前后对比判断是否还有新内容——高度不再变化就说明加载到底返回false让主循环退出。这里scrollWait给 1500 毫秒是保守值网络慢的时候可以调大调太小会在内容还没渲染完时就采集导致漏条。3.3 单条操作菜单交互的完整链路这是整个脚本最容易翻车的地方。点“更多”按钮弹出菜单菜单是异步渲染的点完立刻找菜单项大概率找不到必须等待。async function hideOne(card) { const moreBtn card.querySelector(SELECTORS.moreBtn); if (!moreBtn) return false; moreBtn.click(); // 等待菜单容器出现最多等 3 秒 const menu await waitFor( () document.querySelector([rolemenu]), 3000 ); if (!menu) return false; // 遍历菜单项按文本匹配“设为仅自己可见” const items Array.from(menu.querySelectorAll(li, [rolemenuitem])); const target items.find((el) el.textContent.includes(设为仅自己可见) ); if (!target) { // 没找到就关掉菜单避免遮挡后续操作 document.body.click(); return false; } target.click(); await sleep(randomBetween(CONFIG.minDelay, CONFIG.maxDelay)); return true; }逐段说moreBtn.click()触发菜单弹出waitFor是一个轮询函数每隔 100 毫秒检查一次条件超时返回null这是处理异步渲染的标准套路比写死setTimeout可靠得多菜单项匹配用textContent.includes而不是 class 选择器因为菜单项的 class 名经常变但“设为仅自己可见”这几个字是产品文案相对稳定。找不到目标项时主动document.body.click()关闭菜单否则菜单浮层会挡住下一条卡片的按钮导致后续全部失败——这个细节不处理脚本跑到第二条就卡死。3.4 主调度串行消费 滚动补给async function main() { let processed 0; while (processed CONFIG.maxPerRun) { const cards await collectCards(); if (cards.length 0) { const hasMore await autoScroll(); if (!hasMore) break; // 到底了收工 continue; } for (const card of cards) { if (processed CONFIG.maxPerRun) break; const ok await hideOne(card); if (ok) { processed; log(已处理 ${processed} 条); } await sleep(randomBetween(CONFIG.minDelay, CONFIG.maxDelay)); } } log(运行结束共处理 ${processed} 条); }调度逻辑是“先消费当前批次消费完再滚动补给”。注意这里是串行for...of配await不是Promise.all并发——并发点击会同时弹出多个菜单互相遮挡而且请求节奏过于密集。串行虽然慢但每条之间有随机延时整体行为更像人在操作。processed计数同时受maxPerRun约束达到上限立即退出这是防止脚本失控的最后一道闸。4. 避坑与排查跑不起来时先看这几条4.1 现象脚本报错“Cannot read properties of null”原因几乎都是选择器失效。微博前端改版后article标签或“更多”按钮的aria-label变了querySelector返回null后续.click()直接抛异常。解决办法是在控制台手动执行document.querySelectorAll(article).length如果返回 0说明卡片选择器要换再手动点开一条微博的菜单用元素检查工具看菜单项的真实结构把SELECTORS里的值更新掉。养成习惯每次微博大改版后先跑一遍选择器自检。4.2 现象前几条成功后面全部失败典型原因是菜单浮层没关掉。hideOne里如果没找到目标菜单项就直接return false浮层还开着下一条卡片的“更多”按钮被遮挡点击落到了浮层上。解决就是 3.3 节里那句document.body.click()——任何异常分支都要保证菜单被关闭。另外确认waitFor的超时时间够不够网络慢时 3 秒可能不够调到 5 秒试试。4.3 现象跑着跑着页面卡死、浏览器无响应多半是死循环。autoScroll判断“到底”的逻辑依赖scrollHeight不再增长但如果页面有懒加载占位符导致高度反复微调hasMore永远返回true主循环就停不下来。加一个滚动次数上限比如最多滚 50 次作为兜底超过就强制break。同时maxPerRun一定要设别图省事改成Infinity。4.4 现象部分微博变成了“仅自己可见”部分没变先看是不是被去重逻辑误伤。collectCards用正文前 40 字符做键如果两条微博开头文字完全相同比如都是转发同一句话后一条会被当成重复跳过。把去重键改成“正文 时间戳”的组合或者干脆用卡片 DOM 节点本身做WeakSet去重更稳妥。另一种可能是操作太快菜单还没渲染完就点了适当调大minDelay。4.5 现象脚本在控制台能跑包成用户脚本后不生效检查match是否覆盖了实际页面地址微博有多个域名和路径变体写窄了就不注入。再检查run-at如果设成document-start脚本执行时 DOM 还没渲染所有选择器都拿不到东西改成document-idle。最后确认脚本管理器没有对该域名禁用。5. 二次开发把它改成你自己的批量操作框架这个项目的价值不止于“隐藏微博”。它的骨架——配置区、选择器区、采集、单条操作、串行调度——是一个通用的“列表页批量操作”模板。把hideOne里的菜单项文本从“设为仅自己可见”换成“删除”就是批量删除器换成“取消关注”配合粉丝列表页的选择器就是批量取关工具。改造时我一般按这个顺序动刀第一步把SELECTORS和CONFIG抽成独立对象甚至外部 JSON让同一份调度代码适配不同页面。第二步把hideOne抽象成performAction(card, actionName)动作名从配置读菜单项匹配逻辑复用。第三步给main加一个“干跑模式”dryRun: true只采集和匹配、不真正点击先在控制台打印“将要操作第 N 条正文片段”确认匹配无误再关掉干跑。这个模式救过我很多次——有次选择器写错干跑输出显示匹配到了导航栏的按钮要是直接真跑就点到别的地方去了。验证改造是否成功别只看“跑完了没报错”。跑完之后手动抽查随机翻几条处理过的条目确认状态真的变了再翻几条没处理过的确认没被误伤。数量上对一下日志里的processed和实际变化条数是否一致不一致就说明有操作失败但被静默吞掉了去hideOne的各个return false分支加日志定位。还有一个容易被忽略的点这类脚本依赖页面结构而页面结构会变。我的习惯是每次运行前先跑一段自检代码检查所有SELECTORS是否都能命中至少一个元素命中不了就中止并提示“选择器可能已失效”。从那以后我每次改这类 DOM 自动化脚本都强制先走一遍干跑加选择器自检再放开全量省下来的返工时间远比写自检的几分钟多。希望帮到你。本文还有配套的精品资源点击获取