首页
/ 用 Supabase Edge Functions + Puppeteer 连接 Browserless 实现网页截图服务

用 Supabase Edge Functions + Puppeteer 连接 Browserless 实现网页截图服务

2026-09-06 18:25:09作者:卓艾滢Kingsley

本文以开源仓库 examples/edge-functions 中的 puppeteer 示例为蓝本,讲解如何在 Supabase Edge Functions(基于 Deno)中运行 Puppeteer 无头浏览器截图,并说明为何必须借助 Browserless 这类托管浏览器服务、如何完成本地开发与生产部署。读完本文,你可以独立实现一个“传入 URL 即返回网页 PNG 截图”的 Serverless 接口,并掌握相关密钥、鉴权与调用链配置。

示例做了什么:一个可复制的“URL → PNG”截图接口

先看示例目录结构(puppeteer 函数目录):

supabase/functions/puppeteer/
├── README.md   # 官方说明文档
└── index.ts    # 函数入口与核心实现

示例的核心逻辑非常直观:通过查询字符串传入待截图的网页 url,函数驱动无头浏览器打开该页面并生成一张 PNG 截图返回给调用方。若省略 url 参数,代码会回退到 http://www.example.com 作为默认页面(见 index.ts)。

需要特别说明的是,官方 README 明确指出:Edge Functions 由于资源约束,无法直接运行 Headless Browser 实例。Supabase Edge Functions 以 Deno 运行时为基础、按请求冷启动并受严格的 CPU / 内存配额约束,而 Chrome 内核的单实例开销远高于普通 Serverless 函数预算。因此该示例的落地方式是:让函数扮演“编排者”,通过 WebSocket 远程连接一个托管浏览器服务(示例选用 https://browserless.io),把截图这种重型计算“外包”出去。这也是社区中“Serverless + 托管浏览器”的标准组合模式。

源码实现逐行拆解

完整实现位于 index.ts,仅 30 行,核心代码如下:

import puppeteer from 'npm:puppeteer@^25'
import { withSupabase } from 'npm:@supabase/server@^1'

// Authenticated endpoint, so deploy with verify_jwt = true.
export default {
  fetch: withSupabase({ auth: 'user' }, async (req) => {
    try {
      const browserWSEndpoint = `wss://chrome.browserless.io?token=${Deno.env.get(
        'PUPPETEER_BROWSERLESS_IO_KEY'
      )}`
      // Visit browserless.io to get your free API token
      const browser = await puppeteer.connect({
        browserWSEndpoint,
      })
      const page = await browser.newPage()

      const url = new URL(req.url).searchParams.get('url') || 'http://www.example.com'

      await page.goto(url)
      const screenshot = await page.screenshot()

      return new Response(screenshot as BodyInit, {
        headers: { 'Content-Type': 'image/png' },
      })
    } catch (e) {
      console.error(e)
      return Response.json({ error: e.message }, { status: 500 })
    }
  }),
}

关键点逐条展开:

  • npm 依赖直引:Edge Function 运行在 Deno 上,但代码可直接用 npm: 前缀 import npm 包。import puppeteer from 'npm:puppeteer@^25' 把 Puppeteer v25 拉入函数沙箱;import { withSupabase } from 'npm:@supabase/server@^1' 引入 Supabase 的官方服务端辅助库。这与仓库中 import_map.json 时代的 https://deno.land/x/puppeteer@16.2.0 老写法形成对照——新示例已切换到更贴近 npm 生态的导入方式。
  • 鉴权包装器 withSupabasewithSupabase({ auth: 'user' }, handler) 要求请求必须携带有效的用户级 JWT(而非匿名 key)。这意味着该函数是“受保护端点”,与仓库 config.toml[functions.puppeteer] 段落的 verify_jwt = true 设置一一对应,故其部署命令也不需要 --no-verify-jwt 开关。
  • 远程连接而非本地启动puppeteer.connect({ browserWSEndpoint }) 是理解本示例的关键——它不会在你本地进程中 spawn Chromium,而是连接 Browserless 暴露的 WebSocket 端点。端点为 wss://chrome.browserless.io?token=...,其中 token 从环境变量 PUPPETEER_BROWSERLESS_IO_KEY 读取。
  • 兜底 URLnew URL(req.url).searchParams.get('url') || 'http://www.example.com' 解析查询参数,缺省则截 example.com,便于本地空手验证。
  • 截图与响应page.goto(url) 打开页面后,page.screenshot() 返回 Buffer;随后封装为 Content-Type: image/pngResponse 直接返回,调用方拿到的就是一张 PNG 图片。注意代码未等待 networkidle,极端情况下大页面可能截到未完全渲染的画面,可按需在 goto 中追加 { waitUntil: 'networkidle2' }
  • 错误处理try/catch 捕获失败路径,console.error 落日志,并向客户端返回 { error } 与 HTTP 500。

环境变量:密钥配置与命名坑位

supabase/.env.local.example 可以看到本示例预留的变量条目:

# puppeteer
PUPPETEER_BROWSERLESS_IO_TOKEN=

而源码中读取的是 PUPPETEER_BROWSERLESS_IO_KEY(见 index.ts)。两者后缀不一致(TOKEN vs KEY)是本仓库内实际存在的细节差异:模板文件写的变量名与运行时 Deno.env.get 读取的变量名并不完全相同。实操时应以运行时代码为准,在 .env.local 与线上 secrets 中设置 PUPPETEER_BROWSERLESS_IO_KEY=<你在 browserless.io 获取的 token>,同时可保留模板中的另一行以免困惑。密钥来源在代码注释与官方 README 中均有提示——访问 browserless.io 注册后可获得免费 API token。

本地开发:serve 一条命令跑起来

官方 README 给出的本地启动命令:

supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt

各参数含义:

  • supabase functions serve:在本地以监听模式启动 Edge Functions 开发服务器;
  • --env-file ./supabase/.env.local:把本地密钥文件注入运行环境,PUPPETEER_BROWSERLESS_IO_KEY 由此生效;
  • --no-verify-jwt:本地调试时跳过 JWT 校验,可直接用浏览器或 curl 触发。

启动后导航到本地端点即可验证:

http://localhost:54321/functions/v1/puppeteer

默认行为是给 example.com 截图;若想验证任意网页,追加查询参数即可,例如:

# 对指定网页截图
curl -o page.png "http://localhost:54321/functions/v1/puppeteer?url=https://supabase.com"

仓库在 examples/edge-functions/README.md 中补充了更完整的本地开发前置步骤,可一并参考:先执行 supabase start(确保 Docker 守护进程运行),再 cp ./supabase/.env.local.example ./supabase/.env.local 生成本地密钥文件并按需填值,最后才启动 supabase functions serve

部署到云端:鉴权保持开启

与本地调试不同,正式部署时不需要 --no-verify-jwt,因为该函数本就要求用户级鉴权:

supabase functions deploy puppeteer --no-verify-jwt

——注意这是官方 README 给出的示例命令,实际使用时请根据你的鉴权策略二选一:

  1. 保持 verify_jwt = true(推荐,与 config.toml 一致):直接执行 supabase functions deploy puppeteer,函数入口依赖 Supabase Auth 的 JWT,前端可用 supabase-js 客户端的 .functions.invoke('puppeteer') 携带用户会话调用;
  2. 放开鉴权做公开服务:可去掉 config.tomlverify_jwttrue 值或临时加 --no-verify-jwt 部署,但需自行评估被刷量/滥用风险——截图接口会消耗 Browserless 配额,公开暴露前务必三思。

部署前的标准工序(来自仓库顶层 examples/edge-functions/README.md):

supabase login                                    # 登录 CLI(Dashboard 生成 Access Token)
supabase link --project-ref your-project-ref      # 关联远端项目
supabase secrets set --env-file ./supabase/.env.local   # 同步密钥到生产
supabase secrets list                             # 校验密钥是否写入
supabase functions deploy puppeteer               # 部署函数

其中 supabase secrets set 是把本地 .env.local 中的 PUPPETEER_BROWSERLESS_IO_KEY 推送到云端 Secrets 的关键步骤;生产环境建议单独维护一份生产密钥文件,与本地密钥隔离。

调用结果与同类函数横向参照

生产部署并完成一次调用后,得到的响应体即为 image/png 截图,可直接以图片形式展示或二次落库。若需把截图存进 Supabase Storage,可参考仓库内 og-image-with-storage-cdntweet-to-image 两个同族示例,它们展示了“渲染 → 上传 Bucket → CDN 分发”的完整链路,可作为本示例从“返回二进制”升级为“返回公网图片 URL”的直接参考。另需注意,示例目录中还存在基于 og_edge / satoriopengraph 方案,其不依赖完整浏览器、冷启动更快——当你的目标是生成固定模板的社交媒体卡片而非通用网页截图时,后者是更轻量的替代路线。

常见问题与边界提醒

  • 截图超时或空白:Puppeteer connect 走 WebSocket 远程浏览器,若目标站点过慢,建议为 page.goto 配置 waitUntil 与超时,并将函数超时阈值同步调大。
  • 密钥命名不一致:以 .env.local.examplePUPPETEER_BROWSERLESS_IO_TOKEN 为模板建文件后,务必补充运行时代码真正读取的 PUPPETEER_BROWSERLESS_IO_KEY(后缀为 KEY),否则会得到 500 与 token 缺失错误。
  • 配额与费用:Browserless 免费额度有限,截图请求消耗对方服务资源,公开暴露的接口可能被恶意遍历消耗配额。
  • 鉴权必须配合:结合 config.tomlverify_jwt = true,生产调用建议通过 supabase-js 的 invoke 携带用户令牌,而不是裸奔的匿名请求。

小结

这个 30 行的示例完整展示了一条高价值模式:Deno Edge Function 不自己开浏览器,而是通过 puppeteer.connect 把渲染任务交给 Browserless 托管浏览器,再把 PNG 截图以流式响应回传。从源码(index.ts)到密钥模板(.env.local.example)、从 config.toml 鉴权到 CLI 部署命令,仓库给出了可直接复制运行的完整闭环。若你的业务需要“网页快照、链接预览、定时巡检页面渲染结果”,照此模式即可在 Supabase 体系中快速产出自己的截图服务。

登录后查看全文
热门项目推荐
相关项目推荐