Puppeteer使用示例详解:从页面自动化到截图与请求拦截实战

做前端或者自动化相关开发的同学,基本都听过Puppeteer使用示例详解这类资料。但很多教程贴一上来就丢一段 launch 代码,跑通就完事,真正遇到坑反而没人讲。作为一个常年拿 Puppeteer 做页面自动化、批量截图和线上巡检的人,我打算把平时用得最多的几组示例跟背后的原理一起拆开讲讲,代码可以直接抄,坑也先替你踩一遍。

Puppeteer 是 Node.js 生态里最经典的浏览器自动化库,通过 DevTools 协议驱动无头 Chrome/Chromium,能模拟点击、填表单、截图、导出 PDF、拦截请求、跑性能分析。适合做自动化测试、页面监控、批量生成报告、定时抓取公开页面数据这类场景。不管你是测试工程师、前端开发还是运维,只要需要和网页打交道,这套工具值得花半小时掌握。

1. 先搞清楚 Puppeteer 到底解决什么问题

1.1 一句话讲明白它的定位

很多新手第一次看到 Puppeteer 的代码会困惑:这不就是操作浏览器吗?我用按键精灵不行吗?区别在于,Puppeteer 不是靠模拟鼠标键盘那种“物理外挂”方式,而是直接通过 Chrome 的 DevTools 协议和浏览器内核通信。这意味着它能做到很多人类操作做不到的事,比如拦截网络请求、直接修改页面 JavaScript 上下文、读取浏览器性能指标。

换个角度理解:它是把浏览器当成一台可以编程控制的设备。你不需要真的打开一个窗口、用眼睛去看页面长什么样,只要给它一个网址,它就能在后台帮你去访问、解析、操作、收集数据。传统手工操作浏览器需要几十分钟的事情,脚本批量跑几分钟就能完成。

我自己的项目里,最常见的使用场景是这么几类:

  • 页面自动化测试:特别是端到端测试,模拟用户走完注册、登录、下单流程,比纯单元测试更有说服力
  • 批量截图:定时给公司官网首页截图,做视觉回归测试;或者给文档站点生成预览图
  • 生成 PDF:把后台报表页面导出成 PDF,按周推送邮件,省去手动打印的环节
  • 公开页面数据采集:对无登录墙的公开信息做定时采集,比如商品价格对比、行业排行榜
  • 预渲染:把单页应用在服务端跑一遍,输出完整的 HTML,解决部分 SEO 需求的兜底方案

1.2 它和 Selenium、Playwright 的差别在哪

选型的时候大家总喜欢对比这几个工具,我直接说结论:Puppeteer 优势在于和 Chrome 结合最紧密、依赖最轻,因为它是 Chrome 团队官方维护的。Selenium 支持多浏览器,但配置更重,要装驱动,还要维护浏览器版本匹配;Playwright 是后起之秀,功能很强,支持多浏览器,但如果你只需要 Chrome/Chromium,Puppeteer 完全够用,API 设计也更符合前端直觉。

选型建议:大厂多浏览器兼容测试选 Playwright,传统企业测试平台选 Selenium,个人项目或数据自动化直接上 Puppeteer。一个项目里如果只瞄准 Chromium 内核,Puppeteer 的安装成本和体积都是最小的。

2. 环境准备与安装实操

2.1 安装以及下载 Chromium 的坑

Puppeteer 是一个 Node.js 包,安装没什么特殊要求,Node 版本建议 14 以上。我的建议是遇到问题先用 LTS 版本,避免因为 Node 过新导致原生模块编译失败。

新建项目目录后执行:

npm init -y npm install puppeteer

默认情况下,这条命令会把兼容版本的 Chromium 一起下载到本地。这一步通常是最容易出现问题的,因为 Chromium 包体积不小,而且下载地址在国外,网络环境不好时会非常慢,甚至直接卡住。

如果你遇到下载超时,有两个思路。第一个思路是用镜像源下载,设置环境变量指向国内镜像地址,再重新安装:

