1. 很多人装完油猴后三天就放弃卡在哪先讲一个我经常遇到的现象有人听说油猴脚本很强大可以清广告、自动签到、优化网页于是装了一个 Tampermonkey 浏览器扩展兴冲冲打开某个资讯站结果页面该有的广告还在悬浮窗还飘着点哪里都没反应。两小时后对方跟我说“这东西没用我卸载了”。问题出在一个很容易被忽略的认知错位油猴管理器本身不是一个“功能插件”它更像一个运行引擎。引擎装好了但里面没有加载任何“程序”自然什么都干不了。所谓油猴脚本本质上就是一段 JavaScript 代码只不过在开头声明了自己的名称、版本、匹配哪些网址、需要哪些权限剩下的逻辑和你在网页控制台里写的代码没什么区别。所以这篇文章帮大家理清一条完整的使用链路油猴管理器安装好了之后下一步去哪里找脚本、怎么装、怎么判断脚本是否生效、匹配规则怎么理解以及遇到脚本失效时怎么排查。我还会分享一些从零手写脚本的模板和调试技巧新手可以直接照着做。适合看这篇文章的人有两类一类是只想“用脚本”希望把某些网页操作自动化另一类是想“写脚本”借油猴练手做点提升效率的小工具。这两条路我都走过下面按实际使用顺序来讲。2. 从零到第一次看到脚本生效总共四步2.1 先把管理器安装到浏览器里这一步本身不复杂但有个细节需要提醒安装完 Tampermonkey 之后工具栏上会出现一个黑色或灰色的图标很多浏览器会默认把扩展图标收到“扩展菜单”里记得手动把它固定出来否则后面完全找不到入口。安装完成后先打开油猴的管理界面也就是“管理面板”。不同浏览器上的面板布局略有差异但核心功能都差不多已安装脚本列表、脚本编辑器、日志面板、设置选项。如果你用的是基于 Chromium 内核的浏览器插件兼容性通常是最稳定的。旧版本的某个浏览器如果一直安装失败检查一下浏览器本身是否需要更新或者换个浏览器再试。2.2 从脚本仓库找现成脚本油猴脚本的获取方式和普通软件不太一样它不在浏览器的扩展商店里搜索而是需要在用户脚本分享社区里找到。在这些平台上一个脚本通常就是一个详情页页面上有明显的“安装此脚本”按钮。点击后浏览器会弹出确认窗口里面能看到脚本的名称、匹配当前网址范围、以及要申请的权限。确认没问题点“安装”脚本就算进了你的油猴里。这里我强调一句安装完先别急着去各种网站乱逛。我们要看脚本是否生效得先看它的“匹配规则”。如果脚本规则里写明只在某个路径下生效那它就不会去管其他网站的事。这是油猴脚本的基本工作方式不是我安装的某个功能没装上。2.3 新建一个最小脚本验证整条链路如果你一时找不到合适的现成脚本也可以先新建一个脚本确认自己的油猴运行环境是通的。操作路径是管理面板 - 新建脚本编辑器会自动生成一个模板。把模板替换成下面的内容// UserScript // name 页面标题提醒助手 // namespace local.demo // version 0.1.0 // description 打开指定页面后弹出当前页面标题用来验证油猴运行链路 // match https://*.example.com/* // run-at document-idle // grant none // /UserScript (function () { use strict; const title document.title; alert(当前页面标题是 title); })();保存后找一个匹配该规则的页面访问。如果你真的访问了一个符合“https://.example.com/”规则的页面就会看到弹窗。看到弹窗说明油猴的链路是通的问题只出在脚本选择或匹配规则上。这一步很重要它能帮你把“油猴坏了”和“脚本不会用”区分开。2.4 管理面板里几个常用按钮别乱点管理面板里最容易引起误解的是这几个地方启用/禁用开关控制脚本是否运行不删除代码。编辑按钮可以直接修改脚本保存后刷新页面才会按新版本执行。删除按钮会真的把脚本移除操作前想清楚。自动更新很多老手开着但我个人建议新装的脚本先观察一两天再决定要不要自动更新原因后面避坑部分会说。这里再补充一个经验在脚本详情里看到的“上次更新日期”和“安装量”是你选择脚本时的重要参考。如果某个脚本已经几年没更新大概率已经不适配现在的网站结构了装上也只会让控制台报错不解决问题。3. 匹配规则和运行时机决定脚本在哪儿“醒过来”3.1 match 与 include 的取舍很多新手写脚本时最常犯的错误就是元数据里的匹配规则写不对。你写的脚本代码再好match配错了脚本就根本不会执行。先看匹配规则的主干字段字段作用典型写法match声明脚本在哪些网址生效语法遵循浏览器扩展匹配模式https://*.example.com/*include更灵活的包含匹配支持正则表达式/^https?:\/\/(www\.)?example\.com\//exclude排除某些网址优先级高于匹配https://example.com/admin/*noframes是否在 iframe 子页面中运行noframesmatch是大多数场景下最合适的字段它要求你写清楚协议、域名、路径。比如// match https://*.example.com/*这里的*.example.com能匹配example.com本身也能匹配a.example.com但不能匹配example.org。路径末尾的/*表示该域名下所有路径。include更灵活但我不建议新手一上来就用正则因为一旦写错排查成本远高于收益。普通网站直接写match就够了。exclude用来排除管理后台、接口路径等不需要脚本执行的地址它优先级更高能帮你避免很多误触发。3.2 运行时机document-start 和 document-end 有多重要匹配规则解决“在哪运行”run-at则解决“什么时候运行”。油猴支持四种比较常用的运行时机run-at 取值执行时机典型用途document-start文档加载的最早期DOM 还没完整解析注入全局样式、提前拦截请求、修改页面基础结构document-bodybody 元素出现之后早期插入组件但此时大量子节点还没生成document-endDOM 全部解析完成但可能还没触发 DOMContentLoaded读取节点、绑定事件document-idleDOMContentLoaded 之后页面空闲阶段大多数脚本的默认选择新手最容易忽视这个字段。你在document-start就去查一个#content节点大概率拿到 null因为内容还没渲染出来。反过来你想在页面出现前就隐藏某个广告位结果等到document-idle才执行广告已经闪了一下体验照样不好。我一般的原则是改样式用document-start绑定普通事件用document-end需要等异步渲染完成且不追求极快响应的用document-idle。3.3 grant 权限和沙箱机制grant决定你的脚本能使用哪些“油猴特权 API”。最基础的两个值是none和具体 API 名。当你在脚本里写GM_setValue、GM_getValue、GM_xmlhttpRequest这类函数时必须在元数据里声明对应的权限否则脚本会直接在控制台报错比如“GM_setValue is not defined”。如果脚本只是普通 DOM 操作不需要油猴的特殊能力就明确写// grant none此时脚本与页面共享同一个上下文调试起来最方便也能直接访问页面里的全局变量。但要注意一旦声明了某个 GM 权限脚本就运行在油猴的沙箱里它的全局环境会和页面隔离开。这时候如果脚本想读取页面里某个全局变量反而读不到。所以我看到网上很多教程让新手无脑加grant none但实际情况要看你要做什么。如果你要用跨域请求能力常见的字段组合是// grant GM_xmlhttpRequest // connect api.example.comconnect声明允许该脚本向哪些域名发出请求。这个列表别乱写只写真正需要的域名能减少很多安全风险。4. 可以立刻投入使用的几类实用脚本场景4.1 页面净化与固定元素清理这类脚本的使用场景最常见。新闻资讯页面往往有大量干扰元素右侧悬浮窗、底部弹窗、文章中间插入的组件。油猴脚本可以直接把这些元素隐藏而且比手动刷新页面清理广告的效率高得多。如果不想下载现成大脚本自己用几行 CSS 就能实现一个轻量净化脚本// UserScript // name 页面元素净化小工具 // namespace local.css.demo // version 0.1.0 // description 隐藏指定干扰元素 // match https://news.example.com/* // run-at document-start // grant none // /UserScript (function () { use strict; const style document.createElement(style); style.textContent .right-side-float, #bottom-popup, .middle-banner { display: none !important; } ; document.head.appendChild(style); })();这类脚本的思路很简单难点在于你怎么确认元素选择器。F12 打开开发者工具选中干扰节点看它的 class 或 id再替换进去就行。用document-start的原因是避免页面元素先渲染出来再被隐藏减少视觉闪烁。4.2 自动填表与重复性表单操作第二个高频场景是自动填充。比如某些系统的每日填报一个月有二十天填的是同样内容手动操作其实非常浪费时间。现成脚本里有一部分就是这类表单辅助脚本但站点结构一变就容易失效。想稳定可以自己写短逻辑把上次填写的内容存到GM_setValue里下次打开页面自动带出。我在第五节会给出整套代码。这里想先说明一个安全习惯自动填表脚本要做到“填充但不盲目提交”。哪怕内容一样表单也可能有隐藏字段随时间变化直接自动点击提交容易出错。我自己的做法是只自动填充、留一个人工确认提交的步骤这样数据出问题还能及时发现。4.3 表格导出和数据整理网页里有一类非常常见却又很头疼的操作——明明页面展示了一个完整的数据表格却没有导出按钮。用手动复制粘贴整理成 Excel遇到几百行数据会让人崩溃。油猴脚本可以遍历页面表格把数据拼成 CSV 内容再触发浏览器下载。这里有个技术细节保存 CSV 时要在内容最前面加上\ufeff字符也就是 UTF-8 BOM否则 Excel 打开可能乱码。很多人现在告诉你“用 Blob 下载 CSV”但不说这个细节实际跑起来才发现巨坑。第五节的第二个脚本会给出完整实现。4.4 文本提取与跨页信息汇总这类脚本平时看起来不起眼但一旦需要临时处理页面数据非常有用。比如某次你需要把所有文章标题和链接整理成一份清单一条条复制粘贴确实累写一个几行的小脚本遍历页面里的h2和链接节点把结果打到控制台再一次性复制出来效率高很多。这种临时脚本不需要考虑通用性反正它只在匹配的页面里运行用完删除也不心疼。4.5 挑选现成脚本时的“四看”原则从脚本仓库选脚本时我这里有一套看得比较仔细的标准看最后更新时间。超过一年的脚本很可能已经不适配当前页面结构。看安装量和评论。安装量高、有近期评论的通常维护状态还行。看代码量。几十行的小脚本容易审查几百上千行的脚本要格外谨慎。看请求去向。装好后打开开发者工具的网络面板观察脚本是否有向无关域名发送数据的行为。第 4 点尤其重要。部分恶意脚本会利用 GM_xmlhttpRequest 把你在目标站点上的数据回传到第三方服务器。脚本源码里如果出现了奇怪的请求地址就别用了。5. 自己动手写第一个带状态保存的脚本5.1 脚本骨架是怎么组成的一个标准的用户脚本可以拆成两部分元数据块和业务逻辑。元数据块是脚本开头的注释段虽然注释形式但油猴会读取它。元数据里写名称、版本、描述、匹配规则、运行时机、权限声明。业务逻辑就是一个 IIFE 包裹的 JavaScript 函数避免污染全局变量。以“自动填充每日表单”为例需求是打开表单页把上次填过的内容带入输入框并且在下一次提交前清空记录避免下次误用旧数据。// UserScript // name 自动填充每日表单 // namespace local.form.demo // version 0.2.0 // description 记录上一次填写内容在指定页面加载时自动带出 // match https://portal.example.com/* // run-at document-idle // grant GM_setValue // grant GM_getValue // /UserScript (function () { use strict; function whenReady(fn) { if (document.readyState ! loading) { fn(); } else { document.addEventListener(DOMContentLoaded, fn); } } whenReady(() { const textarea document.querySelector(#daily-text); const submitBtn document.querySelector(#submit-btn); if (!textarea) { return; } const saved GM_getValue(lastContent, ); if (saved) { textarea.value saved; } textarea.addEventListener(input, () { GM_setValue(lastContent, textarea.value); }); if (submitBtn) { submitBtn.addEventListener(click, () { GM_setValue(lastContent, ); }); } }); })();几个值得解释的细节whenReady函数是为了兼容多种运行状态。光靠run-at document-idle有时还不够尤其是某些页面脚本会提前读完 DOM这时候需要再判断一次document.readyState。GM_getValue读取上次保存的内容第二个参数是默认值空字符串就代表没有历史记录。输入框每次触发input事件就把最新文本存进去。所以即使用户中途改了内容下次打开也能还原。点击提交按钮后立刻清空记录。这样不会出现“上次提交的内容又在下一次打开时被旧值填进去”的尴尬情况。5.2 另一个可复用的脚本表格导出 CSV再看一个相对完整的业务脚本。目标为指定页面右下角添加一个“导出当前表格”按钮点击把页面里某个表格转换成 CSV 文件下载。// UserScript // name 表格导出CSV小助手 // namespace local.csv.demo // version 0.1.0 // description 在页面右下角添加“导出当前表格”按钮输出为CSV文件 // match https://data.example.com/* // run-at document-idle // grant none // /UserScript (function () { use strict; function exportTable(table) { const rows Array.from(table.rows).map((row) Array.from(row.cells).map((cell) cell.innerText.trim()) ); const csv rows .map((row) row.map((cell) ${cell.replace(//g, )}).join(,)) .join(\n); // 加 BOM防止 Excel 打开中文乱码 const blob new Blob([\ufeff csv], { type: text/csv;charsetutf-8;, }); const url URL.createObjectURL(blob); const link document.createElement(a); link.href url; link.download export.csv; link.click(); URL.revokeObjectURL(url); } setTimeout(() { const table document.querySelector(table.data-list); if (!table) { return; } const btn document.createElement(button); btn.textContent 导出当前表格; btn.style.cssText position:fixed;right:20px;bottom:20px;z-index:99999;padding:8px 16px;cursor:pointer;; btn.addEventListener(click, () exportTable(table)); document.body.appendChild(btn); }, 1000); })();这里有一个实际技巧CSV 内容里的字段值如果包含逗号、引号、换行直接拼接会把表格结构搞乱。所以我把每个单元格值都用双引号包起来内部原有的双引号再替换成两个双引号这是 CSV 格式的标准转义规则。按钮用position: fixed固定在右下角避免破坏原页面布局。如果你用的站点也有表格但选择器不同把table.data-list改成你页面上实际的选择器即可。5.3 从“能用”到“好用”的改造思路很多人学到这一步第一版脚本往往只是把需求写死在自己的站点上这没什么问题。但如果你想让它更持久有两个常见改进方向把选择器和配置项抽到脚本顶部一个常量对象里以后改起来方便。增加一个“排除条件”比如某些页面不需要展示按钮但你不想改匹配规则可以在逻辑里加if (location.pathname.includes(/detail/)) return;。6. 动态页面和重复挂载脚本失效的最常见元凶6.1 问题现象为什么一个脚本在普通页面能用在后台管理页里毫无反应不少脚本新手会遇到这种情况在自己写的示例页面里一切正常放到实际目标站点后等了半天按钮都没出现控制台也不报错。原因通常是目标页面是动态加载的脚本执行的时候目标 DOM 节点根本还没生成。单页应用尤其明显。很多前端框架用路由切换页面整个文档只加载一次后续靠 JS 更新内容。你的脚本只在document-idle执行一次那自然是装完就没下文了。6.2 MutationObserver 的最小可用样板这个问题的通用解法是使用MutationObserver监听 DOM 变化等目标节点出现后再初始化脚本逻辑。一个比较常见的最小样板如下const observer new MutationObserver(() { const target document.querySelector(#dynamic-container); if (target !target.dataset.inited) { target.dataset.inited 1; initScript(target); } }); observer.observe(document.body, { childList: true, subtree: true, });这里有两个细节值得说明childList: true表示监听子节点增加或移除subtree: true表示监听范围覆盖所有后代节点。这样页面任何地方新插入节点都能触发回调。我特意给目标节点加了一个dataset.inited标记避免初始化逻辑被反复执行。6.3 事件监听器叠加的几个后果还有一个很隐蔽的问题如果每次 DOM 变化都重新绑定事件事件监听器会越积越多。你第一次点击按钮执行一次回调第二次点击可能执行两次多次刷新或多次触发容器变化后行为会变得非常混乱。我的习惯是优先采用事件委托也就是把监听器挂到稳定的父节点或document上而不是直接挂在动态生成的子节点上document.addEventListener(click, (event) { const btn event.target.closest(.export-btn); if (btn) { exportTable(btn.dataset.tableId); } });这样即使按钮是动态生成的也不用反复绑定正好和MutationObserver搭配使用。6.4 观察器的资源开销别忽视监听整个document.body的子树变化在复杂页面上会产生大量回调尤其是页面里有高频渲染的图表或滚动表格时容易有性能影响。如果你是为了一个固定区域尽量缩小观察范围而不是监听整个 body。另外观察器要在适当时机断开比如找到目标节点并完成初始化后在代码里调用observer.disconnect()。否则它会一直盯着页面变化白白消耗资源。7. 脚本突然失效时按这条链路排查7.1 先确认脚本还是亮的再确认页面确实匹配脚本失效很多人的第一反应是重装或删了重写。我的建议是先做三层确认第一层去管理面板看脚本是否处于启用状态排除不小心点击了“停用”的情况。第二层看当前网址和脚本元数据里的匹配规则是否一致。油猴界面上有时会显示“已在此页面运行”这句话很能说明问题。如果没有显示说明匹配规则对不上脚本当然不执行。第三层打开浏览器开发者工具的控制台看有没有脚本报错。报错通常分两类一类是页面元素选择器找不到说明页面结构变了另一类是变量冲突说明脚本和页面自身的 JS 发生了环境混杂。7.2 用控制台日志标记脚本执行阶段有些脚本在中间某一行出错导致后半段逻辑全没执行。这时可以在关键位置插入console.log标记逐段定位console.log([userscript] stage 1: started); // 某段逻辑 console.log([userscript] stage 2: after init);刷新页面后看控制台能走到哪一步就知道是哪段出了问题。用完后记得删掉这些日志。7.3 网站改版和脚本更新的适配问题页面结构一旦改版老脚本几乎必然失效。这个失效规律没办法避免因为脚本依赖的是当前 DOM 结构。此时先看脚本是否在仓库里有新版本有就更新。没有的话只能手动调整选择器把脚本里失效的 class 名换成新页面上的。改完以后建议在版本号后面加个小版本递增比如 0.1.0 改成 0.1.1方便自己日后回忆这次改动。7.4 自动更新也不是越频繁越好油猴脚本支持自动从仓库拉取新版本但我不建议所有脚本都开。个别作者会频繁改需求某个改动刚好和你自己的使用习惯冲突新版本反而不好用。如果你希望脚本稳定就把自动更新关闭等自己有空时手动更新更新前先看看版本变化和评论。脚本编辑界面里通常有更新日志或 GitHub 链接先读再更新比盲更新省心。8. 我长期使用中坚持的几个脚本管理习惯先说说最基础的一条尽量装那些能在源码层面看懂在做什么的脚本。不是劝每个人都成为开发者而是说至少在安装前花几分钟看下脚本头部元数据和请求域名。如果哪里都读不懂或者里面藏着一个你不认识的网址就别用了。脚本运行在你登录态的页面里风险边界一定要自己把关。另一个习惯是按需启用。油猴支持为每个脚本设置“包括/排除”规则我为常用脚本设置只对必要域名生效浏览器里其他页面的性能就不会受到影响。曾经遇到过有人图方便给脚本写了一条特别宽的匹配规则导致每次打开任意站点都要执行一遍脚本里的页面净化逻辑累人又没有意义。还有备份。油猴脚本本质是纯文本我自己在本机建了一个“userscripts”文件夹把改过的脚本都存一份。每次改动都递增版本号并把关键改动说明写在描述字段里。这比依赖某个平台云端保存可靠得多。最后一条是脚本写完以后要删掉临时测试代码。比如我在验证阶段喜欢用alert弹窗但如果一个自动化填表脚本还弹窗就失去自动化意义了。上线前把alert、多余的console.log清理干净脚本整洁很多以后回看也不容易迷路。
阅读完成 · 觉得有帮助?