capture-site
將即時網站提供的所有內容儲存一次,以便後續步驟都使用這些擷取資料進行。
金律
即時網站是唯讀的,擷取完成後同樣是唯讀的。 腳本只會發出有間隔的 GET 與 HEAD 請求;絕不提交表單、登入,或手動擷取以填補缺口。筆記寫入後,capture/ 下的任何內容都不會再被編輯:它將是所有關卡檢驗的證據。
使用時機
轉換流程的第 2 步(convert-site 會自動執行),或在需要盤點即時網站時(暫存或新網站的盤點是 launch-audit 的工作)。若擷取因錯誤而中斷(退出碼 3),請依照下方說明續傳。
限速
所有腳本都使用桌面版 Chrome 的使用者代理字串。擷取腳本(capture、media、assets、fingerprint)共用一個基於主機的限速器:預設情況下,請求間隔 400 毫秒,最多 4 個請求並行(media:6 個),因此回應快速的網站可在幾分鐘內完成讀取。Chromium 相關步驟(shoot、css-coverage、styles)一次載入一個頁面,間隔 500 毫秒,而 launch-audit 與 pre-launch 則使用自己的限速。限速器會自動調整:
- 收到 429 或 503 回應時,該主機的延遲時間會加倍(最多 32 倍),並降為一次只能有一個請求;在連續收到 30 秒的正常回應後,會逐步恢復(延遲減半,並行數增加 1),直到回到預設值。
- 回應時間超過 5 秒時,該主機的並行請求數會減半(在減半期間不會再次觸發),並透過連續 30 秒的正常回應逐步恢復(每 30 秒可再增加 1 個並行請求)。
- 403 或網路錯誤同樣會計入下方提到的「連續 3 次」停止條件。
--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 次。若嘗試 3 次後仍被封鎖(403、429、503 或網路錯誤),該 URL 會連同其狀態碼與attempts記錄在inventory.json中(sitemap 或 REST 列表則記錄在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 -
擷取。 發現機制包含 sitemap(robots.txt 中的每個
Sitemap:行,加上平台的預設索引)、REST 列表(WordPress,當指紋辨識發現 REST 時;可使用--no-rest跳過)、連結爬取,以及--seed <path>或--seed-file <file>(每行一個路徑或 URL,#開頭為註解)用於爬取無法觸及的路徑(舊 URL、舊的.htaccess)。爬取完成後,Wayback Machine 中未找到的路徑會寫入wayback-unverified.txt(可使用--no-wayback跳過;若列表失敗,會等待 10 秒後再嘗試一次,然後才在meta.json的wayback中記錄failed)。?p=短連結會記錄為未驗證,除非傳入--verify-shortlinks參數,否則不會擷取。爬取完成後,它會請求一個不存在的路徑(/<32 個十六進位字元>/)一次,以便將網站自己的 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 則不儲存並附帶note;若超過 5 MB 則狀態為"too-large"),且assets.mjs會將其以kind為"root-icon"列出在assets/index.json中,包含其path,以便建置流程將其放置於該路徑。URL 會以大寫的百分比編碼作為鍵值(%c3%a9與%C3%A9視為同一個 URL),因此用兩種拼法連結的頁面只會被擷取一次。robots.txt 中命名但回應 404(或任何非 200 狀態碼)的 sitemap,會連同其狀態碼記錄在sitemaps/index.json中,並在meta.json的discoveryErrors中記錄。位於其他主機上的 sitemap(robots.txt 指向姊妹站點的 sitemap,或索引 sitemap 列表中包含的;www 與 apex 視為同一主機)會記錄為{ url, host, skipped: "other host" },且永不擷取,因此這些 URL 不會進入爬取佇列。兩個檔名相同的 sitemap URL 不會互相覆蓋:第二個會以其目錄路徑作為後綴儲存(例如page-sitemap-es.xml)。--exclude <prefix>[,<prefix>...]可將網站的部分路徑排除在外(例如 WordPress 多站點的子站點如/president/,或網站擁有者保留的區域):路徑以此前綴開頭的 URL 將永不請求,無論是 sitemap、REST 列表、連結或重導向指向它。位於排除前綴下的 sitemap 會記錄在sitemaps/index.json中,狀態為{ url, skipped: "excluded" };重導向到排除區域的目標會被記錄但不請求;指向排除區域的連結會保留在已儲存的 HTML 中,但在links.json中歸類為excluded而非internal;meta.json的excluded會記錄這些前綴,基準線(baseline)也會帶入。前綴與路徑的比較會進行標準化,因此/présidence/、/pr%c3%a9sidence/與/pr%C3%A9sidence/被視為相同前綴。樣式表或根圖示若重導向到排除區域,則不會跟隨(該樣式表會以狀態"excluded"記錄)。--resume會保留這些前綴;在--resume時新增的前綴,會移除已擷取的該前綴下的內容(包括其頁面及對應的pages/與text/檔案、來自該區域的重導向、饋送與 sitemap,以及僅這些頁面使用的樣式表與腳本),並印出已移除的內容。media.mjs與assets.mjs同樣會跳過這些排除前綴下的檔案。node <skill-dir>/scripts/capture.mjs https://www.example.com --out capture然後進行 Wayback 第二階段,當
wayback-unverified.txt不為空時:它會對每個已存檔的路徑發出一次請求,因此那些仍會重導向的路徑會以來源seed記錄在capture/redirects.json中(基準線的重導向會拾取它們),而其餘的路徑則會連同其狀態記錄在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 標籤或 sitemap 引用的,或其檔案名稱出現在已擷取頁面、樣式表或腳本中的(data-thumb、data-bg、JSON 設定、滑桿腳本)。WordPress 中僅 REST 媒體列表命名的尺寸,會以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;重導向會記錄其位置;太大的檔案會在提高上限後再次請求),則會儲存一個變體作為替代,且該條目的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 -
資產。 將標誌、favicon、apple-touch-icon、
<link rel=manifest>的網頁 manifest 以及每個@font-face字型存入capture/assets/,此外,當meta.json的icons記錄為 200 時,還會儲存根目錄的/favicon.ico與/apple-touch-icon.png(root-favicon.*、root-apple-touch-icon.*):node <skill-dir>/scripts/assets.mjs --capture capture標誌會先在首頁的
<header>中尋找,然後在 class 或 id 包含 logo 名稱的其他元素中尋找(.logo、.site-logo、#logo-wrap)。在每個元素中:優先尋找 src、alt、class 或 id 包含 "logo" 字詞的<img>(但不包含 "logout"),接著是包含 "logo" 在其 class、id、aria-label 或<title>中的內聯<svg>(儲存為logo.svg,assets/index.json中url: null;若它僅<use>了頁面中其他地方定義的 symbol,則無法獨立呈現,其 note 會說明),然後是首頁連結中的第一個<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有回應時執行;未經詢問絕不安裝;若缺少,請在筆記中說明(跳過,非通過)。執行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 會被重試。
…[內容已截斷,待翻譯]