PUPPETEER_DOWNLOAD_BASE_URL=https://npmmirror.com/mirrors/chrome-for-testing npm install

第二个思路是放弃自动下载,使用 puppeteer-core,只装 API 不装浏览器,然后指向系统里已有的 Chrome。这也是我推荐的方式,省空间还能避免反复踩下载的坑:

npm install puppeteer-core

注意:puppeteer-core 不会自动下载 Chromium。launch 时必须手动传 executablePath 指定浏览器路径,否则会报“No executable found”的错。

2.2 连接本地 Chrome 的实战配置

安装完之后先做一次最小启动验证。下面这段代码确认环境是否正常:

const puppeteer = require('puppeteer-core'); (async () => { const browser = await puppeteer.launch({ headless: 'new', executablePath: '/usr/bin/chromium' }); const page = await browser.newPage(); await page.goto('https://example.com'); console.log(await page.title()); await browser.close(); })();

headless 这个参数值得单独说一下。老版本里写headless: true就行,新版本 Chrome 推出了新的无头模式,写headless: 'new'可以获得和旧模式更接近的真实渲染效果。实测下来,新无头模式在字体渲染、WebGL 支持方面都比旧模式更接近有头浏览器,建议默认使用。

如果你是在服务器上以 root 用户运行,经常会遇到沙箱报错。这时候可以加两个 Chrome 启动参数:

const browser = await puppeteer.launch({ headless: 'new', executablePath: '/usr/bin/chromium', args: ['--no-sandbox', '--disable-setuid-sandbox'] });

我在多台服务器上跑过,这条基本能解决启动即崩溃的问题。但要注意,关闭沙箱有安全隐患,只建议在隔离的容器或专用服务器上这样做,不要在有其他服务的机器上随便关。

3. 截图与 PDF 生成示例

3.1 全屏截图和视口设置的正确姿势

截图应该算 Puppeteer 最常用的功能了。最简单的截图代码三行就能完成:

const page = await browser.newPage(); await page.goto('https://example.com', { waitUntil: 'networkidle2' }); await page.screenshot({ path: 'homepage.png', fullPage: true });

fullPage: true表示截取整个页面高度,而不是只截可视区域。这个参数背后的逻辑是浏览器会临时调整视口高度到整个页面的滚动高度再截图,截完再恢复。所以如果页面里有用到固定定位的浮层,截长图时可能出现浮层重复出现的情况,这是 Chrome 渲染机制决定的,遇到的话把浮层隐藏后再截。

视口设置的坑最多。移动端页面截图必须提前设置好宽度和高度,不然默认 800x600 的桌面视口会让移动端布局完全走样:

await page.setViewport({ width: 375, height: 812, deviceScaleFactor: 2 });

deviceScaleFactor: 2模拟的是 iPhone 的 2 倍像素密度,截出来的图清晰度更高,适合做设计稿对比。但要注意,这个值会直接影响截图输出文件的体积,大批量截图时谨慎使用。

如果只想截页面里某一个区域,比如某个图表卡片、某段代码块,不要自己算坐标去裁剪,直接用元素选择器:

const el = await page.$('.chart-card'); await el.screenshot({ path: 'chart.png' });

用元素截图的好处是页面滚动后元素位置变了也能正确定位,比自己裁图片靠谱得多。

3.2 生成 PDF 的参数细节

把页面转 PDF 是另一个高频需求。Puppeteer 的 page.pdf 接口可以直接输出带排版的文件,不需要安装额外的打印组件:

await page.goto('https://example.com/report', { waitUntil: 'networkidle2' }); await page.pdf({ path: 'report.pdf', format: 'A4', printBackground: true, preferCSSPageSize: true, margin: { top: '15mm', right: '10mm', bottom: '15mm', left: '10mm' } });

两个重要细节:

