Website Clone - 通用网站克隆
Overview
将任意可公开访问的网页克隆为可离线浏览的静态副本:下载入口页面(可选同站子页面)及其引用的全部资源,把 HTML/CSS 中的引用统一改写为相对路径,输出结构清晰的目录树和下载报告。
核心脚本 scripts/clone_site.py 仅依赖 Python 3 标准库,无需安装任何第三方包。
Quick Start
python scripts/clone_site.py <url> -o <输出目录>
示例:
# 克隆单页
python scripts/clone_site.py https://example.com -o ./clone-example
# 克隆页面并跟进 1 层同站链接(最多 20 页)
python scripts/clone_site.py https://docs.example.com/guide -o ./clone-guide --depth 1 --max-pages 20
完成后用浏览器打开 <输出目录>/index.html 即可离线浏览。
懒加载 / JS 渲染站点(浏览器快照模式)
对滚动后才加载内容的站点(门户 feed、信息流、图片瀑布流等),纯 HTTP 抓取会丢失懒加载内容。此时先用快照脚本渲染出完整 DOM,再克隆:
# 1. 浏览器渲染 + 自动滚动触发懒加载(需 playwright,默认滚到页面高度连续 3 轮不变)
python scripts/snapshot.py <url> -o rendered.html
# 2. 从渲染后的 DOM 克隆(url 仍作为资源解析基准)
python scripts/clone_site.py <url> --html-file rendered.html -o <输出目录>
静态 DOM 模式(默认开启):从快照克隆时,脚本会剥离页面全部 <script> 并把 loading="lazy" 图片改为 eager——快照 DOM 已含全部懒加载内容,剥离 JS 后页面变成纯静态传统模式:滚动不会再触发运行时请求,也就不会出现"加载中转圈但永远出不来"的情况,且完全不需要联网。若确需保留原站 JS(如依赖脚本实现的交互),加 --keep-scripts,此时会注入离线优先 shim 拦截远程请求(注意:滚动加载类站点离线后仍会出现"加载中"提示)。
环境要求:pip install playwright && playwright install chromium。若已手动放置 Chromium(如 Chrome for Testing 解压到 %LOCALAPPDATA%\ms-playwright\chromium-<rev>\chrome-win64\),脚本会优先以 channel="chromium" 启动完整浏览器,无需 headless-shell。
判断何时用快照模式:克隆完成后若 index.html 中 <img> 数量明显偏少、或正文文本量远小于线上实际内容,即可判定存在懒加载,改用快照模式重新克隆。
输入参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
| url(必填) | - | 目标页面 URL,可省略 https:// 前缀 |
| -o, --output | ./site-clone | 输出目录 |
| --depth N | 0 | 跟进同站 <a> 链接的层数,0 = 仅克隆入口页 |
| --max-pages N | 30 | 最多下载的页面数 |
| --max-assets N | 500 | 最多下载的资源数(防失控) |
| --timeout S | 15 | 单请求超时秒数 |
| --header "K: V" | 无 | 附加请求头,可重复(如 --header "Cookie: x=1" 传登录态) |
| --no-referer | 关 | 默认自动携带 Referer: <入口页URL>(模拟浏览器,绕过防盗链);某些站点拒绝异常 Referer 时用此项关闭 |
| --user-agent UA | Chrome UA | 自定义 User-Agent |
| --html-file PATH | 无 | 用本地(浏览器渲染后)HTML 作为入口页,配合 snapshot.py 克隆懒加载站点 |
| --keep-scripts | 关 | 从 --html-file 克隆时保留页面 JS 并注入离线 shim(默认剥离脚本、输出纯静态页) |
| --no-offline-shim | - | 不注入离线优先拦截脚本 |
| --no-static-render | - | 不提取内嵌 JSON / 不生成静态渲染回退页 |
输出目录约定:必须保存在用户当前工作空间内(例如 <workspace>/clones/<站点名>/),运行时显式传 -o;严禁使用系统临时目录。
输出结果
<输出目录>/
├── index.html 入口页(其余页面按原站路径镜像存放)
├── index.rendered.html SPA 时的静态渲染回退页(无需 JS 即可看内容)
├── data/*.json 从页面提取的内嵌 JSON 数据
├── assets/<域名>/... 全部静态资源(CSS/JS/图片/字体/音视频)
└── report.md 下载报告:页面/资源清单、失败清单、功能开关状态
SPA 页面有三个打开方式:index.html(React 等运行时用内嵌数据渲染)→ 失败时用 index.rendered.html(纯静态卡片渲染,禁 JS 也能看)→ data/*.json(原始数据兜底)。
克隆完成后必须向用户报告:保存路径、页面/资源数量、失败项(见 report.md)。
工作流程
- 确认目标 URL 可公开访问(不是内网/登录墙页面;需要 Cookie 时用
--header传入)。 - 选择合适的
--depth/--max-pages:单页演示用默认值;文档站/小型站按需提高。 - 运行脚本,检查控制台输出与
report.md。 - 抽查
index.html:确认资源引用为相对路径(assets/...或../assets/...)。 - 将输出目录完整交付给用户,说明双击 index.html 即可离线浏览。
已妥善处理的常见情况
- 相对路径改写:HTML 属性(src/href/srcset/poster/data-src 等)、
<style>块、内联 style、CSS 内url()与@import全部改写为相对路径;子目录页面自动计算正确的../层级;<base>标签被识别并移除以防干扰。 - 下载失败:失败资源的引用自动回退为原始绝对 URL(在线时可正常加载),并在 report.md 中逐条列出,页面不会出现死链。
- 编码:自动检测 charset(HTTP 头 / meta 标签),GBK/GB2312 页面正确解码后统一以 UTF-8 写盘,并同步改写 meta charset,中文内容无损。
- 扩展名修复:无扩展名 URL(如 CDN 图片接口)按 Content-Type + 文件魔数自动补全扩展名,保证 file:// 下浏览器能正确识别。
- 懒加载属性:data-src / data-srcset / data-original / data-bg 等同样被抓取。
- 边界安全:跳过
data:/javascript:/mailto:/锚点;同 URL 不同 query 生成不同文件名防覆盖;单文件 25MB 上限;跨域 CDN 资源可下载。
SPA 离线增强(内嵌数据提取与静态渲染)
对 React/Vue 等前端渲染站点,脚本内置三重离线保障(默认开启):
- 数据 URL 本地化:内嵌在
<script>或 SSR 注释(如<!--s-data:{...}-->)里的资源 URL 一并改写为本地路径,SPA 运行时渲染列表时图片走本地。 - 离线优先拦截:向每个页面
<head>注入 shim,把fetch/XHR 的远程请求模拟为网络失败——SPA 只能使用内嵌初始数据,绝不回退到在线数据(可用--no-offline-shim关闭)。 - 静态渲染回退页:提取内嵌 JSON,启发式识别其中的对象数组(卡片/列表数据),生成
rendered.html——标题/图片/链接/热值字段自动卡片化,图片映射本地资源,禁用 JS 也能完整浏览(可用--no-static-render关闭);原始 JSON 同步落盘data/*.json供复用。
已知边界:提取与渲染为通用启发式,复杂嵌套数据可能识别不全;SPA 若把初始数据完全放在独立 API(页面 HTML 无内嵌),则回退页无数据可渲染,此时 report.md 会注明"no embedded JSON list data found"。
适用范围与限制
适用:静态页面、文档站、博客、企业官网、落地页、HTML 模板/演示站,以及有内嵌初始数据的 SPA(React/Vue 服务端渲染站点,配合离线增强三件套)。
不适用 / 已知限制(遇到时应如实告知用户):
- 数据完全依赖运行时 API 的 SPA:页面 HTML 不含内嵌数据时,只能克隆到布局框架;index.html 需联网才有内容(shim 注入后表现为空数据),静态回退页无数据可渲染。
- 需要登录的页面:可用
--header "Cookie: ..."传登录态,但会话过期后失效。 - ES Module 相对导入的 JS:模块内部
import语句不会改写,离线后可能加载失败。 - 流媒体/加密视频(DRM)无法克隆。
合规提醒:本技能仅用于个人备份、学习研究、离线演示等合法用途。克隆结果受原网站版权约束,不得用于抄袭、仿冒或商用;请尊重目标站点 robots 协议,避免对同一站点高频抓取(脚本内置并发与数量上限即为此设计)。
Resources
scripts/clone_site.py- 核心克隆脚本(Python 3 标准库,零依赖),直接运行python clone_site.py --help查看参数。scripts/snapshot.py- 浏览器快照脚本(需 playwright),用于懒加载/JS 渲染站点的 DOM 渲染与懒加载触发。
微信扫一扫