AX 爱鲜报

综合 / 开发工具 · 2026-10-24 10:45 · 4 阅读 · 0 赞

用 Playwright + 多 Agent 流水线一键还原任意网站

介绍 ai-website-cloner-template 的工程化思路:用 Playwright 抓取多视口截图、计算样式与交互状态,再通过规约驱动多个 Agent 并行生成组件,最后做视觉回归 QA,实测掘金首页还原度约 70% 以上。

一、前言

本文介绍一个能把精美网站一键复制的工程模板——ai-website-cloner-template 的用法与实现原理。

读完你能收获:

  • AI 自动逆向解析网页的新思路;
  • Playwright 的基本用法;
  • ai-website-cloner-template 的整体设计。

用法很简单。第一步,把 GitHub 上的模板拉到本地:

git clone https://github.com/JCodesMore/ai-website-cloner-template.git

第二步,运行 Claude Code,执行 /clone-website(本质上是模板工程里的一个 skill):

3dfdca83a7eb81576305b5fc2833fe5d.png

以掘金(https://juejin.cn)为例,先看效果。

PC 端对比(左:生成 / 右:原图):

bcf4f44235c451291204bf95bb67a957.png

移动端对比(左:生成 / 右:原图):

cd1b1a44d37879542b551e1cec909794.png

多端兼容场景:

2026-07-048.14.44-ezgif.com-video-to-gif-converter.gif

整体评估:

  • UI 还原度实测在 70% 以上,尚未达到完美;
  • 移动端适配等细节处理得比较好;
  • 生成代码的可维护性尚可,但质量很大程度上取决于所用模型。

下面看内部是怎么处理的。

二、核心实现

2.1 核心原理

138c7c494a76e62e4047bcf0308ab52a.png

整个流程分为四个阶段。

Phase 1:摸底(Reconnaissance)

拿到目标 URL 后,先搞清楚它长什么样、怎么动。

  • 截图:用 Playwright 启动真实浏览器,在三个标准视口(1440×900、768×1024、390×844)下分别截全页图和首屏图。用三个视口是为了验证响应式断点,而不是靠猜。
  • 抓 design tokens:在浏览器里执行 JS,遍历所有 DOM 节点,用 getComputedStyle() 聚合去重字体、字号、颜色、背景、圆角、间距。读的是最终生效的计算值,因此能穿透 @apply、CSS 变量和媒体查询。
  • 交互扫描:静态截图只能看到当前状态,但 tab 切换会换内容、滚动可能改变 header 样式、hover 可能让卡片变色。所以要模拟滚动、点击 tab、悬停,把每个交互态都抓一份。
  • 产出:一批截图和一批 JSON(tokens.json、assets.json、topology.json 等),放在 docs/research/ 和 docs/design-references/ 下。

05b16ab2b73f8287c4b3a9cdf0498ce8.png

c2d06a90278674467aabd50efeaf39a3.png

215ca30db1f8b3a7d8a605ac1caf930b.png

Phase 2:搭骨架(Foundation)

摸底完成后先铺地基,后续组件才能开工。

  • design tokens 落地:把抓到的颜色、字号映射到 Tailwind v4 的 @theme 块和 :root CSS 变量,之后写组件时 bg-primary、text-muted-foreground 等 class 可直接使用。
  • 字体策略:目标站用 Google Fonts 就走 next/font/google;像掘金这种纯系统字体(PingFang SC、Microsoft YaHei)则退回 system font stack,省掉外网请求。
  • 图标去重:页面里可能有三四十个 SVG 节点,很多是重复的。脚本去重后通常只剩十几个,整理成 React 组件放进 icons.tsx。
  • 资源下载:把图片、logo、favicon 批量下载到 public/images/ 和 public/seo/,按用途分目录。下载不了的占位图用基于 id 的 hash 渐变色块顶替,保证每个 id 颜色唯一、视觉上合理。
  • 产出:globals.css、icons.tsx、public/images/ 全部就绪。

Phase 3:组件生成(Spec & Build)

这是流水线最关键的阶段,也最能体现“规约驱动”的思想。

  • 写 spec 文件:先不写代码,而是为每个 section 写一份 markdown 规约(*.spec.md),作为 builder agent 的施工合同,内容包括:
    1. 目标组件长什么样(截图路径);
    2. 像素级样式(字号 17px、padding 12px 20px 0px、颜色 #252933,全部来自 getComputedStyle 的真实值);
    3. 真实内容数据(标题、作者、缩略图均从目标 DOM 抓取,不是 Lorem Ipsum);
    4. 交互模型(hover、点击、是否有滚动驱动)。
  • 复杂度预算:单份 spec 不超过 150 行。掘金首页 12 个 section 就拆成 12 份 spec,分派给 12 个 builder agent。拆得越细,单个 agent 任务越聚焦,质量越高。
  • worktree 并发:每个 builder agent 在独立的 git worktree 分支上工作,互不冲突。完成后由 orchestrator 逐个 merge 进 main,每次 merge 立刻跑一次 npm run check(lint + typecheck + build),保证 main 分支始终可构建。
  • 产出:每个 section 一个 React 组件文件及其对应的 spec 文件。

121af4ffe5e688769d4854a5fd094b78.png

Phase 4:拼装与 QA(Assembly & QA)

组件写完后拼成完整页面,并验证与目标站的相似度。

  • 拼装:page.tsx 按 z-index 和断点组合所有组件。例如 header 是 sticky top(z-index: 250),移动端浮动下载按钮用 position: fixed(z-index: 100),不能挡住 header 但要能盖住 footer。
  • 视觉 QA:脚本同时截取三个视口下的“目标站”和“克隆站”,生成 target-*.png 与 clone-*.png 并排对比。优先级依次为:
    1. 尺寸差异(宽高、padding、gap,最容易暴露);
    2. 颜色差异(背景、文字、边框,肉眼最敏感);
    3. 布局差异(flex 方向、grid 列数、sticky 是否失效)。

整个过程中,核心是真实获取浏览器里的页面状态,Playwright 在其中起关键作用。

2.2 Playwright 简介

Playwright 是 Microsoft 开源的端到端测试与浏览器自动化库,可以用代码控制真实浏览器(Chrome、Firefox、Safari),自动完成点击、填写、截图、读取页面内容等操作。

在 Node 环境唤起浏览器:

// 1. 启动浏览器
const { chromium } = require('playwright');
const browser = await chromium.launch({ headless: true });

创建独立的隐身窗口:

// 2. 新建一个 context(独立隐身窗口)
const context = await browser.newContext();

借助这些能力可以完成 e2e 等自动化验证:

// 点击
await page.click('button#submit');
// 导航跳转
await page.goto('https://example.com');

也可以截图留存效果:

await page.screenshot({ path: 'screenshot.png' });

下面是常用 API 能力的总结:

1bbe988dbfe40bc21918c37a106a3d51.png

三、具体实现思路

多视口截图

const viewports = [
  { name: "desktop-1440", width: 1440, height: 900 },
  { name: "tablet-768", width: 768, height: 1024 },
  { name: "mobile-390", width: 390, height: 844 },
];

for (const vp of viewports) {
  const ctx = await browser.newContext({
    viewport: { width: vp.width, height: vp.height },
    locale: "zh-CN",
    userAgent: "Mozilla/5.0 ... Chrome/131.0.0.0 ...",
  });
  const page = await ctx.newPage();
  await page.goto(url, { waitUntil: "networkidle", timeout: 60000 });
  await page.waitForTimeout(2500); // 等 hydration
  await page.screenshot({ path: `${outDir}/${vp.name}-full.png`, fullPage: true });
  await ctx.close();
}

三个标准视口各截一张 fullPage 图。注意 waitUntil: "networkidle" 之后还要再等 2.5 秒——掘金用了大量懒加载图片和 IntersectionObserver,networkidle 不等于“完全稳定”。

提取全局样式 tokens

用 page.evaluate() 执行 JS,遍历所有 DOM 节点调用 getComputedStyle():

const tokens = await page.evaluate(() => {
  const all = document.querySelectorAll("*");
  const fonts = new Set(), sizes = new Set(), colors = new Set();
  for (const el of all) {
    const s = getComputedStyle(el);
    fonts.add(s.fontFamily);
    sizes.add(s.fontSize);
    if (s.color !== "rgba(0, 0, 0, 0)") colors.add(s.color);
  }
  return { fonts: [...fonts], sizes: [...sizes].sort(), colors: [...colors] };
});

掘金真实抓出来的 token:

主色蓝:  rgb(30, 128, 255)  →  #1e80ff
页面底:  rgb(242, 243, 245) →  #f2f3f5
卡片底:  rgb(255, 255, 255) →  #ffffff
正文:    rgb(37, 41, 51)    →  #252933
次要文字:  rgb(81, 87, 103)   →  #515767
三级文字:  rgb(134, 144, 156) →  #86909c
边框:    rgb(228, 230, 235) →  #e4e6eb
危险红:  rgb(246, 66, 66)   →  #f64242
字体族:  PingFang SC, Microsoft YaHei, Helvetica Neue

强制交互扫描

这一步是整个 skill 的精华。静态截图只能看到“现在长什么样”,但站点是“活”的:tab 切换会换内容、滚动会让 header 变样式、hover 会让卡片换底色。

以掘金为例做一次完整 sweep:

// 滚动 sweep:截不同 scrollY 下的 header 样式
for (const y of [0, 300, 600, 900, 1200, 1500, 1800, 2094]) {
  await page.evaluate((sy) => window.scrollTo({ top: sy, behavior: "instant" }), y);
  await page.waitForTimeout(400);
  const data = await page.evaluate(() => {
    const header = document.querySelector("header");
    return {
      scrollY: window.scrollY,
      headerBg: header ? getComputedStyle(header).backgroundColor : null,
      headerShadow: header ? getComputedStyle(header).boxShadow : null,
    };
  });
  scrollSnapshots.push(data);
}

// 点击 sweep:点“最新” tab 看内容是否变化
const tabClick = await page.evaluate(() => {
  const target = [...document.querySelectorAll("a, button, div, span")].find(
    (el) => el.textContent?.trim() === "最新" && el.children.length === 0
  );
  if (!target) return { ok: false };
  target.click();
  return { ok: true };
});
await page.waitForTimeout(1500);
const latestContent = await page.evaluate(() => {
  return document.querySelectorAll(".entry").length; // 真实数据:1 张卡
});

实测发现掘金“最新” tab 切换后文章数从 16 降到 1,说明这不是纯 UI 切换,而是真实重新拉数据。克隆时用 useState 切两份静态 mock,生产部署则需要后端 API 配合。

页面拓扑映射

把所有 section 列出来,标注交互模型:

1. Header       — 静态 (无 scroll 变化)
2. LeftSidebar  — 点击 (导航)
3. ArticleList  — 点击 (tab 切换)
4. ArticleCard  — hover (subtle bg)
5. CtaBanner    — 静态
6. RightRail    — 静态 (刷新按钮 mock)
7. Footer       — 静态
8. MobileAppPill — 静态 (position: fixed)

这一步的产出是 PAGE_TOPOLOGY.md,作为后续 spec 文件的索引。

d2413f24dfb89d57ada3b56c89939dbe.png

以上就是 Phase 1。后续阶段会对样式、资源以及组件生成规范 spec 做系统化处理,这里不再展开。最后看 Phase 4 的视觉 Diff。

QA 脚本遍历三个视口,分别截取 target 和 clone:

for (const vp of viewports) {
  const ourCtx = await browser.newContext({ viewport: vp });
  const ourPage = await ourCtx.newPage();
  await ourPage.goto("http://localhost:3010/", { waitUntil: "networkidle" });
  await ourPage.screenshot({ path: `clone-${vp.name}-full.png`, fullPage: true });

  const targetCtx = await browser.newContext({ viewport: vp });
  const targetPage = await targetCtx.newPage();
  await targetPage.goto("https://juejin.cn/", { waitUntil: "networkidle" });
  await targetPage.screenshot({ path: `target-${vp.name}-full.png`, fullPage: true });
}

对比找差异的优先级:

  1. 尺寸差异:宽高、padding、gap(最容易暴露);
  2. 颜色差异:背景、文字、边框(肉眼最敏感);
  3. 布局差异:flex 方向、grid 列数、sticky 失效。

掘金实战中,mobile 一次性暴露了 3 个差异(tag wrap、author truncate、button-in-anchor),desktop 和 tablet 一次通过。

四、总结

ai-website-cloner-template 的价值不在于“能复制网站”——这本身就是 DevTools 的本职工作。它把“截图 + 抄代码”重构为一条可复用、可验证、可并发的工程流水线:先 Reconnaissance 抓计算值,再 Foundation 搭骨架,然后用 Spec & Build 的规约驱动多 Agent 并行生成,最后 Assembly & QA 做多视口视觉回归。先写 spec、再写代码这个顺序的颠倒,是从“AI 玩具”迈向“AI 工程”的分水岭。

建议 clone 一份到本地,挑一个欣赏的站点跑一遍,亲身感受“规约驱动 + 多 Agent 并行”的真实体感。

标签 AI Agent Playwright Claude Code 前端工程化

评论

登录后才可评论

0 条评论

  • 暂无评论,来写第一条吧。