第一个,printBackground: true一定要加。如果不加,PDF 里所有背景色和 background-image 都会消失,页面瞬间变成白底黑字,视觉上跟丢了一半样式一样。我第一次生成报表 PDF 时忘了这个参数,结果图表区域一片空白,排查了半小时才发现是背景色被自动去掉了。

第二个,preferCSSPageSize: true表示优先使用 CSS 里的 @page 规则设置的页面大小。如果前端页面已经按 A4 设计好了打印样式,加上这个参数能精确控制页面尺寸,反之则用 format 参数统一指定。

还有一点要记住:page.pdf 在旧版无头模式(headless: true)下工作正常,但在某些非无头版本下会报错。如果你发现 pdf 调用失败,优先检查是不是 headless 配置不对。

提示:截图和 PDF 都建议在 waitUntil: 'networkidle2' 之后再执行。networkidle2 表示 500ms 内网络请求数不超过 2 个,这时候页面资源基本加载完毕,比默认的 load 事件更可靠。

4. 页面自动化交互示例

4.1 等待策略:从 sleep 到 waitFor

新手写交互代码最常犯的错误就是到处用await page.waitForTimeout(3000)这种死等。死等的问题是网络快的时候浪费时间,网络慢的时候又不够用,属于两头都吃亏。

正确思路是“等条件,而不是等时间”。Puppeteer 提供了一套完整的等待方法:

方法作用适用场景
page.waitForSelector等某个选择器出现在 DOM 中最常用,等按钮、等列表加载
page.waitForFunction等待页面内 JS 执行结果为真等数据渲染完成、等状态变化
page.waitForNavigation等待跳转完成点击后触发页面跳转时用
page.waitForRequest等待某个网络请求发出配合接口触发判断使用
page.waitForResponse等待某个网络请求返回等接口数据返回后继续操作

举个例子,页面上有个列表是异步加载的,如果用死等大概率会踩中加载时序问题。正确写法是这样:

await page.click('.load-more-btn'); await page.waitForSelector('.item-card');

点击按钮后,等待新卡片元素出现在 DOM 里,再继续下面的断言或采集。这样不管接口响应是 200ms 还是 2000ms,代码都能正确工作。

最实用的组合是 waitForFunction 配 document.querySelector,处理单页应用里数据刷新的场景:

await page.waitForFunction( (selector, text) => { const el = document.querySelector(selector); return el && el.textContent.includes(text); }, { timeout: 10000 }, '.status', '已完成' );

等页面上的状态文本变成“已完成”再继续,比任何固定超时都准确。

4.2 表单填写、点击与弹窗处理

模拟用户提交表单是端到端测试的核心场景。基础操作包括输入文本、点击按钮。一段完整的登录表单自动化一般是这样的:

await page.type('#username', 'test_user', { delay: 30 }); await page.type('#password', 'secret123', { delay: 30 }); await page.click('#login-btn'); await page.waitForNavigation({ waitUntil: 'networkidle2' });

delay参数模拟真实用户逐字输入的速度,主要为了让页面事件监听器有足够时间响应。很多框架在 input 事件里做了防抖处理,如果 value 是一瞬间设进去的,可能触发不了后续逻辑,所以模拟真实输入反而比直接赋值更可靠。

如果你要填的内容是纯数字验证码或者需要清空再输入,先要清掉输入框默认值:

await page.$eval('#phone', (el) => { el.value = ''; }); await page.type('#phone', '13800138000', { delay: 10 });

弹窗和对话框是经常被忽略的。页面里的 alert 如果不处理,脚本会一直卡住,因为无头模式下不会有人去点确定。要提前挂上事件监听:

page.on('dialog', async (dialog) => { console.log('弹窗内容:', dialog.message()); await dialog.accept(); });

不要小看这个监听,少了它脚本可能会静默挂起十分钟直到超时。我用一个演示项目测试过,加了 dialog 监听后整体稳定性提升非常明显。

4.3 多标签页与 iframe 处理

