ARTICLE DETAIL

资讯详情

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

JS网页自动刷新脚本实战:从控制台到Puppeteer的完整指南

JS网页自动刷新脚本实战:从控制台到Puppeteer的完整指南 1. 为什么你需要一个网页自动刷新脚本先聊点实际的。做前端开发、数据采集、自动化测试的人大概率都经历过这种场景改完代码按F5页面没变清缓存再刷还是旧内容盯着监控大屏数据十分钟不更新非得手动点一下刷新按钮。尤其是调试服务端接口、观察动态渲染结果、或者抓取某个实时变动的网页数据时没有自动刷新整个人的工作效率至少砍掉一半。所谓“JS网页自动刷新脚本”本质上就是用JavaScript控制浏览器定时重新加载当前页面或者刷新页面中的某个局部区域。它不需要安装重量级软件不需要买付费工具甚至不需要懂很深的编程知识——只要你会打开浏览器控制台或者会写几行简单的脚本就能实现。这篇文章我会从三个层次来讲第一层手动在控制台执行适合临时应急不用装任何东西第二层写一个可复用的脚本配合浏览器扩展或本地文件做到“打开即用”第三层用Node.js写后台刷页逻辑适合需要批量采集、监控页面变化的场景。以及会穿插大量我在实际使用中踩过的坑和总结出来的经验比如刷新后如何保留滚动位置、如何判断页面加载完成、如何减轻服务器压力等等。适合谁看想解放双手的普通用户、刚入门的前端新手、经常需要和网页数据打交道的运营和测试人员。全文以原生JavaScript为主不依赖特定框架也不需要你引入一堆乱七八糟的库。2. 最朴素的方案控制台三行代码搞定定时刷新2.1 核心代码与逐行解释压箱底的第一招打开浏览器按F12进入开发者工具切到Console控制台标签页粘贴下面这段代码回车执行setInterval(function() { location.reload(); }, 5000);意思很简单每5000毫秒即5秒执行一次location.reload()把当前页面重新加载一遍。你立刻就能看到页面开始自动刷新。稍微进阶一点如果不想刷新整个页面只想刷新页面中的某个区域——比如一个实时变化的比分、行情、数据面板可以用下面这种局部刷新写法setInterval(function() { var target document.getElementById(live-data); if (target) { fetch(target.dataset.url || location.href) .then(function(res) { return res.text(); }) .then(function(html) { var parser new DOMParser(); var doc parser.parseFromString(html, text/html); var newContent doc.getElementById(live-data); if (newContent) { target.innerHTML newContent.innerHTML; } }).catch(function(err) { console.log(刷新失败, err); }); } }, 10000);这段代码依赖目标区域有一个>// UserScript // name 网页自动刷新器 // namespace http://your-namespace.com/ // version 1.0 // description 定时刷新当前页面保护滚动位置支持间隔自定义 // match *://*/* // grant none // /UserScript (function() { use strict; // 默认刷新间隔毫秒可以改 const DEFAULT_INTERVAL 10000; // 从localStorage读取用户设定的间隔 function getInterval() { const stored localStorage.getItem(autoRefreshInterval); const parsed parseInt(stored, 10); return isNaN(parsed) || parsed 2000 ? DEFAULT_INTERVAL : parsed; } // 保存滚动位置 function saveScrollPosition() { sessionStorage.setItem(scrollY, window.scrollY); } // 恢复滚动位置 function restoreScrollPosition() { const saved sessionStorage.getItem(scrollY); if (saved ! null) { window.scrollTo(0, parseInt(saved, 10)); } } // 监听页面卸载提前存位置 window.addEventListener(beforeunload, saveScrollPosition); // 页面加载完成后恢复位置 window.addEventListener(load, function() { restoreScrollPosition(); }); setInterval(function() { saveScrollPosition(); location.reload(); }, getInterval()); })();这段脚本最明显的改进是刷新之前把滚动位置存到sessionStorage里页面重新加载完成后立即恢复。这样你本来停在页面很下面的位置刷新完还是在那个位置不用重新往下滚。实际用下来感受差距非常大。match *://*/*表示所有站点都生效但我个人建议你改成具体的域名比如match https://example.com/*不然你逛淘宝逛到一半开始自动刷新那体验绝对酸爽。3.2 配合localStorage做动态控制上面脚本里我用localStorage.setItem(autoRefreshInterval, 5000)来控制刷新间隔这意味着你可以在控制台里随时修改间隔而不用重装脚本localStorage.setItem(autoRefreshInterval, 30000);改成30秒后脚本会在下一次执行setInterval回调之前读取新值——不对这里有个小坑。setInterval一旦创建回调的执行间隔是固定的不会因为localStorage变了就自动变。如果想做到动态修改需要自己写递归setTimeout来代替function scheduleReload() { setTimeout(function() { saveScrollPosition(); location.reload(); // 页面重载后脚本会重新执行再走一遍 scheduleReload }, getInterval()); } scheduleReload();页面每次加载后都会重新调用scheduleReload()重新读取一遍localStorage里的间隔这样你改设置立刻就能生效。这才是真正实用的写法。3.3 自制极简浏览器扩展如果你不想依赖油猴也可以做一个最简单的Extension。实现方式不复杂manifest.json加一个content script或者用background脚本配合tabs.reload接口。manifest.json关键配置如下{ manifest_version: 3, name: Auto Refresh, version: 1.0, permissions: [scripting, activeTab], host_permissions: [all_urls], background: { service_worker: background.js }, action: { default_title: 设置自动刷新间隔 } }background.js里可以用chrome.tabs.reload方法每过N秒刷新当前标签页let timer null; chrome.action.onClicked.addListener((tab) { if (timer) { clearInterval(timer); timer null; console.log(自动刷新已停止); } else { const interval 5000; timer setInterval(() { chrome.tabs.reload(tab.id); }, interval); console.log(自动刷新已启动间隔, interval, ms); } });自己写扩展的好处是可控性强不需要第三方工具缺点是Chrome对扩展的权限管理严格调试起来比脚本麻烦一些。对于大多数人来说油猴脚本的性价比已经足够高了。4. 从浏览器外面控制Node.js 与无头浏览器方案4.1 为什么需要Node.js方案浏览器内刷新脚本有一个天然限制它只能作用于已经打开的页面。如果你的需求是批量监控多个URL或者页面挂了自动重开那就需要把控制权放到浏览器外部。我举个例子你有一个数据采集任务每天凌晨要从某个动态页面上抓取最新列表。页面是JavaScript渲染的直接抓静态HTML根本不靠谱。这时候就需要写一个Node.js脚本定时打开无头浏览器等页面渲染完成提取数据然后关闭再来一轮。4.2 用Puppeteer实现定时刷新与数据提取Puppeteer是目前最常用的Node.js无头浏览器库由Chrome团队维护。安装很简单npm init -y npm install puppeteer注意如果安装过程中报npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称这说明你的Node.js环境变量没配好。解决方法是重新安装Node.js并确保安装时勾选“Add to PATH”或者手动把Node安装目录加入系统环境变量。下面给出一个完整的定时刷新加数据提取的例子const puppeteer require(puppeteer); const URL_TO_MONITOR https://example.com/data-page; async function fetchPageData(browser) { const page await browser.newPage(); try { // 等待页面加载网络空闲后再继续 await page.goto(URL_TO_MONITOR, { waitUntil: networkidle0, timeout: 30000 }); // 等待某个关键元素出现 await page.waitForSelector(.data-list, { timeout: 10000 }); // 提取数据 const result await page.evaluate(() { const items document.querySelectorAll(.data-list .item); return Array.from(items).map(item item.innerText.trim()); }); console.log(抓取时间:, new Date().toLocaleString()); console.log(数据条数:, result.length); // 这里可以写你的存储逻辑比如写入文件或数据库 } catch (err) { console.error(页面加载失败:, err.message); } finally { await page.close(); } } async function main() { const browser await puppeteer.launch({ headless: true, // 如果是调试阶段可以改成 false 看浏览器界面 args: [--no-sandbox, --disable-setuid-sandbox] }); // 每60秒执行一次 setInterval(async () { await fetchPageData(browser); }, 60000); // 立即执行一次不用等第一轮间隔结束 await fetchPageData(browser); } main().catch(err { console.error(脚本崩溃:, err); });有几个关键点值得解释waitUntil: networkidle0表示等待页面500ms内没有网络请求再认为加载完成。对动态渲染页面来说这个设置比直接等待精确得多。waitForSelector用来等待目标数据元素出现避免“元素还没渲染出来就提取”导致拿到的列表是空的。每次循环都新建一个Page对象用完就关防止内存泄漏。如果你跑长任务这个习惯必须养成不然24小时下来浏览器进程的占用能把机器拖垮。4.3 轻量替代方案Playwright 与 SeleniumPuppeteer虽然好用但它对Chrome的依赖比较强。如果你的环境里只有Firefox或者Edge可以考虑用Playwright它支持Chromium、Firefox和WebKit三种内核API设计比Puppeteer更顺手一些。const { chromium } require(playwright); (async () { const browser await chromium.launch(); const page await browser.newPage(); await page.goto(https://example.com); await page.waitForSelector(.content); const html await page.content(); console.log(页面长度:, html.length); await browser.close(); })();Selenium则是老牌的浏览器自动化方案多语言支持齐全但配置麻烦现在写新脚本我基本不推荐它了。5. 刷新过程中的三大坑位置丢失、重复加载、资源泄露5.1 滚动位置丢失这个问题前面提过控制台直接刷新时页面会回到顶部。如果你在看一个长列表第50条数据刷一次就回到第一屏那自动刷新不仅没有解放双手反而添乱。解决方案我已经在上面的油猴脚本里写了用sessionStorage在beforeunload事件里保存window.scrollY加载完成后用window.scrollTo(0, parseInt(saved))恢复。这里有个细节恢复滚动位置的时机要选对。直接写在load事件里可能会因为页面底部图片懒加载导致高度还在变化位置恢复不准。稳妥的做法是配合requestAnimationFrame或者在load事件后延时一小段再恢复window.addEventListener(load, function() { setTimeout(function() { window.scrollTo(0, sessionStorage.getItem(scrollY) || 0); }, 100); });实测下来100ms的延时对大部分页面都够用少数图片特别多的页面可以把时间调到200ms-300ms。这个“延时恢复”的细节普通教程里基本不会提。5.2 重复加载与服务器压力每个页面刷新都会产生一次完整的HTTP请求如果页面本身包含大量图片、视频或者复杂接口高频刷新会对服务器造成不必要的压力。尤其是公司内部后台系统可能同时有很多人挂着自动刷新服务器的负载会飙升。我的经验是不要用小于3秒的间隔去刷新带复杂页面的站点。静态页面开5秒也就罢了动态数据展示页面至少设置10秒以上。如果只是为了监控数据变化优先使用局部刷新方案也就是第2节里的那个fetch替换DOM的方式它只请求数据接口的体积通常比整个页面小得多。另外很多站点有防频繁刷新机制短时间连续刷新会触发403或者限流届时你不但刷不出新数据反而把自己的IP给封了。这也是为什么脚本里最好加一个随机抖动的原因function getJitteredInterval(base) { return base Math.floor(Math.random() * 3000); } setTimeout(function() { location.reload(); }, getJitteredInterval(10000));10秒基础上随机增加0-3秒请求分布更自然不容易触发风控。5.3 长时运行的内存泄露与进程占用用浏览器扩展脚本可能感觉不明显但用Node.js方案时长时间运行Puppeteer很容易遇到内存增长问题。主要原因是浏览器实例和页面对象没有被彻底清理。建议每个周期执行完后调用global.gc()手动触发垃圾回收——不过这需要你启动Node进程时加上--expose-gc参数node --expose-gc your-script.js然后在每次循环结束后调用if (global.gc) { global.gc(); }对于超过一周的长任务还可以设计一个兜底重启逻辑如果连续N次抓取结果为空自动重启浏览器进程避免浏览器内部状态卡死。6. 脚本的进阶玩法条件触发与多页面管理6.1 只在数据变化时才刷新定时刷新其实是一种“无脑轮询”。更优雅的方案是先抓取数据存进内存然后定时对比只有当内容发生变化时才做刷新或者通知。这能大幅减少不必要的页面刷新请求。我写过一个简化的版本let lastSnapshot ; setInterval(async () { const currentSnapshot await fetchPageData(browser); if (currentSnapshot ! lastSnapshot) { console.log(数据发生变化执行刷新); lastSnapshot currentSnapshot; // 触发刷新或通知逻辑 } else { console.log(数据未变跳过刷新); } }, 30000);把fetchPageData返回的数据字符串化后对比无论是长度比较还是内容比较都有个前提你提取的数据必须是稳定结构不能包含时间戳或不固定的随机内容否则每次都会判定为“变了”。6.2 多页面监控队列当你需要同时监控5个、10个页面时单页面循环就不太够用了。我建议维护一个待监控URL数组用队列的方式依次刷新这样既能避免所有页面同时刷新对网络的冲击又能在单个页面异常时不至于影响其他任务。const urls [ https://site1.com/page, https://site2.com/page, https://site3.com/page ]; async function processQueue(browser) { for (const url of urls) { try { await fetchPageData(browser, url); } catch (e) { console.error(监控 ${url} 失败, e.message); } // 每个页面之间间隔随机时间防止请求过于集中 await sleep(1000 Math.random() * 2000); } } function sleep(ms) { return new Promise(resolve setTimeout(resolve, ms)); }这样做的好处是任何一个页面超时或者报错都会被单独捕获不会导致整个脚本退出。6.3 与命令行参数结合如果你需要让脚本更具通用性推荐用process.argv接收命令行参数比如指定刷新地址、间隔、最大运行次数等const args process.argv.slice(2); const url args[0] || https://example.com; const interval parseInt(args[1], 10) || 10000; const maxRuns parseInt(args[2], 10) || Infinity;这样就不需要每次改代码直接node monitor.js https://example.com/data 15000 20跑20次后自动停止。在写批量采集计划的时候这个做法能省很多事。7. 实测对比与选型建议为了让你直观了解各方案的适用场景我整理了一张自己长期使用下来形成的对照表方案上手难度生效范围持久性适用场景控制台代码极低当前页面刷新即失效临时快速查看油猴脚本低匹配的站点浏览器重启仍生效日常开发调试、个人使用自制扩展中自己控制的范围浏览器重启仍生效需要分享给团队、正式发布Node.js Puppeteer较高无页面限制进程结束为止数据采集、定时监控、自动化测试Node.js Playwright较高多浏览器支持进程结束为止跨浏览器测试、多端一致性验证从这个表能看出没有一个方案是“绝对最好”的。平时调试几个页面油猴脚本足够涉及生产环境的数据监控直接上Puppeteer或Playwright。我个人最常用的组合是开发调试时用控制台快速验证写稍微正式一点的任务时用油猴脚本批量数据监控就交给Node.js方案。这套组合已经跑了一年多稳定性和便利性都令我满意。8. 你能直接拿去用的完整带配置脚本最后分享一个我自己日常在用的油猴脚本完整版带有简单的键盘快捷键配置。按AltR启动或停止自动刷新按AltUp和AltDown调节间隔页面会有简单的状态提示。核心代码如下// UserScript // name 多功能自动刷新器 // namespace http://your-namespace.com/ // version 2.1 // description 支持启动/停止、间隔调整、滚动位置保存的自动刷新脚本 // match *://*/* // grant none // /UserScript (function() { use strict; let enabled false; let timer null; let interval parseInt(localStorage.getItem(arb_interval), 10) || 10000; function showStatus(msg) { console.log([自动刷新], msg); } function saveScroll() { sessionStorage.setItem(arb_scroll, window.scrollY); } function restoreScroll() { const y sessionStorage.getItem(arb_scroll); if (y ! null) { setTimeout(() window.scrollTo(0, parseInt(y, 10)), 120); } } function start() { if (enabled) return; enabled true; saveScroll(); timer setInterval(() { saveScroll(); location.reload(); }, interval); showStatus(已启动间隔 interval ms); } function stop() { if (!enabled) return; enabled false; clearInterval(timer); timer null; showStatus(已停止); } function changeInterval(delta) { const newVal interval delta; if (newVal 2000 || newVal 600000) return; interval newVal; localStorage.setItem(arb_interval, interval); if (enabled) { clearInterval(timer); timer setInterval(() { saveScroll(); location.reload(); }, interval); } showStatus(间隔调整为 interval ms); } window.addEventListener(beforeunload, saveScroll); window.addEventListener(load, restoreScroll); document.addEventListener(keydown, (e) { if (e.altKey e.code KeyR) { e.preventDefault(); enabled ? stop() : start(); } if (e.altKey e.code ArrowUp) { e.preventDefault(); changeInterval(-1000); } if (e.altKey e.code ArrowDown) { e.preventDefault(); changeInterval(1000); } }); if (window.self window.top) { console.log(自动刷新器已加载。按 AltR 启动/停止Alt↑/↓ 调整间隔); } })();复制到Tampermonkey里新建脚本就能用。快捷键不要太顺手不然很容易误触。我第一次测试时写的是CtrlR结果和浏览器刷新快捷键冲突了还没反应过来页面就被刷新了。改成Alt组合键后这个问题就消失了。最后一个建议不管用哪种方式实现自动刷新都要记得在不需要的时候及时关掉。别问我为什么知道——曾经有个后台监控页挂了一天自动刷新数据没抓到几条系统的访问日志倒是刷新了厚厚一叠。脚本是工具好不好用全看怎么用。
返回列表