建议试试逆向,用playwright,自动抓取网页,截图拍照。这应该可以抓到可视化页面
我最近在搞把别的网址的所有内容直接复刻的AI玩法,我感觉你这个可以交给AI办就行
刚好最近完成了一个类似的项目,将客户的一个国外版的网站复刻到国内,翻译,并放入 WordPress 架构中,并可以可视化编辑,简单总结一下,具体实现上可能与你实际工作有出入,但过程上应该类似,希望对你有帮助。
我全程主要使用了两个工具,Vibe Coding 用了 GLM 5.3,以及 Flywheel 的 Local,一个 WordPress 插件 - All in One WP Migration
项目说明
这个项目的目标是将一个约 310 页的现代框架(React/Next.js 类)站点逐像素复刻为 WordPress Block Theme,并实现内容本地化,最终打成一个可迁移的全量包。
适用场景
- 源站使用任意技术栈(SSR/CSR/静态),但没有源码。
- 目标是"像素级 1:1":视觉、结构、交互与源站一致,内容做本地化(如中文化)。
- 目标架构是 WordPress(客户要求可自主维护内容)。
- 主要劳动力是 AI,由人来审查与验收。
三条规定到死的原则
- 1:1 = 逐字复刻源站真实 DOM + 真实 CSS 值。不创造、不近似、不自己发明、BEM 命名、不凭印象补内容。源站没有的元素绝不加,源站有的元素绝不漏。
- 数值只来自两个地方:源站渲染后的
index.html(结构与 class)和styles.css(精确值)。截图只能用于最后的视觉对照,不能用于推导数值(截图有压缩、retina 缩放、懒加载截断,测量误差大到误导)。 - AI 报告必须可审计。每个值标注出处(“源站 styles.css 第 N 行:值 X” vs “量截图约 Y”)。禁止把截图测量说成"读了源码"——本项目曾因此浪费大量 token ,并误导验收。
总流程
各阶段可回溯、不可跳序。
Phase 0 环境与地基
→ Phase 1 素材抓取
→ Phase 2 WP 架构
→ Phase 3 设计系统
→ Phase 4 逐页复刻 SOP
→ Phase 5 内容本地化管线
→ Phase 6 验收
→ Phase 7 打包交付
经验教训:本项目最初跳过 Phase 1(直接手写页面 + 对截图猜),导致首页克隆错源页面、产品页臆想布局,最终全部返工。素材抓取是整个项目的地基。
Phase 0:本地环境与项目纪律
用 Local 创建空 WordPress
- 工具:Local(by WP Engine / 原 Flywheel),选空白站点模板,起个本地域名。
- 优点:一键起停 Apache/Nginx + MySQL + PHP,数据库可直接在 Local 的 Shell 里用
wpCLI 操作。 - 坑:Local 站点偶尔起不来——重启 Local 应用即可;CLI 连数据库要用 Local 提供的 socket 路径(
mysql -S <socket>),不是默认 TCP。 - WordPress 主题选 Block Theme(
theme.json+ templates + parts),这是后面一切的前提。
git 范围约定(重要)
只把自己写的代码入库,第三方与环境不入库:
wp-content/
├── themes/<my-theme>/ # 主题:theme.json / templates / parts / assets
└── plugins/<my-core>/ # CPT + 分类法 + 表单 + SEO
└── plugins/<my-blocks>/ # 自建区块(render.php + style.css + view.js)
不入库:
- 抓取素材目录(
_pages/、_assets/,本项目约 830MB) - Local 环境配置
- 第三方插件(迁移时另装)。
.gitignore一开始就写好,否则素材会淹没 diff。
进度文件三件套
| 文件 | 角色 | 规则 |
|---|---|---|
| dev_plan.md | 计划(要做什么) | 不记状态 |
| PROGRESS.md | 状态(做到哪) | 改代码的同一个 commit 必须更新,不攒批 |
| AGENTS.md | AI 行为宪法(见 §9) | 每次会话开工前 AI 必读 |
PROGRESS.md 里维护已知问题登记表(编号 + 位置 + 影响 + 状态)和关键决策记录(日期 + 决策 + 理由)。误报的结论不要删除,标注"已撤回"留痕——防止重查一遍。
Phase 1:素材抓取(项目地基)
为什么必须"渲染后抓取"
源站若是 React/Vue 类应用,curl / 常规爬虫拿到的是水合前的空壳(骨架 HTML 或错误桩)。本项目实测:旧抓取里首页 hero 文本出现 0 次、图片列表全空、公共 CSS/JS 全部 429——只有截图是真的,结构化数据全是桩。拿桩数据建页面,再用自己渲染的破页面"验收",永远对不上。
自写 Playwright 爬虫
单文件爬虫(tools/crawl.py,约 200 行),每页产出:
_pages/<分类>/<页面>/
├── index.html # 渲染后完整 DOM —— 结构与 class 的唯一依据
├── styles.css # 计算后/抓取的完整 CSS —— 数值的唯一依据
├── content.json # 结构化提取:标题/段落/链接/图片/视频/meta
├── assets-extracted.json # 所有 img/video/background-image 真实 URL
├── desktop-fullpage.png # 桌面全页截图 —— 视觉验收目标
└── mobile-fullpage.png # 移动全页截图 —— 响应式验收目标
爬虫要点:
- 真实 Chromium + 桌面 UA;
- 等水合完成:以"页面正文特征文本出现"为信号(
page.wait_for_function检查若干 hint 词),再加networkidle; - 滚动到底触发全部懒加载,再 dump DOM 与图片/视频真实 URL(
currentSrc,不是data-src); - 一次导航出双截图:
set_viewport_size切桌面/移动,无需二次导航; - CSS 用"抓取源站样式表"或对关键元素计算样式落盘,总之每页目录里必须有
styles.css。
抓取纪律(两条血的教训)
- 分批抓,不要一次抓全站。一次 300+ 页会压垮源站/触发反爬限流(本项目被 429 过)。按模板分组、每批 3 页左右,建完模板再铺该组其余页。
- 抓完立刻校验素材完整性:
index.html里能否 grep 到正文特征词、content.json图片数是否 >0、styles.css是否存在。缺styles.css就动手复刻 = 盲干(本项目首页因此返工过,后来把它写成了 SOP 的强制步骤 0)。
页面清单 manifest
先建 _pages 目录 → 目标站 URL 的映射表(build-page-manifest.py),全站每个目标页都指向一个抓取源目录。没有映射的页面不许开工——本项目曾因没有 manifest,把首页错克隆成了另一个落地页,两次重做后才查明。
Phase 2:WordPress 架构
结构选型:Block Theme + 自建区块 + CPT
wp-content/
├── themes/my-theme/
│ ├── theme.json # 设计 token(颜色/字号/间距 → CSS 变量)
│ ├── templates/ # front-page.html / single-<cpt>.html / archive-*.html
│ ├── parts/ # header.html / footer.html
│ ├── assets/css/source.css # Tailwind v4 源(@theme + @source)→ npm run build 出 compiled.css
│ └── functions.php
└── plugins/my-blocks/
└── blocks/<name>/{block.json, render.php, style.css, view.js}
关键决策与理由:
| 决策 | 理由 |
|---|---|
| 区块无 JS 构建步骤,render.php 服务端渲染 + 手写 style.css | 1:1 复刻要的是输出确定性的 HTML;跳过 webpack/@wordpress/scripts 整条链,AI 改完即生效,可审计 |
| 内容页走 CPT + 区块化 post_content(single-.html 只有 header/内容/footer) | 同模板的 100+ 详情页共用一个模板,内容差异全部在 DB 里,种子脚本灌入 |
| CSS 走 Tailwind v4 @theme token 路线 | 源站本身是原子化 CSS(utility class);1:1 的最短路径是把它的 token 搬进 @theme,而不是用传统手写 BEM 重写一遍 |
| 种子脚本放 tools/,必须幂等(upsert by slug) | 非幂等 seed 曾造成约 20 个重复页面 |
| 全本地资源(字体自托管;仅字体 CDN 可例外) | 交付独立站,运行时不依赖源站 |
转写内容的编译陷阱(本项目最深的一个坑)
Tailwind v4 靠 @source 扫描源文件生成 utility。但转写进 DB 的 post_content 不在文件系统里,@source 扫不到——于是 col-[1/span_4]、h-[110vh] 这类任意值 class 全部不编译,复杂网格静默塌缩,且毫无报错。
解法
写 tools/dump-content.php,把全站 post_content dump 成一个 all-content.html 放进 assets/css-source/,让 @source 扫到。任何内容变更后必须重跑 dump + npm run build,否则新 class 不生效。这条要写进 SOP,AI 极易忘记。
Phase 3:设计系统对齐
- 从源站
styles.css提取设计 token(间距--spacing-*、字号--text-*、颜色、断点),逐值 对进theme.json与 Tailwind@theme; - token 有多变体时(如
navvsnav-lg),读 token 定义 + 看渲染上下文再选,不盲抄 DOM class(源站因有公告横幅用大值,本地无横幅该用小值); - 布局原语(如
page-margin内容沟槽、gap-y-*垂直节奏、满宽出血段)要识别出来并全局统一——同一页所有内容元素共享同一父容器语义,漏掉包裹容器(如page-margin+ 统一 gap 的 flex 列)就会用各自 margin 代替,间距全错。
Phase 4:逐页复刻 SOP(核心循环)
每页/每元素严格按序执行,跳步 = 违规:
步骤 0 确认素材齐:ls _pages/<分类>/<页面>/(index.html + styles.css 必须都在)
缺则补抓:python3 tools/crawl.py <url> <outdir>
步骤 1 读 index.html:结构 + class + 真实文案(含父容器!)
步骤 2 读 styles.css:grep token 定义 + 应用规则,拿精确数值
步骤 3 读 content.json:文案与链接
步骤 4 逐字转写进 WP(模板/区块/DB),中文化只译不增不改不删
步骤 5 编译:改了内容 → php tools/dump-content.php && npm run build
步骤 6 验收(见 §8)
步骤 7 诚实报告:每个值标注出处
要点:
- 步骤 1 的"父容器"是隐形大坑:只抄单个元素而漏掉包裹它的容器 class(page-margin / gap / grid),元素自身全对但整体间距错位。
- 步骤 4 禁止 bespoke BEM:一切 class 用源站真实 class。AI 的本能是"语义化重写",必须用规则摁住。
- 多页面共用模板时按"模板组"推进:同模板抓 3 个代表页 → 建模板 → 组内其余页纯内容灌入。310 页的项目因此才可行。
Phase 6:验收体系(确定性,不靠眼睛)
数值级测量
Playwright(脚本化,不是肉眼)在用户真实视口(本项目:3302 宽屏 + 1440 + 移动 390)逐元素测 x / width / line-height / 颜色,与源站抓取值/截图对照。输出表格,不做"看起来差不多"的结论。
左边距一致性检查(每页必做)
量该页每个 section 首个内容元素(标题/卡片/正文)的 x,打印成表。所有非例外元素必须同一 x(桌面如 48px、移动 24px)。出现 x=72 / x=0 / x=96 就是布局病——常见根因:容器 padding 叠加、缺 page-margin、双层 padding。这是"间距看起来乱"的根治手段。
class 集合 diff(替代计数审计)
抽取源站 index.html 主内容区与本地渲染结果的 class 集合做差集(过滤 data-*/wp-*/框架私有前缀噪声)。差集非空 → 逐个查证(真删了 vs 语法等价转换)。
为什么禁用计数审计(“两边都是 5 个 h2、3 个 article”):数量对 ≠ 1:1。本项目实测:某页 21 个按钮 class 缺失,计数审计全绿,class diff 一眼暴露。配套写 tools/style-diff.py 做样式规则级 diff。
宽屏陷阱(易漏整层布局)
若源站存在超宽断点机制(如 ≥1728px 时 body 限宽居中、个别元素显式满宽出血),在 1440 视口下这些规则不生效——1440 验收全绿、3302 全崩。规则:宽屏验收必须实时打开源站同视口并排实测,禁止拿窄视口抓取值外推。
视觉验收的定位
截图(抓取时留存的 desktop/mobile fullpage)只用于数值对齐后的最终对照。并且视觉类 AI 分析工具会误报(把正确的东西标成错的),所以只信脚本测量。
AI 协作:提示词与行为约束
这是本项目区别于传统开发的部分:主力是 AI,人的工作是立规矩、验收、纠偏。
AGENTS.md = 持久提示词(宪法)
把规则写进仓库根的 AGENTS.md(各家 coding agent 都会自动读取),每会话开工前要求 AI 完整读一遍。内容层次:
- 铁律(§1 的三条,放最顶上,加粗);
- 达标标准:选一个已验收达标的元素(本项目:首页 hero)作全站基准,写明"每页须达到同样保真度"+ 判定必要条件清单;
- Workflow(§7 的 SOP,注明"跳步 = 违规");
- 已踩坑清单(§10,持续追加,每坑写:现象/根因/解法/如何预防);
- 项目结构与数据流速查。
AGENTS.md 要控制体积(稳定规矩放里面,流水状态放 PROGRESS.md / handoff 笔记),但坑清单必须放 AGENTS.md——它是防止 AI(以及换了模型的下一个会话)重蹈覆辙的唯一机制。
会话级提示词模板
开局(新会话):
开工前完整读 AGENTS.md。本次任务:复刻
<页面>。按 Workflow 步骤 0 开始,先确认_pages/<分类>/<页面>/素材齐全。每个数值标注出处(源站 styles.css 行号 or 截图测量),禁止混用。
验收:
用 Playwright 脚本在 3302 和 1440 视口测
<页面>每个 section 首个内容元素的 x/width,打印成表;再做源站与本地主内容 class 集合 diff。只报数据,不做"基本一致"的结论。
纠偏(发现 AI 编造时):
你说"读了源站 CSS",但给出的值在
_pages/.../styles.css里 grep 不到。重新从该文件第 N 行取值。以后"读 CSS"这个词只允许指 grep 该文件。
9.3 人类验收纪律
- 用户报的问题是"一类"不是"一个":收到单个问题先全站扫同类,批量修;
- AI 声称完成 ≠ 完成,要求可复现的证据(测量表、diff 输出、curl 页尾完整性检查);
- 重大纠偏后把教训立刻写进 AGENTS.md 坑清单,否则下个会话必再犯。
特别注意:反模式与坑清单(按主题归组)
抓取与素材
| # | 坑 | 后果 | 预防 |
|---|---|---|---|
| 1 | 拿水合前的空壳 HTML 当真实 DOM | 页面数据从源头裂成两半,永远对不上 | 渲染后爬虫(§4.2),抓后 grep 特征词校验 |
| 2 | 一次抓全站 | 触发限流 429、素材半残 | 分批,按模板组 |
| 3 | 页面目录缺 styles.css 没发现就开工 | 盲干,数值全靠猜 | SOP 步骤 0 强制 ls 检查 |
| 4 | content.json 有多种 schema,转换器只解一种 | 静默得到 0 段落,页面空白正文 | 转换器兼容两种结构 + 空结果报警 |
| 5 | 抓到的错误页(404/500 桩)进了内容库 | 错误文案被发布 | 入库前过滤标题特征 |
复刻方法
| # | 坑 | 后果 | 预防 |
|---|---|---|---|
| 6 | 用自创 BEM "语义化重写"源站 class | 与源站结构系统性偏离,无法 diff | 禁 bespoke BEM,逐字转写真实 class |
| 7 | 凭印象/截图猜布局 | 编造出源站不存在的元素 | 每个元素都能在源站 index.html 里指出处 |
| 8 | 只抄元素漏父容器(page-margin/gap 包裹层) | 元素全对、整体间距全错 | 转写时带完整祖先链 |
| 9 | 把截图测量说成"读了 CSS" | 误导验收,token 大量浪费 | 诚实报告纪律(§1 铁律 3) |
| 10 | 断点变体盲抄(nav vs nav-lg) | 导航高度错误 | 读 token 定义 + 渲染上下文 |
| 11 | 计数审计(元素个数对)当 1:1 证明 | class 级缺失全绿 | class 集合 diff(§8.3) |
| 12 | 窄视口验收外推宽屏 | ≥1728 整层布局差异漏掉 | 宽屏实时并排实测(§8.4) |
WordPress / CSS 特有
| # | 坑 | 后果 | 预防/解法 |
|---|---|---|---|
| 13 | @layer 冲突:WP 注入与遗留手写 CSS 未分层,优先级高于 @layer utilities 内的 utility(与 specificity 无关) | .text-black 等原子类静默失效 | 遗留 CSS 包进 @layer legacy 降级;末尾留未分层兜底块 |
| 14 | 元素级直接规则压继承:theme.json styles.elements.heading.color 生成 h1..h6{color:#fff},直接命中元素 > 祖先 utility 继承 | 彩色卡片里的标题永远是白字 | theme.json 不设 heading 颜色,标题走继承 |
| 15 | WP contentSize 卡住 header 内层 | 导航不贴两侧 | 对应容器 max-width:none |
| 16 | post_content 在 DB,Tailwind @source 扫不到 | 任意值 class 不编译、网格塌缩、无报错 | dump-content 进扫描目录 + 内容变更后重跑(§5.2) |
| 17 | wp_update_post 走 kses 过滤 | block 标记/中文引号被静默剥掉 | seed 脚本先 remove_filter(‘content_save_pre’,‘wp_filter_post_kses’),或直写 SQL |
| 18 | shortcode 输出被 wpautop 包 | 结构被污染 | 输出前后做 trim/占位处理 |
| 19 | seed 脚本非幂等 | 同页 ×3 重复 | upsert by slug,跑前查重 |
| 20 | 改 PHP 后只看渲染是否 200 | 页面半途截断(fatal 吞在页中),元素检查照样过 | 改 PHP 必 curl 页面验尾部标记闭合 |
| 21 | 本地化排版覆盖层(如 CJK 行高/字距 !important 全局覆盖) | 把源站真实排版值全部打掉 | 不做全局语言覆盖,按源站值 + 换本地字体 |
资源与部署
| # | 坑 | 后果 | 预防/解法 |
|---|---|---|---|
| 22 | 源站 CDN 反爬 429,curl/Playwright 下载大媒体全失败 | 视频/大图缺件 | 浏览器内 fetch(location.href) 读 body 转 base64 带出(同源凭证请求不走下载限流) |
| 23 | 图片懒加载未触发就 dump | 图片 URL 缺失 | 爬虫滚动到底再抽取 |
| 24 | 站内残留外链(JSON-LD image 指源站 CDN) | 运行时依赖源站 | 全库扫外链域名清单清零 |
| 25 | 迁移包不含引擎静态资源/媒体库 | 目标服务器恢复残缺 | 选全量迁移方案(如 All-in-One WP Migration),打包前逐项核验:引擎 chunks、uploads、DB 内 URL 全相对化 |
11. 内容本地化管线
- 机翻走 API 批处理(如 Claude API):脚本幂等 + 断点续跑 + 块感知(识别 HTML 块结构,不把标签翻译坏);
- 译文要求"信达雅":只译不增不改不删,标题禁句号,术语参考品牌方既有中文材料;
- 用前缀锚定正则替换翻译(不是裸字符串替换),避免同词误伤代码/属性;
- 人工审校按页面类型分优先级:营销首页 > 新闻稿 > 海量长尾详情页;
- 改大块内容前先备份 DB(seed/翻译脚本跑前 mysqldump)。
12. 工具清单
| 工具 | 用途 | 备注 |
|---|---|---|
| Local | 本地 WP 环境 | 一键起停;CLI 用其 socket 连 DB |
| tools/crawl.py | Playwright 渲染后单页爬虫 | 水合等待 + 滚动 + 双视口截图 + DOM/CSS/内容/资产四件套 |
| tools/crawl-all.py | 批量编排 | 分批跑 |
| tools/build-page-manifest.py | 目标 URL ↔ 抓取源映射 | 无映射不开工 |
| tools/fetch-assets.py | 资产下载本地化 + 去重 | 处理 429 见坑 22 |
| tools/dump-content.php | DB 内容 dump 进 Tailwind 扫描目录 | 内容变更后必跑 |
| tools/seed-*.php | CPT 内容种子(产品/新闻/品牌/设备…) | 全部幂等 |
| tools/translate-content.php | API 机翻管线 | 幂等 + 断点 + 块感知 |
| tools/style-diff.py | 源站 vs 本地样式规则 diff | 配合 class diff |
| Playwright 脚本 | 数值级验收(x/w/line-height/class diff) | 输出表,不目测 |
| Tailwind v4 CLI | source.css → compiled.css | @theme token + @source 扫描 |
| All-in-One WP Migration | 全量打包交付 | 打包前按坑 25 核验清单 |
13. 度量:怎么判断"做完了"
每页放行条件(缺一不放):
- SOP 步骤 0–7 全部走过(素材齐 → DOM/CSS/文案三读 → 转写 → 编译);
- Playwright 测量表:关键元素 x/w 与源站一致(用户视口全集);
- 左边距一致性表:全页同 x(例外仅限显式出血段);
- class 集合 diff 为空(或差集逐项查证为语法等价);
- 控制台 0 报错、0 外链、图片全载入;
- 内容译文符合"只译不增不改";
PROGRESS.md已更新。
迁移
- Local 里的开发本地版安装插件,我用的就是 All in One WP Migration,你也可以用其他的,可以完整迁移的即可,我只是一直用这个用顺手了,并不代表这个事情只有它能完成
- 导出全站文件
- 目标 WordPress 网站安装同样的插件
- 导入文件即可