实际操作中经常要打开多个页面。比如巡检场景下,需要同时打开十几个页面做健康检查。Puppeteer 支持在一个浏览器实例里开多个 page:

const pageA = await browser.newPage(); const pageB = await browser.newPage();

每个 page 是独立的标签页,拥有独立的会话和存储。但注意,同一浏览器实例里的多个 page 共享同一个 userDataDir,也就是共享 Cookie 和本地存储。如果你需要隔离登录态,必须用browser.createIncognitoBrowserContext()创建隐身上下文:

const context = await browser.createIncognitoBrowserContext(); const page = await context.newPage();

iframe 的处理是另一个麻烦。页面里有内嵌 iframe 时,:hover和普通的 selector 都定位不进去,必须切换到 frame 对象:

const frame = await page.frames().find(f => f.url().includes('widget')); const button = await frame.$('.widget-submit'); await button.click();

如果 iframe 加载较慢,可以先等 frame 出现:

await page.waitForFrame('https://widget.example.com');

我实测过很多页面,iframe 是自动化测试里最容易出问题的点,特别是跨域 iframe 会限制很多 DOM 操作。遇到跨域限制场景,尽量从主页面入手,或者用接口测试代替页面自动化。

5. 请求拦截与脚本注入

5.1 拦截请求,屏蔽图片和统计接口

Puppeteer 有一个杀手级功能,就是能拦截页面发出的所有网络请求。这可以大幅提升页面加载速度和稳定性。

最典型的场景是采集公开列表数据时,根本不需要图片资源,把图片请求全部丢弃,页面加载速度能快出好几倍:

await page.setRequestInterception(true); page.on('request', (request) => { const type = request.resourceType(); if (['image', 'stylesheet', 'font'].includes(type)) { request.abort(); } else { request.continue(); } });

同样的逻辑还能用来屏蔽统计类接口。很多站点会加载第三方数据上报脚本,这类请求既占带宽又无关紧要,可以直接忽略:

page.on('request', (request) => { const url = request.url(); if (url.includes('analytics') || url.includes('tracking')) { request.abort(); } else { request.continue(); } });

请求拦截还有一个实用场景是修改接口返回数据。配合request.respond能伪造接口响应,做前端异常场景的测试非常方便:

page.on('request', (request) => { if (request.url().includes('/api/user')) { request.respond({ status: 500, contentType: 'application/json', body: JSON.stringify({ message: '服务异常' }) }); } else { request.continue(); } });

用这个方案可以模拟接口 500、超时、返回空数据等情况,不需要真的把后端服务弄挂。前端异常提示写得好不好,一张伪造响应就能验证出来。

5.2 在页面里注入自定义脚本

有时你需要直接在页面上下文里执行 JavaScript,读取或修改数据。page.evaluate就是干这个的:

const data = await page.evaluate(() => { const rows = document.querySelectorAll('.list-item'); return Array.from(rows).map(row => { return { title: row.querySelector('.title')?.textContent?.trim(), price: row.querySelector('.price')?.textContent?.trim() }; }); }); console.log(data);

evaluate 里写的是浏览器环境里的 JS,不是 Node 环境。所以不能调用 Node 的 require、module 之类的 API,这一点新手经常搞混。

如果你要在页面里先注入一段通用的工具函数再执行采集逻辑,可以用page.addScriptTag:

await page.addScriptTag({ url: 'https://cdn.example.com/helper.js' }); // 或者注入内联代码 await page.addScriptTag({ content: `window.__helper = { formatPrice: (p) => '¥' + Number(p).toFixed(2) }` });

还有一种更进阶的注入手法,利用page.evaluateOnNewDocument在每次新页面创建时就注入代码。它会在页面任何脚本运行之前执行,因此可以用来改写全局变量,模拟特殊环境:

await page.evaluateOnNewDocument(() => { Object.defineProperty(navigator, 'webdriver', { get: () => false }); });

