跳到正文
文章封面

Playwright 本地批量网页长截图:替代在线工具的稳定自动化方案

在线截图服务受配额、隐私和页面复杂度限制;用 Playwright 在本地或 CI 里跑同一套脚本,长页与批量 URL 可复现、可版本管理,是高热度网社区里更稳的「黑科技」实用工具路线。

在线截图工具的局限与本地自动化的适用场景

常见在线工具的问题

  • 配额与限速:免费档每日次数有限,批量 URL 容易触顶。
  • 隐私与合规:内网、登录态、含敏感数据的页面不宜上传到第三方。
  • 页面还原差:懒加载、无限滚动、自定义字体、WebGL 常截不全或空白。
  • 不可复现:同一 URL 不同时间结果不一致,难以做回归对比。

适合用 Playwright 本地/CI 的场景

场景 说明
文档/营销页归档 固定视口全页 PNG,入库或发 PR 附件
多环境对比 staging / prod 同一脚本批量截,diff 视觉
移动端验收 切换 device 描述符,一次跑 iPhone/Android 视口
定时巡检 cron 或 GitHub Actions nightly 截关键页面

自动化测试栈里 Playwright 本就是为「真实浏览器渲染」设计的,拿来做网页截图是顺手能力,不必再绑 Selenium 或 Puppeteer 若你已在用 Playwright 测 E2E。

Node 环境安装 Playwright 与最小可运行脚本

环境要求

  • Node.js 18+(LTS 推荐)
  • 磁盘空间:Chromium 浏览器包约 150MB+

安装步骤

mkdir playwright-screenshots && cd playwright-screenshots
npm init -y
npm install playwright
npx playwright install chromium

只装 Chromium 可减小体积;需要 WebKit/Firefox 时再 npx playwright install

最小可运行脚本

新建 screenshot.js

const { chromium } = require('playwright');
const path = require('path');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle' });
  await page.screenshot({ path: 'example.png', fullPage: true });
  await browser.close();
  console.log('saved example.png');
})();

运行:

node screenshot.js

常见安装坑

  • playwright install 未执行:运行时报找不到 browser,务必在项目里执行 npx playwright install chromium
  • 公司代理:设置 HTTPS_PROXY / HTTP_PROXY 后再 install。
  • Linux 无头依赖:CI 里若缺库,用 Playwright 官方 Docker 镜像或 npx playwright install-deps(需 sudo)。

长页面全屏截图、移动端视口与批量 URL 配置

长页面全屏截图

fullPage: true 会滚动并拼接整页;注意:

await page.goto(url, {
  waitUntil: 'networkidle', // 或 'domcontentloaded' 加快,但可能截到未加载图
  timeout: 60000,
});

// 懒加载:滚到底再截
await page.evaluate(async () => {
  await new Promise((resolve) => {
    let total = 0;
    const distance = 400;
    const timer = setInterval(() => {
      window.scrollBy(0, distance);
      total += distance;
      if (total >= document.body.scrollHeight) {
        clearInterval(timer);
        window.scrollTo(0, 0);
        resolve();
      }
    }, 100);
  });
});

await page.screenshot({
  path: outPath,
  fullPage: true,
  type: 'png',
});
  • 超大页内存:极高页面可能 OOM,可改用 clip 分段截再拼接,或限制最大高度。
  • deviceScaleFactor:Retina 清晰图可 page.setViewportSize({ width: 1280, height: 720 }); 并设 deviceScaleFactor: 2(在 newContext 时)。

移动端视口

用内置设备描述符,比手调 UA 更稳:

const { devices } = require('playwright');
const iPhone = devices['iPhone 13'];

const context = await browser.newContext({
  ...iPhone,
});
const page = await context.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({ path: 'mobile.png', fullPage: true });
await context.close();

常用:iPhone 13Pixel 5Galaxy S9+。高热度网博客做响应式验收时,同一 URL 循环多种 devices 即可。

