
1. 项目概述当网页视频播放遇上自动化脚本如果你经常在网页上看视频尤其是那些需要手动点击“下一集”或者在不同视频源之间切换的场景那么“JavaScript自动切换并播放视频”这个需求你一定不陌生。这听起来像是一个懒人工具但背后其实是一个典型的浏览器自动化场景而Tampermonkey油猴脚本管理器正是实现这个想法的绝佳平台。简单来说这个项目就是利用Tampermonkey编写一段JavaScript脚本让它像一只无形的手自动帮你完成视频播放页面的点击、切换、播放等操作从而解放你的双手。这个想法的应用场景远比想象中广泛。比如你在一个在线学习平台观看系列课程每节课结束后都需要手动点击“下一节”几十节课下来这个重复动作就变得非常烦人。又或者你在一个聚合了多个视频源的网站上追剧每次播放完一集都需要从一堆“播放源”按钮中重新选择并点击流程繁琐。更进阶一点有些网站的视频播放列表是动态加载的或者有复杂的鉴权逻辑手动操作不仅低效还可能因为网络波动导致中断。通过编写一个Tampermonkey脚本我们可以将这些重复、规律性的操作自动化实现“打开页面泡杯咖啡回来时视频已经自动播到最新一集”的惬意体验。这个项目的核心价值在于“个性化”和“自动化”。它不依赖于网站官方提供的功能很多网站确实没有完善的自动连播而是由用户自己定义规则去适配千变万化的网页结构。这要求我们不仅要懂JavaScript还要懂如何与网页文档对象模型DOM交互如何处理异步加载如何应对网站反爬或结构变动。接下来我将以一个模拟的“在线视频学习平台”为例拆解如何从零开始构建这样一个自动化脚本分享其中的核心思路、技术细节以及我踩过的那些坑。2. 核心思路与方案设计2.1 需求分析与技术选型要实现“自动切换并播放”我们需要将这个大目标拆解成几个可执行的子任务监控播放状态脚本需要知道当前视频何时播放完毕。定位目标元素播放完毕后需要找到“下一集”按钮或下一个视频源的链接。执行切换操作模拟用户点击触发页面跳转或视频源切换。处理新页面/新播放器页面切换或播放器更新后需要重新绑定监控事件形成循环。增加容错与控制处理网络延迟、元素加载慢、页面结构变化等异常情况并提供手动开关。基于这些任务技术选型就很明确了平台Tampermonkey。它是浏览器扩展提供了一个安全、隔离的环境来运行用户脚本UserScript可以方便地管理脚本、设置匹配的网址规则match并拥有GM_*系列API来增强功能。核心语言JavaScript。这是与浏览器和网页交互的唯一语言。关键APIMutationObserver用于监听DOM变化应对动态加载内容的网站。addEventListener监听视频元素的ended、timeupdate等事件。setTimeout/setInterval用于轮询检查元素是否存在作为MutationObserver的备选或补充。click()方法模拟用户点击。GM_notification(可选)用于在操作成功或失败时发送桌面通知。为什么不选用其他自动化工具如Selenium或Puppeteer因为它们更适用于完整的浏览器自动化测试或爬虫需要独立运行环境无法像Tampermonkey脚本这样轻量、持久地附着在特定网页上随开随用。Tampermonkey脚本的优势在于“用户视角”的自动化无缝集成到日常浏览中。2.2 脚本架构设计一个健壮的自动播放脚本不应该是一堆线性执行的代码。我倾向于采用一种“状态机”或“事件驱动”的架构让逻辑更清晰。基本工作流如下初始化脚本在匹配的页面加载后运行。首先检查页面上是否存在视频播放器元素如video或某些特定播放器的容器div。绑定监听器找到视频元素后为其绑定ended事件监听器。事件触发当视频播放结束时ended事件被触发。寻找下一集在事件处理函数中使用document.querySelector或document.querySelectorAll配合特定的CSS选择器寻找“下一集”按钮。这个选择器需要你事先分析目标网页的HTML结构来确定。执行切换如果找到按钮调用其click()方法。如果没有找到则可能通过MutationObserver等待按钮出现或者尝试寻找下一个视频的页面链接a href...并进行跳转window.location.href newUrl。循环与重置如果是页面跳转新页面会再次触发脚本初始化。如果是单页应用SPA仅更新了播放器则需要在新的播放器元素上重新执行步骤1和2绑定新的监听器。异常处理在整个过程中任何一个环节失败如元素找不到、点击无效都应有相应的错误处理比如重试几次、发出通知或直接停止脚本避免陷入无限错误循环。这个架构的核心是“监听 - 响应 - 重置监听”的循环。难点往往在于如何精准地定位元素以及如何处理网站复杂的动态加载逻辑。3. 核心代码实现与详解下面我将分模块展示一个基础但功能完整的Tampermonkey脚本代码并逐段解释。我们假设目标网站是一个简单的学习平台视频播放完毕后右侧有一个课程列表当前播放的课程项会有一个.active类下一节课的链接就在下一个li元素中。// UserScript // name 视频自动连播助手 // namespace http://tampermonkey.net/ // version 1.0 // description 在指定学习平台自动播放下一节视频 // author You // match https://example-learning-site.com/course/* // grant GM_notification // /UserScript (function() { use strict; // 配置项可灵活修改 const CONFIG { nextButtonSelector: .course-sidebar .chapter-list li.active li a, // 下一节课的CSS选择器 videoSelector: video, // 视频元素选择器 retryTimes: 3, // 元素查找重试次数 retryDelay: 1000, // 重试间隔(毫秒) enableNotification: true, // 是否启用通知 }; let currentRetryCount 0; let observer null; /** * 主初始化函数 */ function initAutoPlay() { console.log([自动连播] 脚本启动正在寻找视频元素...); const video findVideoElement(); if (video) { setupVideoListener(video); setupMutationObserver(); // 为动态加载的页面结构增加监听 } else { // 如果没立即找到视频可能是页面加载慢尝试轮询 retryFindVideo(); } } /** * 查找视频元素 */ function findVideoElement() { return document.querySelector(CONFIG.videoSelector); } /** * 轮询查找视频元素备选方案 */ function retryFindVideo() { if (currentRetryCount CONFIG.retryTimes) { console.warn([自动连播] 重试 ${CONFIG.retryTimes} 次后仍未找到视频元素脚本停止。); return; } currentRetryCount; console.log([自动连播] 第 ${currentRetryCount} 次尝试查找视频...); setTimeout(() { const video findVideoElement(); if (video) { setupVideoListener(video); } else { retryFindVideo(); } }, CONFIG.retryDelay); } /** * 为视频元素设置播放结束监听 * param {HTMLVideoElement} video */ function setupVideoListener(video) { // 先移除可能存在的旧监听器避免重复绑定 video.removeEventListener(ended, handleVideoEnded); video.addEventListener(ended, handleVideoEnded); console.log([自动连播] 视频结束监听器已绑定。); if (CONFIG.enableNotification) { GM_notification({ text: 自动连播已就绪, title: 脚本提示, timeout: 2000 }); } } /** * 视频播放结束的处理函数 */ function handleVideoEnded() { console.log([自动连播] 当前视频播放完毕寻找下一节...); const nextLink document.querySelector(CONFIG.nextButtonSelector); if (nextLink) { console.log([自动连播] 找到下一节链接: ${nextLink.href}); nextLink.click(); // 模拟点击 // 如果是SPA点击后页面可能不刷新需要在这里重新初始化监听 // 这里假设点击后页面会跳转由新页面重新运行脚本 } else { console.log([自动连播] 未找到下一节链接可能已是最后一节。); if (CONFIG.enableNotification) { GM_notification({ text: 已是最后一节课程自动连播停止。, title: 脚本提示, timeout: 3000 }); } } } /** * 设置MutationObserver监听DOM变化 * 用于应对视频播放器或课程列表是JS动态生成的情况 */ function setupMutationObserver() { // 如果已经存在观察者先断开之前的连接 if (observer) { observer.disconnect(); } // 创建一个观察者实例监听整个body子树的变化 observer new MutationObserver((mutationsList) { for (let mutation of mutationsList) { // 检查是否有新增节点并且这些节点中包含视频元素 if (mutation.type childList mutation.addedNodes.length 0) { // 简单检查如果新增节点中有video标签或者整个播放器容器被替换了就重新初始化 // 这里可以根据实际网站结构优化选择器 const video findVideoElement(); if (video !video.hasAttribute(data-autoplay-bound)) { // 给视频元素打上标记避免重复绑定 video.setAttribute(data-autoplay-bound, true); setupVideoListener(video); console.log([自动连播] 检测到动态加载的视频已重新绑定监听。); } } } }); // 开始观察body元素配置观察子节点的增减 observer.observe(document.body, { childList: true, subtree: true }); console.log([自动连播] DOM变化监听器已启动。); } // 页面加载完成后延迟一小段时间再初始化确保DOM基本就绪 window.addEventListener(load, () { setTimeout(initAutoPlay, 500); }); // 同时如果页面是SPA通过history API监听路由变化高级用法 // 这里提供一个基础示例部分网站可能需要 const originalPushState history.pushState; const originalReplaceState history.replaceState; history.pushState function (...args) { originalPushState.apply(this, args); window.dispatchEvent(new Event(locationchange)); }; history.replaceState function (...args) { originalReplaceState.apply(this, args); window.dispatchEvent(new Event(locationchange)); }; window.addEventListener(popstate, () { window.dispatchEvent(new Event(locationchange)); }); window.addEventListener(locationchange, () { // 路由变化后稍等片刻让新内容加载 setTimeout(initAutoPlay, 1000); }); })();代码关键点解析元信息头 (UserScript): 这是Tampermonkey脚本的配置区。match指定了脚本生效的网址模式必须精确。grant声明了需要使用的特殊API比如GM_notification。配置对象 (CONFIG): 将所有可配置项集中管理如选择器、重试次数。这样当网站改版时你只需要修改这里的选择器字符串而不用深入代码逻辑。findVideoElement与retryFindVideo: 直接查询和轮询查询相结合。不是所有页面一打开视频元素就存在的轮询是一个简单有效的容错机制。setupVideoListener: 绑定ended事件前先移除旧监听器这是一个好习惯防止内存泄漏或重复绑定导致函数被执行多次。handleVideoEnded: 核心响应函数。这里使用了CONFIG.nextButtonSelector来查找元素。nextLink.click()是模拟点击的最直接方式。MutationObserver: 这是处理现代网页单页应用、动态加载的利器。它监听指定DOM节点内的变化一旦发现新增节点中包含视频元素就重新执行绑定。我们给绑定过的视频加了一个自定义属性>function waitForElement(selector, callback, maxAttempts 10) { let attempts 0; const interval setInterval(() { const element document.querySelector(selector); if (element) { clearInterval(interval); callback(element); } else if (attempts maxAttempts) { clearInterval(interval); console.warn(等待元素 ${selector} 超时。); } attempts; }, 500); } // 使用方式 waitForElement(CONFIG.videoSelector, (video) { setupVideoListener(video); });监听特定事件: 有些网站会在内容加载完成后触发自定义事件或者使用如window.appReady这样的全局变量。这需要你观察网站的JavaScript执行逻辑。4.3 应对网站反自动化策略一些网站会检测异常的自动化操作比如极快频率的点击、没有伴随鼠标移动事件的点击等。添加随机延迟: 在关键操作如点击下一集前用setTimeout添加一个随机的、人性化的延迟例如 1~3 秒。function simulateHumanClick(element) { const delay 1000 Math.random() * 2000; // 1-3秒随机延迟 setTimeout(() { element.click(); }, delay); }触发更多事件: 除了click()可以尝试模拟更完整的事件流。function advancedClick(element) { element.dispatchEvent(new MouseEvent(mousedown, { bubbles: true })); element.dispatchEvent(new MouseEvent(mouseup, { bubbles: true })); element.dispatchEvent(new MouseEvent(click, { bubbles: true })); // 有时还需要focus事件 element.focus(); }降低检测频率: 避免使用setInterval进行毫秒级的高频DOM查询这很容易被识别为脚本行为。MutationObserver本身是异步的频率较低更隐蔽。4.4 增加用户控制界面一个成熟的脚本应该允许用户临时关闭或配置。Tampermonkey提供了GM_getValue和GM_setValueAPI来存储配置。你还可以在页面上注入一个小的控制面板。// grant 需添加 GM_getValue, GM_setValue // grant GM_getValue // grant GM_setValue function createControlPanel() { const panel document.createElement(div); panel.id auto-play-control; panel.style.cssText position: fixed; bottom: 20px; right: 20px; background: #fff; border: 1px solid #ccc; padding: 10px; z-index: 9999;; panel.innerHTML strong自动连播控制/strongbr labelinput typecheckbox idtoggleAutoPlay 启用自动连播/labelbr button idnextNow立即下一集/button ; document.body.appendChild(panel); const toggle panel.querySelector(#toggleAutoPlay); toggle.checked GM_getValue(enabled, true); // 读取存储的状态 toggle.addEventListener(change, (e) { GM_setValue(enabled, e.target.checked); if (e.target.checked) { initAutoPlay(); } else { // 停止所有监听和轮询 if (observer) observer.disconnect(); console.log([自动连播] 已手动关闭。); } }); panel.querySelector(#nextNow).addEventListener(click, () { // 手动触发下一集逻辑 const nextLink document.querySelector(CONFIG.nextButtonSelector); if (nextLink) nextLink.click(); }); } // 在initAutoPlay开始时调用 createControlPanel(); if (GM_getValue(enabled, true)) { initAutoPlay(); }5. 常见问题排查与优化实录在实际使用中你肯定会遇到各种问题。下面是我总结的一些常见情况及其解决方案。问题现象可能原因排查步骤与解决方案脚本完全不运行1.match网址模式错误。2. Tampermonkey未启用或脚本未启用。3. 脚本语法错误。1. 检查浏览器地址栏URL是否匹配match规则支持通配符*。2. 点击Tampermonkey图标确保脚本是启用状态。3. 打开浏览器控制台F12 - Console查看是否有红色报错信息。找到了视频但结束后不切换1.ended事件未触发可能是流媒体或特殊播放器。2. “下一集”按钮的选择器错误或失效。3.click()模拟点击无效。1. 尝试监听timeupdate事件判断当前时间是否接近视频总时长。2. 在控制台手动执行document.querySelector(CONFIG.nextButtonSelector)验证是否能找到元素。3. 尝试使用advancedClick函数模拟更完整的事件或直接使用window.location.href跳转。切换到新页面后脚本在新页面不生效1. 新页面URL不在match规则内。2. 新页面是SPA未触发脚本重新初始化。1. 调整match使其覆盖所有课程页面如https://example.com/course/*。2. 实现并启用上文提到的SPA路由监听逻辑。脚本运行一段时间后失效1. 网站DOM结构更新旧的选择器失效。2.MutationObserver配置过于宽泛或狭窄漏掉了变化。1. 重新分析页面更新CONFIG中的选择器。2. 调整MutationObserver的observe配置例如将subtree: true改为只观察特定容器。在回调函数中打印mutation观察实际变化的是什么。控制台出现大量错误或脚本导致页面卡顿1. 轮询 (setInterval) 频率过高且未清除。2.MutationObserver回调函数执行了耗时操作。3. 内存泄漏旧监听器未移除。1. 用setTimeout替代不间断的setInterval或在找到元素后及时clearInterval。2. 在MutationObserver回调中使用debounce防抖技术避免短时间内的多次变化触发多次处理。3. 确保在绑定新事件监听器前移除旧监听器在脚本停止或页面卸载时断开observer。独家避坑技巧“选择器沙盒”在脚本开头定义一个测试模式。例如设置CONFIG.debug true当开启时脚本不执行实际点击只是在控制台打印它找到的元素和准备执行的操作。这能安全地验证你的逻辑是否正确。日志分级使用console.log,console.warn,console.error区分不同重要性的信息。在脚本关键节点如初始化、找到元素、触发点击输出日志方便追踪执行流。版本管理与备份网站经常改版。在Tampermonkey中编辑脚本时先复制一份旧版本代码作为备份。修改选择器后更新脚本的version号这样你就能清楚知道哪个版本对应哪个网站结构。社区力量很多流行的网站已经有用户分享了现成的Tampermonkey脚本。在GreasyFork等脚本平台搜索一下也许能找到灵感或直接可用的解决方案你可以学习别人的代码来完善自己的脚本。编写Tampermonkey脚本是一个与网站“斗智斗勇”的过程充满了乐趣和挑战。它要求你细心观察、耐心调试并不断适应变化。当你成功运行起一个自己编写的自动播放脚本看着视频一集接一集无缝播放时那种成就感是实实在在的。希望这份详细的指南能帮你少走弯路顺利打造出属于你自己的浏览自动化工具。