这行代码把 navigator.webdriver 改成了 false,可以在一定程度上减少被页面反自动化脚本识别的概率。这类技巧属于双方博弈的范畴,我只建议在合法合规的测试场景里使用,不要拿去做突破访问限制的事情。

5.3 并发采集与性能调优

跑批量任务时,一个个顺序打开页面太慢了。我常用的提速方案是在同一个浏览器里控制并发数,合理开多个 page:

async function processUrls(urls, concurrency = 5) { const browser = await puppeteer.launch({ headless: 'new' }); const results = []; const workers = Array.from({ length: concurrency }, async () => { while (urls.length > 0) { const url = urls.pop(); if (!url) break; const page = await browser.newPage(); try { await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 }); const data = await page.evaluate(() => document.title); results.push(data); } catch (err) { console.error('处理失败:', url, err.message); } finally { await page.close(); } } }); await Promise.all(workers); await browser.close(); return results; }

这里的关键工程点是每次处理完都调用 page.close()。不关页面会导致浏览器内存持续上涨,批量跑上千个 URL 时内存直接被打满,这是我踩过最大的坑之一。

并发数也不是越大越好。实践下来,单机场景并发 5 到 8 个页面的性价比最高。并发太大会导致 CPU 和内存争抢,页面加载反而变慢,还可能被目标站点限制访问。

采集过程中加延时也是很有必要的。给每个页面加 200 到 800 毫秒的随机延迟,能降低对目标服务器的瞬时压力,也更接近真实用户行为。这个值不用太大,但完全不加的话,短时间大量请求会显得非常“机器化”。

6. 常见问题与排查心得

6.1 启动失败的几种典型场景

这么多年用下来,启动失败排行前几的原因无非就这几个。

沙箱问题表现最直接,报错里带有sandbox字样。解决方式就是前面提到过的加--no-sandbox参数。这个报错常见于 CentOS 7 的服务器和部分 Docker 镜像,原因通常是系统没有正确配置用户命名空间。

executablePath报错指的是你给 puppeteer-core 配置的浏览器路径不存在。这个坑很隐蔽,因为很多 Linux 发行版的 Chromium 可执行文件不一定在/usr/bin/chromium,有的是/usr/bin/chromium-browser,还有 Chrome for Testing 的路径里带着版本号。解决方案是先which chromium或者find / -name '*chrome*' 2>/dev/null确认真实路径。

还有一类是缺少依赖库。Chromium 在 Linux 上依赖很多动态链接库,尤其是libX11、libnss3、libatk之类的图形库。最典型的报错是:

error while loading shared libraries: libnss3.so

这类问题在裸的容器镜像里最常见,需要安装对应依赖包,比如 CentOS 执行yum install -y nss atk at-spi2-atk cups-libs libdrm libxkbcommon-x11。如果你用的是别人维护的 Docker 镜像,最好先确认镜像里带不带这些默认库。

6.2 元素找不到与超时处理

waitForSelector 超时是最常见的运行期报错。问题往往是选择器写错了,或者页面元素在鼠标点击之后才异步渲染。

排查思路可以从这几个角度入手:

  • 先确认页面中是否真的存在目标元素,用page.$试一下
  • 用page.content()打印当前 HTML,看元素到底是什么时候出现的
  • 选择器是否太脆弱,比如依赖了动态生成的 class 名

动态 class 是另一个大坑。很多现代前端框架(各种组件库、CSS-in-JS 方案)会生成哈希类名,你选中一次,下一次重新部署可能就变了。这个问题的解决思路不是每次去改选择器,而是优先用相对稳定的属性定位,比如>const el = await page.$('button:contains("确认")'); // 或者用 XPath const xpath = '//button[contains(text(), "确认")]'; const button = await page.$x(xpath);

$X 返回的是数组,用的时候记得取第一个元素。

如果某个元素时有时无,多半是接口偶发超时或前端重试机制造成的。稳妥做法是包一层重试逻辑:

