capture-site
一次性地将现网站点所提供的一切内容保存下来,这样后续的所有步骤都可以基于这个本地副本进行。
黄金法则
现网站点只读,并且抓取副本在创建后也只读。 这些脚本仅发出有节律的 GET 和 HEAD 请求;绝不提交表单、登录或手动获取以填补遗漏。一旦生成了笔记,capture/ 目录下的任何内容都不会被编辑:它是所有关卡读取的证据。
何时使用
转换流程的第二步(convert-site 会运行它),或者任何时候需要对现网站点进行盘点(预发布或新站点是 launch-audit 的任务)。如果抓取过程因阻塞而退出(退出码 3),可以按下面的方法进行恢复。
节流策略
每个脚本都发送桌面版 Chrome 的 User-Agent 头。抓取脚本(capture、media、assets、fingerprint)共享一个按域名限制的限流器:默认情况下,请求开始之间间隔 400 毫秒,最多并发 4 个(media 为 6 个),因此一个响应迅速的域名可以在几分钟内完成读取。Chromium 步骤(shoot、css-coverage、styles)一次只加载一个页面,间隔 500 毫秒,launch-audit 和 pre-launch 则使用自己的节流策略。限流器会自动适应:
- 收到 429 或 503 响应时,该域名的延迟将加倍(最多 32 倍),并且并发数降为 一次一个请求;在连续收到 30 秒正常响应后,限制会逐步恢复(延迟减半,并发数加一),直至回到默认值。
- 如果某个响应的耗时超过 5 秒,该域名的并发请求数将减半(在减半期间不会再触发此操作),恢复方式相同:每连续 30 秒正常响应,并发数增加一个。
- 403 或网络错误的处理方式与之前的连续三次停止逻辑相同。
--polite 模式是旧有的节流方式:一次一个请求,间隔 2500 毫秒(适用于 capture、media、assets)。如果目标站点位于带有机器人验证墙的共享主机上(例如 SiteGround 的 /.well-known/sgcaptcha/,或者对少量快速请求就返回 429 或 503 的主机),或者当之前的运行因阻塞而停止时,请从一开始就使用该模式:使用 --polite 恢复。--delay <ms> 和 --concurrency <n> 可以覆盖默认值或 --polite 模式。对现网站点请保持默认值或使用 --polite 模式(--delay 0 仅用于本地测试数据),并且绝不要更改 --ua 来绕过封锁。
capture会预先获取最多--concurrency个队列中的 URL,并按队列顺序记录它们,因此抓取结果与逐一爬取相同。每个 URL 会重试 3 次。如果一个 URL 在被重试 3 次后仍然被阻塞(403、429、503 或网络错误),其状态和attempts会被记录在inventory.json中(如果是 sitemap 或 REST 列表中的 URL,则记录在meta.json的discoveryErrors中),然后爬取继续。只有在 连续 3 个不同的 URL 都耗尽重试次数且之间没有任何成功请求 时,抓取才会停止,此时状态被保存,这三个 URL 会被重新加入队列并赋予新的重试机会。它从不请求wp-admin、wp-login、xmlrpc.php或?s=搜索页面。media每个文件尝试 3 次:如果持续收到 429 或 503,则立即停止;403 或网络错误会被记录为该文件的状态,当连续出现 3 个这样的文件(按响应到达顺序)时停止。已经在下载的文件会允许完成。每处理 100 个文件保存一次media.json,因此被中断的运行可以从接近断点处恢复。fingerprint不重试:如果首页被封锁,或返回的是机器人验证页面,则退出码为 3。assets和shoot会在遇到连续 3 个被封锁的响应后停止。
命令序列
在将包含 capture/ 目录的文件夹(即站点仓库的根目录)下运行。<skill-dir> 是本技能文件夹的绝对路径,存放于已执行 npm install 的工具包副本中(脚本会导入其包)。每个脚本都能打印 --help;请运行它以获取帮助,而不是阅读源代码。按以下顺序,将每个步骤作为单独的命令运行:
-
指纹识别,然后读取它所命名的平台参考(如下表所示):
node <skill-dir>/scripts/fingerprint.mjs https://www.example.com -
抓取。 发现方式包括:站点地图(robots.txt 中的每行
Sitemap:,加上平台的默认索引)、REST 列表(WordPress,当指纹识别发现 REST 时;--no-rest可以跳过)、链接爬取,以及--seed <path>或--seed-file <file>(每行一个路径或 URL,#开头的行为注释)用于爬虫无法到达的路径(旧 URL、旧的.htaccess)。爬取完成后,爬虫未找到的 Wayback 路径会写入wayback-unverified.txt(--no-wayback可跳过;如果列出失败,会在 10 秒后重试一次,然后meta.json中的wayback会记录failed)。?p=短链接会被记录但不会验证,除非你传递了--verify-shortlinks参数。爬取完成后,它会请求一个不可能存在的路径(/<32 hex>/)一次,这样站点的 404 页面会被保存为pages/404.html(记录在meta.json的notFound中;如果站点对那个路径返回 200 或重定向,则不会保存任何页面),并且会分别请求/favicon.ico和/apple-touch-icon.png各一次,跟随重定向(meta.json中的icons:包含{ status, file, contentType, bytes }的路径);如果返回 200,则逐字节保存为assets/root-favicon.ico或assets/root-apple-touch-icon.png(如果返回的是 HTML 内容,则不保存并添加备注;如果文件超过 5 MB,状态为"too-large"),并且assets.mjs会将其作为kind: "root-icon"列出在assets/index.json中,附带其path,以便构建过程将其放置到位。URL 使用大写百分比转义符作为键(%c3%a9和%C3%A9视为同一个 URL),因此如果一个页面被两种拼写方式链接,只会被抓取一次。robots.txt 中引用的站点地图如果返回 404(或任何非 200 状态码),会在sitemaps/index.json中列出其状态,并在meta.json的discoveryErrors中记录。位于其他主机上的站点地图(robots.txt 引用姊妹站点的,或索引列出了其他主机的;www 和 apex 视为同一个主机)会被列为{ url, host, skipped: "other host" }且永不获取,因此它的 URL 不会进入爬取队列。两个具有相同文件名的站点地图 URL 不会互相覆盖:第二个站点地图会使用其目录路径的后缀保存(例如page-sitemap-es.xml)。--exclude <prefix>[,<prefix>...]可以排除主机上的部分路径(例如 WordPress 多站点中的子站点如/president/,或站点所有者保留的部分):路径以此前缀开头的 URL 永远不会被请求,无论站点地图、REST 列表、链接还是重定向指向它。前缀下的站点地图会在sitemaps/index.json中列为{ url, skipped: "excluded" };重定向到排除路径的请求会被记录,但目标不会被请求;指向排除路径的链接会保留在已保存的 HTML 中,并在links.json中列为excluded,而不是internal;meta.json的excluded字段会记录这些前缀,并且基线也会包含它们。前缀和路径的比较使用规范化形式,因此/présidence/、/pr%c3%a9sidence/和/pr%C3%A9sidence/被视为同一个前缀。如果样式表或根图标重定向到一个排除前缀下的路径,则不会跟随(该样式表会以状态"excluded"列出)。--resume会保留已有的排除前缀;在--resume时添加新的前缀会移除该前缀下已捕获的内容(包括其pages/和text/文件、来自该前缀的重定向、该前缀下的 feeds 和站点地图,以及仅这些页面使用的样式表和脚本),并打印已移除的内容。media.mjs和assets.mjs也会跳过它们前缀下的文件。node <skill-dir>/scripts/capture.mjs https://www.example.com --out capture然后,当
wayback-unverified.txt不为空时,执行 Wayback 第二轮抓取:它会对每个归档路径请求一次,因此那些仍然返回重定向的路径会落入capture/redirects.json,其来源为seed(基线的重定向处理程序会拾取它们),其余路径则以其状态记录在inventory.json中。--seed、--seed-file和--include-query在--resume时均适用,并且不会再查询 Wayback Machine:node <skill-dir>/scripts/capture.mjs https://www.example.com --out capture --resume --seed-file capture/wayback-unverified.txt -
媒体文件。 列出
capture/media.json中的每个媒体文件,并将其保存到.conversion/media-cache/,即转换的工作目录,该目录不会被纳入 git 版本控制:node <skill-dir>/scripts/media.mjs --capture capture --download .conversion/media-cache它会一次下载六个文件。默认情况下(
--sizes referenced),它获取原始文件以及站点使用的每种尺寸:即页面、样式表、srcset、og 标签或站点地图指向的尺寸,或者其文件名出现在任何已捕获页面、样式表或脚本中的尺寸(data-thumb、data-bg、JSON 设置、滑块脚本)。仅由 REST 媒体列表命名的 WordPress 尺寸会被列为downloaded: false, reason: "unreferenced size"且不会请求。它会打印拆分情况,例如920 originals + referenced sizes (downloaded), 4,520 unreferenced sizes (listed only):将该行信息告诉用户。当站点所有者表示旧邮件或其他站点可能链接到这些尺寸时,请使用--sizes all。通过查询字符串调整大小的主机(WordPress VIP、Jetpack Photon:
photo.jpg?w=1024、?resize=300,200)在相同的路径上提供每种尺寸。原始文件是裸路径,因此这就是被请求和保存的内容;每个看到的查询字符串都会在variants下列出(不请求;--sizes all会将其保存为<path>%3F<query>到一个同级文件夹.conversion/media-cache-variants/中,因此上传或复制到仓库中的媒体缓存只包含裸路径)。当裸路径返回错误时(4xx 或 5xx;重定向会在downloadFrom及其 location 中记录,并且过大的文件会在容量限制提高后再次请求),一个变体将被保存到其位置,并且条目的downloadedFrom将是提供它的 URL。缓存清理型查询字符串(不含w、h、resize、crop、fit、quality、format或s参数,例如?ver=3)是同一文件,因此会优先选择它,并且不会标记其他内容;否则,最宽的变体将被保存,并且条目还会包含resized: true。运行会为每个文件打印一行,并显示计数(2 originals unavailable (resized: true) ...),baseline/media.json 会同时保留这两个字段,并且媒体关卡会在不失败的前提下重复计数N resized (original unavailable on live),因为它与实际保存的字节数进行比较。将该计数告诉用户,并在计划和操作手册中列出这些文件,作为现网站点不再提供的原始文件。 -
模板。 将返回 200 的页面聚类到
capture/templates.json。在 WordPress 上,页面首先按模板 body class 分组(home、page、single-post、archive、模板类),并且一个标记明显不同的页面(Jaccard 相似度低于 0.5)会作为page-2拆分出来;其他平台按 Jaccard 相似度 0.6 合并标记,作为template-<n>:node <skill-dir>/scripts/templates.mjs --capture capture -
资产。 将 Logo、favicon、apple-touch-icon、
<link rel=manifest>的 Web 清单以及所有@font-face字体提取到capture/assets/中,此外还包括根目录的/favicon.ico和/apple-touch-icon.png(root-favicon.*、root-apple-touch-icon.*),前提是meta.json的icons记录它们返回了 200:node <skill-dir>/scripts/assets.mjs --capture captureLogo 首先在首页的
<header>内部查找,然后在其 class 或 id 包含“logo”的其他元素(如.logo、.site-logo、#logo-wrap)中查找。在每个元素中:首先查找其 src、alt、class 或 id 包含单词“logo”(但不包含“logout”)的<img>,然后查找包含“logo”在其 class、id、aria-label 或<title>中的内联<svg>(保存为logo.svg,在assets/index.json中url: null;其备注会说明它是否只<use>了页面中其他地方定义的符号,这会导致独立渲染时为空),然后查找链接到首页的链接中的第一个<img>,然后查找第一个<img>。请用肉眼检查assets/logo.*:在某些不寻常的 header 中,一个账户头像或赞助商标记仍然可能胜出。Favicon 和 apple-touch-icon 按其<link>href 提供的样式保存,包括查询字符串,因此如果它们指向上传文件的调整大小版本(?w=32、?w=180),则其字节会与media.json中该文件的条目不同:保留保存的图标,而不是原始文件。 -
现网截图,每种模板在 390、768 和 1440 三种宽度下拍摄,使用视觉审查截图工具:
node <skill-dir>/../visual-review/scripts/shoot.mjs --base https://www.example.com --routes capture/templates.json --widths 390,768,1440 --out capture/screens/live -
Lighthouse(可选)。 仅当
npx --no-install lighthouse --version有响应时才执行;绝不未经请求安装它;如果缺少 Lighthouse,请在笔记中说明(作为跳过的步骤,而非失败的步骤)。运行mkdir -p capture/lighthouse-live,然后按顺序为templates.json中的每个representative运行一次:npx --no-install lighthouse https://www.example.com/ --quiet --chrome-flags="--headless=new" --output json --output html --output-path capture/lighthouse-live/home -
README。 将抓取摘要汇总到
capture/README.md中;其## Notes部分以生成的描述行开头,每次运行时都会重写(包括第二轮 Wayback 验证的路径数量以及各自的响应状态或重定向目标;Wayback 列表失败或由于大小被拒绝;以及所有在重试后仍然失败的 URL)。最后运行该步骤,以及在任何重新运行之后:node <skill-dir>/scripts/readme.mjs --capture capture
| 脚本 | 退出码 |
|---|---|
fingerprint | 0 完成,2 用法错误或不可达,3 被封锁或遇到机器人验证 |
capture | 0 完成,2 用法错误,3 因阻塞、不可达、机器人验证而停止(均可恢复),或没有页面返回 200 |
media | 0 完成(包含缺失文件),2 用法错误,没有抓取内容或没有返回 200 的页面,3 因阻塞停止 |
assets | 0 完成(包含缺失文件),2 用法错误或没有抓取内容,3 因阻塞停止 |
templates | 0 完成,2 用法错误,没有 inventory.json,或“没有已捕获的页面”(均未返回 200) |
readme | 0 完成,2 用法错误或没有 inventory.json |
shoot | 0 全部拍摄,1 某个路由失败,2 用法错误,3 因阻塞停止 |
关于退出码 3
站点正在阻止请求或无法到达。等待片刻(数分钟,而非秒),然后:
fingerprint:重新运行。capture:使用--resume运行相同的命令。仍然在队列中的 URL 将被重试。
…
[因翻译而截断]