← 中文资源

简体中文版本

构建无头浏览器截图服务

一个完整的截图服务:浏览器池、渲染超时、视口预设、整页截图、安全 URL 处理与输出测试。

阅读英文原文

项目概述

本项目实现无头截图服务:在无头浏览器中渲染页面并按请求视口返回 PNG,配以超时与隔离,使恶意页面无法挂起服务。两个关键问题是超时与隔离——每一个导航都要有界,每个请求都要有独立上下文。

架构与关键决策

以浏览器实例池服务请求、每请求一个上下文,使并发由池大小而非到达流量决定;导航超时覆盖整页生命周期(含网络空闲),使持续加载永不收敛的页面不能长期占用工作进程。URL 守卫校验协议并解析主机,在浏览器导航前拒绝私有与回环地址,且需在重定向后复查,因为重定向是绕过一次性检查的常见手法。

实施与测试

以两个视口截取本地静态页断言 PNG 尺寸;请求 file 或内网地址断言被守卫拒绝;断言永不收敛的页面超时而非挂起;断言两个请求不共享 cookie,证明上下文确为隔离。

限制与后续

  • 仅截取单视口的静态图,不做滚动分页与全页截图。
  • 不对目标 URL 鉴权,私有页面需另传会话。
  • 不存储截图,缓存与保留交给调用方。

代码示例

pnpm init
pnpm add playwright express
pnpm add -D typescript tsx vitest
screenshot-service/
├── src/pool.ts
├── src/capture.ts
├── src/viewport.ts
├── src/guard.ts
├── tests/screenshot.test.ts
└── package.json
const browser = await chromium.launch();
pool.push(browser);
const ctx = await browser.newContext({ viewport });
await page.goto(url, { timeout: 15000, waitUntil: 'networkidle' });
const buf = await page.screenshot({ fullPage, type: 'png' });
if (!isPublicHttpUrl(url)) throw new Error('blocked url');
pnpm vitest run