async function waitForSelectorWithRetry(selector, page, attempts = 3) { for (let i = 0; i < attempts; i++) { try { await page.waitForSelector(selector, { timeout: 8000 }); return; } catch (err) { if (i === attempts - 1) throw err; await page.reload({ waitUntil: 'domcontentloaded' }); } } }

6.3 内存管理与异常退出

长期运行的定时任务必须重视内存管理。这里说的不是内存泄漏,而是浏览器进程本身很吃资源。无头 Chromium 每个页面默认占用几十 MB 到几百 MB 内存,跑完不关闭,堆积起来再稳的服务也会 OOM。

我自己的习惯是每次任务结束都显式调用 browser.close(),而不是只关页面。单纯page.close()不会释放浏览器进程占用的全部资源,反复 launch 和 close 也会留下僵尸进程,时间长了服务器负载会一直升高。

排查僵尸进程可以执行:

ps -ef | grep chrome

如果发现大量残留进程,说明程序在异常退出时没有正确清理。这就要在代码里加上兜底逻辑:

try { // 主要业务逻辑 } finally { await browser.close().catch(() => {}); }

Node.js 默认只在uncaughtException时不会自动退出,但异步异常会导致任务中断。遇到这种问题,我建议用一个简单的守护方式:外层包一个Promise,加上 total timeout,超过 50 秒就强制退出:

const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('总超时, 强制退出')), 50000); }); await Promise.race([runTask(), timeoutPromise]);

用这套方案,一个批量巡检脚本在服务器上跑了几百轮没出现过卡死的情况。

调试的时候还有一个小技巧:不要一上来就跑无头模式,先用headless: false加slowMo: 200打开浏览器,看真实页面操作过程。可以设置devtools: true甚至打开开发者工具看网络请求。一旦业务逻辑验证通过,再切回headless: 'new'投入生产运行。这个调试验证方式帮我定位了无数个诡异问题。

收个尾,说点实在的

最后再分享几点我的实际体会。Puppeteer 看起来是个简单的自动化工具,但项目里大量脚本不稳定的根源,往往不是 Puppeteer 本身,而是对页面加载时序的理解不够。你等待的元素到底什么时候出现,依赖的接口什么时候返回,页面跳转是原生路由还是整页刷新,这些细节决定了脚本的稳定性。

我习惯是刚开始写脚本时多花几分钟,用page.on('console')和page.on('requestfailed')把页面的关键日志监听起来,一旦后续运行出问题,能看到运行现场的第一手信息。这比猜原因高效太多。

这个工具目前还在持续进化,新版本不断调整无头模式内核和 API 行为。建议固定一个版本长期使用,不要每次升级都随手改代码。等老项目稳定运行了,再单独抽时间评估新版本迁移。版本锁定了,你的自动化脚本就不会因为一个依赖升级莫名其妙断掉。

相关新闻

OpenClaw架构深度拆解:分布式任务调度与编排系统的设计精髓

OpenClaw架构深度拆解:分布式任务调度与编排系统的设计精髓

做架构拆解这些年&#xff0c;我拆过不少系统&#xff0c;但OpenClaw这套设计是少数让我觉得“值得认真写一篇长文”的项目。它不追求花哨的技术栈堆砌&#xff0c;而是在总体架构的每一个决策点上都体现出了对一致性、可扩展性和可观测性的深思熟虑。这篇拆解不会只罗列功能模…

2026/10/20 16:53:05 阅读更多 →
Apache Pulsar 2.9.1部署实战:从单机到集群的配置与避坑指南

Apache Pulsar 2.9.1部署实战:从单机到集群的配置与避坑指南

简介&#xff1a;Pulsar 2.9.1 是一个由 Apache 社区维护的高性能消息中间件二进制发布包&#xff0c;专为分布式与云原生场景设计。它依托 ZooKeeper 完成元数据协调与集群一致性管理&#xff0c;适合需要在大规模多节点之间搭建高可靠消息通道的开发者或架构师。压缩包约321.…

