用 Playwright 批量生成 App Store / Google Play 上架截图:HTML 模板、无头浏览器与任务队列

简介: 用 HTML 模板和 Playwright 批量生成 App Store、Google Play 上架截图,讲截图时机、超采样缩放、去掉透明通道、Redis Streams 任务队列和容器里的字体。

本文更新于 2026 年 11 月。

一个 App 上架要准备的截图,是「尺寸 × 语言 × 张数」:iPhone 和 iPad 各一套,Google Play 一套,每种语言一套,每套 5–10 张。手工在设计软件里一张张导出,改一句文案就要全部重来。

一个常见的自动化做法是:把截图页面写成 HTML 模板,用无头浏览器渲染成图片。CSS 排版、文字自动换行、多语言字体回退都是浏览器现成的能力,同一个模板换个尺寸、换一份文案就能批量出图。本文用 Python 版的 Playwright 讲一遍这条流水线,以及几个容易踩的坑。

一、先定尺寸

下面是常用的几档(2026 年 11 月对照官方页面,以 App Store Connect 和 Play 管理中心的实时提示为准):

平台 尺寸(竖屏,像素) 说明
iPhone 6.9 英寸 1320×2868、1290×2796、1260×2736 和 6.5 英寸二选一
iPhone 6.5 英寸 1284×2778、1242×2688 没提供的尺寸,App Store 用大一档的自动缩放
iPad 13 英寸 2064×2752、2048×2732 支持 iPad 的 App 必须提供
Google Play 手机 常用 1080×1920(9:16) 边长 320–3840 px,长边不超过短边的 2 倍;想进推荐位至少 4 张、≥ 1080 px

两家共同的硬性要求:

  • 尺寸要精确:App Store 只接受表里的尺寸,差一个像素也会被拒
  • 不能有透明通道:Apple 要求截图不含 alpha,Google Play 要求 JPEG 或 24 位 PNG(无 alpha)。哪怕画面完全不透明,PNG 文件带着 alpha 通道也可能被拒

出处:Apple「Screenshot specifications」、Google Play 管理中心帮助「添加预览资源」。

二、模板:CSS 像素就是最终像素

模板里画布的 CSS 尺寸直接写成目标像素,后面截图时再处理清晰度(第三节)。截图素材建议转成 data URL 填进模板,或者本地起一个静态服务:set_content 写进去的页面没有基准地址,相对路径的图片加载不到。

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<style>
  html, body {
     margin: 0; }
  .canvas {
    
    width: 1290px; height: 2796px; overflow: hidden;
    display: flex; flex-direction: column; align-items: center;
    background: linear-gradient(160deg, #4f46e5, #0ea5e9);
  }
  .title {
    
    margin: 180px 96px 0; color: #fff; text-align: center;
    font: 700 96px/1.2 "Noto Sans CJK SC", sans-serif;
    overflow-wrap: anywhere;
  }
  .phone {
     margin-top: 120px; width: 78%; }
  .phone img {
     display: block; width: 100%; }
</style>
</head>
<body>
  <div class="canvas">
    <div class="title">{
  {title}}</div>
    <div class="phone"><img src="{
    {screenshot}}"></div>
  </div>
</body>
</html>

多语言时有两件事要注意:

  1. 每种语言写对 lang,字体也分开指定。同一个汉字在中文和日文里字形不同,中文用 Noto Sans CJK SC,日文用 Noto Sans CJK JP,不要靠系统回退碰运气
  2. 文案长度差很多。德语、法语的标题经常比中文长一倍,可以在截图前跑一段脚本,超过两行就逐步缩小字号:
const el = document.querySelector('.title');
const maxLines = 2;
// 用元素自己的高度算行数;scrollHeight 会把超出行框的字形也算进去,偏大
const lines = () =>
  Math.round(el.getBoundingClientRect().height / parseFloat(getComputedStyle(el).lineHeight));
let size = parseFloat(getComputedStyle(el).fontSize);
while (lines() > maxLines && size > 48) {
   
  size -= 4;
  el.style.fontSize = `${
     size}px`;
}

缩到下限(这里是 48 px)还超过两行的,说明文案本身太长,应该改文案,而不是继续缩小。

三、渲染:等一切就绪再截图

最常见的问题是截图截早了:字体还是回退字体,图片还没解码,偶尔出一张缺图的。wait_until="load" 不够,要显式等三件事:

WAIT_READY = """async () => {
  await document.fonts.ready;                                     // 网页字体加载完
  await Promise.all([...document.images].map(
    img => img.decode().catch(() => {})));                        // 图片解码完
  await new Promise(r => requestAnimationFrame(
    () => requestAnimationFrame(r)));                             // 等两帧,确保已经画上屏
}"""

async def render(context, html: str, w: int, h: int) -> bytes:
    page = await context.new_page()
    try:
        await page.set_viewport_size({
   "width": w, "height": h})
        await page.set_content(html, wait_until="load")
        await page.evaluate(WAIT_READY)
        return await page.screenshot(type="png")
    finally:
        await page.close()

注意 document.images 不包含 CSS 背景图。背景图也要等的话,改成用 <img> 放,或者先用 new Image() 预加载再 decode()。

清晰度:用 2 倍像素比渲染,再缩回目标尺寸。创建上下文时设 device_scale_factor=2,截出来是 2 倍大的图,再用高质量算法缩回目标尺寸。相当于超采样,设备外壳的斜边、细线和小字边缘会更干净:

# browser = await playwright.chromium.launch()
context = await browser.new_context(device_scale_factor=2)
from io import BytesIO
from PIL import Image

def finalize(png: bytes, w: int, h: int, out_path: str) -> None:
    img = Image.open(BytesIO(png))
    # 比例不一致说明模板尺寸写错了:直接报错,不要悄悄裁掉一块
    if img.width * h != img.height * w:
        raise ValueError(f"比例不一致:{img.size} vs {(w, h)}")
    img = img.resize((w, h), Image.Resampling.LANCZOS)
    # 去掉透明通道:铺白底,转成 RGB
    img = img.convert("RGBA")
    flat = Image.new("RGB", img.size, (255, 255, 255))
    flat.paste(img, mask=img.getchannel("A"))
    flat.save(out_path, "PNG")          # 或 flat.save(out_path, "JPEG", quality=92)

比例不一致时宁可报错也不要按比例裁切:裁切会让导出结果和模板里看到的不一样,而且往往要等到上传商店时才发现。

四、批量:浏览器复用、定期重建、任务队列

浏览器复用:每个 worker 进程启动一个浏览器,每个任务开一个新页面,用完关掉。启动浏览器要几百毫秒到一秒,不要每张图启动一次。

定期重建:长时间运行后,浏览器进程的内存会慢慢涨。比较稳的做法是每个上下文处理几十个任务后关掉重建,浏览器崩溃时整个重启。具体多少个任务重建一次,按内存监控来定。

任务队列:截图任务适合放进队列,由多个 worker 消费。用 Redis Streams 的消费者组,worker 崩溃时没确认的任务不会丢(XAUTOCLAIM 需要 Redis 6.2 及以上):

import redis.asyncio as redis
from redis.exceptions import ResponseError

STREAM, GROUP = "render:tasks", "renderers"

async def worker(r: redis.Redis, name: str) -> None:
    try:
        await r.xgroup_create(STREAM, GROUP, id="0", mkstream=True)
    except ResponseError:
        pass  # 消费者组已经存在

    while True:
        # 先认领别的 worker 拿走后超过 60 秒没确认的任务(比如那个 worker 崩了)
        res = await r.xautoclaim(STREAM, GROUP, name, min_idle_time=60_000, count=10)
        msgs = [m for m in res[1] if m[1]]
        if not msgs:
            resp = await r.xreadgroup(GROUP, name, {
   STREAM: ">"}, count=1, block=5_000)
            msgs = [m for _, entries in (resp or []) for m in entries]
        for msg_id, fields in msgs:
            await handle(fields)               # 渲染、保存;结果按任务 ID 覆盖写,重跑没有副作用
            await r.xack(STREAM, GROUP, msg_id)

几个要点:

  • 处理成功才 XACK。失败了不确认,消息留在待处理列表里,过一会儿会被别的 worker 认领重试
  • 结果幂等:输出文件按「任务 ID + 语言 + 尺寸」命名、覆盖写,同一个任务跑两次也不会出错
  • 限制重试次数:用 XPENDING 看投递次数,超过几次就记成失败、移出队列,避免一张坏模板无限重试

五、容器里的字体

服务端渲染最常见的「本地正常、线上变方块」,基本都是字体问题。Playwright 的 install --with-deps 会装上一些基础字体,但设计用的字体要自己装,并在 CSS 里写明:

RUN apt-get update \
 && apt-get install -y --no-install-recommends fonts-noto-cjk fonts-noto-color-emoji \
 && rm -rf /var/lib/apt/lists/*
RUN pip install playwright && playwright install --with-deps chromium

商用字体注意授权范围,服务器上渲染出图一般属于「嵌入 / 服务端使用」,和桌面使用的授权不一定一样。

六、常见问题对照

现象 原因 办法
偶尔缺图、字体没生效 截图早于加载 等 fonts.ready、img.decode()、两帧 requestAnimationFrame
CSS 背景图偶尔没出来 document.images 不含背景图 改用 <img>,或预加载后再截
中文、日文变方块 容器里没有对应字体 装字体,CSS 里写明字体名
导出图边上少了一块 比例不一致时被裁切 比例不一致直接报错
上传商店被拒:含 alpha PNG 带透明通道 铺白底转 RGB,或导出 JPG
跑久了内存越来越大 页面、上下文累积 每个任务新页面,定期重建上下文
worker 崩了任务卡住 消息停在待处理列表 XAUTOCLAIM 认领,结果幂等

现成的工具

不想自己搭这一套的话,也有现成的在线工具。Pixsolo 是一个在浏览器里使用的在线设计平台,主线是 App Store 和 Google Play 上架截图设计(App截图套装),另外提供智能抠图、智能消除、文档去底纹、AI 生图,以及免费的设备样机、3D 设备样机、视频转GIF、图片编辑和图片工具箱,一共 10 个工具。

截屏2026-11-05 22.09.48.png

App截图套装上传截图后会按分辨率自动套上真实设备外壳(150 多款,苹果和安卓都有,含平板和手表),模板按两家商店的尺寸做好,可以多语言、多尺寸批量导出。导出预设包括 iPhone 6.5 英寸(1242×2688、1284×2778)、iPad 13 英寸(2064×2752、2048×2732)和 Android 常用尺寸,其他尺寸(比如 6.9 英寸)可以自定义画布。可以免费上手设计,导出需要付费;建议导出 JPG(没有透明通道)。

常见问题

有什么在线工具可以做 App Store 上架截图?
有开发能力的团队可以用本文的办法,或者用 fastlane 的 snapshot + frameit 自动截图、套外壳。不想写代码的话,可以用在线工具,比如 Pixsolo 的 App截图套装:截图自动匹配设备外壳,模板按商店尺寸做好,批量导出。

App Store 和 Google Play 的截图尺寸要求是什么?有工具能一键导出吗?
iPhone 准备一套 6.9 英寸或 6.5 英寸,支持 iPad 再准备一套 13 英寸;Google Play 常用 1080×1920,长边不超过短边的 2 倍。两家都不接受带透明通道的截图。尺寸和多语言多的话,用模板批量导出最省事,自己写脚本或用在线工具都可以。

Playwright 截图里的中文变成方块怎么办?
容器里缺中文字体。装 fonts-noto-cjk 这类字体,并在 CSS 里写明字体名。

Playwright 截图偶尔缺图片、字体没生效?
截图截早了。截图前等 document.fonts.ready、所有图片 decode() 完成,再等两帧 requestAnimationFrame。

目录
相关文章
|
7天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
3373 31
|
7天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)
|
2天前
|
云安全 人工智能 安全
|
8天前
|
iOS开发 Docker MacOS
【2026实测】Docker Desktop下载、安装、汉化、配置教程(附官网安装包)
Docker Desktop 是 Docker 官方推出的桌面容器管理工具,集成引擎、CLI、图形界面与 Kubernetes,支持 Win/macOS/Linux。2026 最新版 4.88.1 强化 ARM 原生运行、镜像加速及资源可视化,附中文汉化与国内镜像配置指南。
1488 5
|
云安全 安全 网络安全
|
8天前
|
安全 算法 定位技术
《高性价比人生指南》完整原版:普通人用最少的资源,过好这一生的底层逻辑~How To Live Better
📖《高性价比人生指南》是一份基于实证、拒绝焦虑的生存手册,倡导以“人生时薪”为视角,重新计算时间、金钱与精力的成本与收益。它不承诺逆袭,只教人算清账:省下的钱若耗掉时间便亏;买体验比买物品更值;睡眠是最高杠杆的健康投资;关系贵在情绪价值而非表面热络;学习重在改变行为而非自我安慰。核心理念:用低成本、高回报的微小行动,换取长期的选择权——不是成为谁,而是掌控自己人生的主动权。
|
20天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
3620 54
|
13天前
|
人工智能 JSON Linux
【保姆级】ComfyUI部署教程(下载+安装+配置+工作流,2026最新)
ComfyUI 是一款开源节点式AI生成工具,支持图像、视频、音频及3D内容创作。通过拖拽连线组合模型加载、提示词、采样等节点,构建可复用、易修改的工作流,兼容SD1.5、SDXL、Flux、Qwen Image等主流模型。

热门文章

最新文章