批量 URL 配置

推荐 urls.json + 循环,失败单条不拖死整批:

const fs = require('fs');
const { chromium } = require('playwright');

const urls = JSON.parse(fs.readFileSync('urls.json', 'utf8'));
// urls.json: [ { "url": "https://a.com", "name": "home" }, ... ]

(async () => {
  const browser = await chromium.launch({ headless: true });
  const outDir = path.join(__dirname, 'output');
  fs.mkdirSync(outDir, { recursive: true });

  for (const item of urls) {
    const page = await browser.newPage();
    try {
      await page.goto(item.url, { waitUntil: 'networkidle', timeout: 90000 });
      const file = path.join(outDir, `${item.name || 'shot'}.png`);
      await page.screenshot({ path: file, fullPage: true });
      console.log('OK', item.url);
    } catch (e) {
      console.error('FAIL', item.url, e.message);
    } finally {
      await page.close();
    }
  }
  await browser.close();
})();
  • 并发:默认串行最稳;要快可用 p-limit 限制 3–5 并发,避免内存暴涨。
  • 文件名:用 name 或 URL hash,避免非法字符。

定时任务与 GitHub Actions 集成时的路径、权限与失败重试

本地定时任务(cron)

# 每天 2 点,日志追加
0 2 * * * cd /path/to/playwright-screenshots && /usr/bin/node batch.js >> /var/log/playwright-shot.log 2>&1
  • 使用绝对路径nodewhich node)。
  • 工作目录必须是项目根,否则相对路径 output/urls.json 会错。

GitHub Actions 要点

.github/workflows/screenshots.yml 示例骨架:

name: nightly-screenshots

on:
  schedule:
    - cron: '0 18 * * *'  # UTC,按需要改
  workflow_dispatch:

jobs:
  shot:
    runs-on: ubuntu-latest
    permissions:
      contents: write  # 若要 commit 截图回仓库
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npx playwright install chromium --with-deps
      - run: node batch.js
        env:
          CI: true
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: screenshots
          path: output/

路径与权限

  • Artifact 路径:与脚本里 output/ 一致; monorepo 时在 working-directory 下执行 node scripts/batch.js
  • contents: write:仅在你需要 git add output && git commit 时开启;公开仓注意勿提交含 token 的页面截图。
  • Secrets:登录态用 storageState 从加密 secret 写入 JSON,勿写进仓库。
// 使用已保存登录态(本地生成一次 login.js 保存 state.json)
const context = await browser.newContext({
  storageState: process.env.STORAGE_STATE_PATH || 'state.json',
});

失败重试

  • 单 URL:简单重试 2–3 次,指数退避。
async function gotoWithRetry(page, url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      await page.goto(url, { waitUntil: 'networkidle', timeout: 90000 });
      return;
    } catch (e) {
      if (i === retries - 1) throw e;
      await new Promise((r) => setTimeout(r, 2000 * (i + 1)));
    }
  }
}
  • Workflow 级:对 flaky 网络可 continue-on-error: true 单步 + 汇总 job;或 nick-fields/retry@v2 包裹 node batch.js
  • CI 稳定性launch({ headless: true }) 足够;避免依赖 slowMo;超时比本地略大。

与自动化测试的关系

同一仓库可共用 @playwright/test 的配置与 globalSetup 登录;截图脚本保持独立 node batch.js 也可,减少测试报告耦合。Playwright 官方 trace 不必为纯截图开启。


小结:用 Node Playwright 做网页截图批处理,核心是 fullPage、视口/device、批量 JSON 与 CI 里 install --with-deps、artifact 路径一致。替代在线工具后,流程可进 Git、可重试,更适合高热度网这类追求可复现黑科技的日常实用工具流。

评论

填写昵称与邮箱即可评论,无需登录。

推荐阅读

Playwright 本地批量网页长截图:替代在线工具的稳定自动化方案 - 高热度网