2026/10/20 16:53:05 阅读更多 →
Spring Boot + Vue 在线图书商城毕设源码详解:从环境搭建到系统部署

Spring Boot + Vue 在线图书商城毕设源码详解:从环境搭建到系统部署

简介&#xff1a;面向毕业设计或课程设计场景的Spring Boot与Vue.js前后端分离图书商城实战项目&#xff0c;适合需要完整可运行源码、论文及答辩材料的计算机相关专业学生。资源包约66.44MB&#xff0c;包含全套前后端源码、SQL数据库脚本、毕业设计论文与开题报告&#xff0c…

2026/10/20 16:53:05 阅读更多 →

最新新闻

Ubuntu下PostgreSQL安装配置与Navicat连接指南

Ubuntu下PostgreSQL安装配置与Navicat连接指南

做后端开发或者数据相关工作的朋友&#xff0c;应该都经历过这么一幕&#xff1a;好不容易在 Ubuntu 上把 PostgreSQL 装好了&#xff0c;却发现命令行操作没问题&#xff0c;客户端工具却怎么都连不上&#xff1b;或者反过来&#xff0c;Navicat 装好了&#xff0c;连接时报一…

2026/10/20 17:33:06 阅读更多 →
uniCloud Serverless 实战:英语学习小程序全链路开发

uniCloud Serverless 实战:英语学习小程序全链路开发

简介&#xff1a;这是一套面向英语学习者与小程序开发者的完整项目源码&#xff0c;基于uniapp与uniCloud Serverless架构构建&#xff0c;可直接在微信环境中运行。项目集生词本记录、拍照识别单词、内置词典查询、AI语音翻译、跟读打分评测于一体&#xff0c;并针对考研人群补…

2026/10/20 17:33:06 阅读更多 →
WiFi指纹室内定位实战:从原理到Demo搭建

WiFi指纹室内定位实战:从原理到Demo搭建

1. 室内定位的现实困境与WiFi方案的破局点实话实说&#xff0c;第一次接手“基于WiFi的室内定位”这个方向时&#xff0c;我心里是打鼓的。日常大家用GPS导航已经习惯了&#xff0c;掏出手机就能知道自己在哪&#xff0c;但一旦走进大型商场、地下车库、机场航站楼&#xff0c;…

2026/10/20 17:33:06 阅读更多 →
大模型后端长对话记忆架构全解析:从无状态到有状态

大模型后端长对话记忆架构全解析:从无状态到有状态

做后端的人都有过这种体验&#xff1a;用户和AI助手聊了三十轮&#xff0c;前二十轮里明明已经把需求背景、偏好、禁忌都交代清楚了&#xff0c;结果聊到第三十一轮&#xff0c;AI突然反问一句“您刚刚提到的那个项目背景&#xff0c;方便再描述一下吗&#xff1f;”当场血压就…

2026/10/20 17:33:06 阅读更多 →
D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

D435相机+OpenCV快速上手:彩色图与深度图实时显示全攻略

最近不少做视觉入门的朋友卡在第一关——把D435相机的画面真正“看”到屏幕上。很多人买了一台Intel D435深度相机&#xff0c;兴冲冲插上USB线&#xff0c;结果CPU占用飙高、程序报错、画面黑屏&#xff0c;折腾一晚上就劝退了。其实用D435搭配OpenCV显示彩色图和深度图&#…

2026/10/20 17:33:06 阅读更多 →
自考论文降AI率实战:智能体改写与人工复核全流程

自考论文降AI率实战:智能体改写与人工复核全流程

自考论文写到提交前&#xff0c;最让人血压升高的往往不是查重率&#xff0c;而是学校突然要求提交一份AI检测报告。A同学就是这种状况&#xff1a;论文初稿在查重系统里已经降到合格线&#xff0c;结果学院的通知里多了一句“AI疑似率不得超过20%”。他拿自己的稿子去测了一下…

2026/10/20 17:32:04 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →