Claude Media
Claude CodeでWayback Machineから消えたページを復旧する — CDX APIで過去版を取得

Claude CodeでWayback Machineから消えたページを復旧する — CDX APIで過去版を取得

リンク切れの旧URLについて、Wayback MachineのCDX APIで最新の200の記録を選び、id_付きURLで元のHTMLを取る手順です。シェルスクリプト化と、claude -pに付け替えを任せる許可設定まで扱います。

リンク切れになったページは、Wayback Machine(Internet Archiveのウェブ魚拓)に過去の版が残っていることがあります。画面で探すと時間がかかるので、CDX API(記録の索引を引くHTTP API)で「最後に正常に取れた版」を機械的に選び、その版の元HTMLを取る流れを組みます。ここまでをシェルスクリプトにしておけば、Claude Codeには付け替えの判断と書き換えだけを任せられます。

検査でリンク切れを洗い出す段は、lycheeでリンク切れを直す手順が先に使えます。本記事はその次に当たる「切れたURLの過去版を探す」段です。

CDX APIに何を聞けるか

CDXサーバーは、Wayback Machineが記録を探すときに使う索引を返すHTTPサーバーです。必須のパラメーターは url だけで、そのURLの記録(capture)が1行ずつ返ります。

http://web.archive.org/cdx/search/cdx?url=example.com/

行の列は urlkey timestamp original mimetype statuscode digest length の7つです。fl=timestamp,original のように書くと、必要な列だけに絞れます。output=json を付けると、最初の行が列名の配列で、以降が記録の配列になります。クエリに ? を含むURLを調べるときは、url= の値をパーセントエンコードします。

絞り込みには filter=[!]列名:正規表現 を使います。statuscode や mimetype が使いやすい列で、! を前に付けると一致しないものだけが残ります。期間は from= と to= で、yyyyMMddhhmmss の先頭1〜14桁を書けます。to=2015 なら2015年末までです。

返り値は既定でgzip圧縮です。curlに任せるなら意識しなくてよく、圧縮を切りたいときだけ gzip=false を付けます。

最新の200を1件だけ取る

復旧に使いたいのは、ページが正常に表示されていた最後の記録です。ステータスが200の行だけに絞り、末尾から1件取ります。

curl -s "http://web.archive.org/cdx/search/cdx?url=example.com/\
&filter=statuscode:200&filter=mimetype:text/html\
&fl=timestamp,original&limit=-1"

limit=-1 は「最後のN件」を意味します。公式の説明では、先頭から読み進めて最後のN件以外を捨てるため、遅くなることがあります。完全一致のURLでは、fastLatest=true を足すと速く最新を返す手段もあります。ただし返る件数は最低1件で、それ以上になることがあるため、limit=-1 と併用して件数を抑えます。

「リンクが死ぬ前の版」が欲しいときは、to= で上限を切ります。例えば例のドメインで2015年までに絞ると、次のように2015年末の記録が返ります。

curl -s "http://web.archive.org/cdx/search/cdx?url=example.com/\
&to=2015&filter=statuscode:200&fl=timestamp,original&limit=-1"
# 20151231222136 https://example.com/

最新の200が、すでに差し替え後の別ページということもあります。リンク先が消えた時期が分かるなら、その少し前の日付を to= に入れる方が元の内容に近づきます。

id_付きURLで元のHTMLを取る

記録の timestamp と original が分かれば、次の形でその時点のHTMLを取れます。

https://web.archive.org/web/<timestamp>id_/<original>

タイムスタンプの直後に id_ を付ける点が違いです。手元で上の例のURLを取ると、先頭は <!doctype html> と <title>Example Domain</title> で始まる元のHTMLでした。応答ヘッダーには x-archive-orig- で始まる、保存時の元サーバーのヘッダーが並びます。

タイムスタンプが記録とずれていると、Wayback Machineは近い記録へ転送します。手元では、ずれた指定に302が返り、x-archive-redirect-reason: found capture at 20210101000012 のヘッダーと転送先の location が付きました。curlで取るときは -L を付けて転送を追わせます。CDXで選んだタイムスタンプをそのまま渡す限り、転送は起きません。

通常のURLとの違いは、書き換えの有無

id_ を付けない通常のURLは、再生用にページを書き換えて返します。実在する別のページ(Claude Codeの旧ドキュメント)で、同じ記録を両方の形で取って比べました。

取得方法サイズweb.archive.org を含む箇所
通常のURLサイズ1,439,316バイトweb.archive.org を含む箇所78か所
id_ 付きURLサイズ1,429,565バイトweb.archive.org を含む箇所0か所

通常のURLには、Wayback Machineのスクリプトが差し込まれ、ページ内のリンクも web.archive.org/web/<timestamp>/… の形に付け替えられていました。id_ 付きは、保存時のHTMLがそのまま返ります。差し替え先を探すために旧ページ内のリンクを読むなら、書き換えのない id_ 側が確実です。

curlには--compressedを付ける

id_ 付きURLの取得には、つまずく点が1つあります。応答ヘッダーに content-encoding: gzip が付くため、curlへ何も指定せずに保存すると、gzipのまま書き出されます。最初に取ったファイルはバイナリの塊で、<title> を探しても見つかりませんでした。--compressed を付けると展開され、タイトルは「Claude Code overview - Anthropic」と読めました。

curl -sL --compressed \
  "https://web.archive.org/web/20250916051127id_/https://docs.anthropic.com/en/docs/claude-code/overview" \
  -o overview.html

最後の記録が301のとき

先ほどの旧ドキュメントのURLでは、collapse=statuscode で並べると、200の記録の間に301と、ステータスが - の行が挟まっていました。最後の行は301です。filter=statuscode:200 を付けずに limit=-1 だけで引くと、消える直前の本文でなくリダイレクトの記録を拾います。その記録を id_ 付きで取りに行くと、HTMLでなく301が返り、location には別ドメインの移転先が入っていました。

つまりこのURLは「消えた」のでなく「移転した」ページです。200の記録は移転前の中身であり、現在の差し替え先は、301の移転先を辿って確かめます。最後の記録がどの状態だったかを知りたいときは、filter=statuscode:200 を外して末尾の1件を見ます。

1本のシェルスクリプトにまとめる

CDXで最新の200を選び、id_ 付きURLを出力するスクリプトです。jq が必要です。URLに ? や & が含まれても壊れないよう、--data-urlencode で渡します。

#!/usr/bin/env bash
# 使い方: ./wayback-latest.sh <url> [<この日付以前。例: 2024>]
set -euo pipefail
url="$1"; to="${2:-}"
q=(--get "http://web.archive.org/cdx/search/cdx"
   --data-urlencode "url=${url}"
   --data "output=json" --data "fl=timestamp,original,statuscode"
   --data "filter=statuscode:200" --data "filter=mimetype:text/html"
   --data "limit=-1")
[ -n "$to" ] && q+=(--data "to=${to}")
row=$(curl -sS -m 60 "${q[@]}" | jq -c '.[1] // empty')
if [ -z "$row" ]; then echo "NOCAPTURE ${url}" >&2; exit 3; fi
ts=$(jq -r '.[0]' <<<"$row"); orig=$(jq -r '.[1]' <<<"$row")
echo "https://web.archive.org/web/${ts}id_/${orig}"

output=json の1行目は列名なので、記録は .[1] から数えます。該当する記録が無いURLでは .[1] が空になり、終了コード3と NOCAPTURE を返します。手元で試すと、https://example.com/ に2015を渡した場合は次のURLが出ました。

https://web.archive.org/web/20151231222136id_/https://example.com/

記録の無いパスでは NOCAPTURE https://example.com/zz-never-9137 が標準エラーに出て、終了コードは3でした。

リンク切れ一覧と突き合わせる

リンク切れの一覧は、1行1URLのテキストにしておきます。lycheeなどの出力から、404になった外部URLだけを抜き出したものを想定します。一覧を1行ずつ回し、復旧できたものを対応表にします。

while read -r u; do
  if a=$(./wayback-latest.sh "$u" 2>/dev/null); then
    printf '%s\t%s\n' "$u" "$a"
  else
    printf '%s\t%s\n' "$u" "NOCAPTURE"
  fi
  sleep 1
done < broken-urls.txt > restore-map.tsv

sleep 1 は、短時間に多数のリクエストを送らないための間隔です。公式のREADMEにレート制限の数値は書かれていないので、数百本を回すときは間隔を長めにとり、失敗した行は後で再実行します。

出来上がる restore-map.tsv は、左が旧URL、右がアーカイブのURLか NOCAPTURE です。NOCAPTURE の行は、アーカイブにも無いURLです。その行だけは、リンクごと削除するか、別の情報源を探すかを人が決めます。

復旧したHTMLを読ませて差し替え先を探す

付け替える先が、アーカイブのURLでよいとは限りません。旧ページの中身が分かれば、現在も生きている同等のページを探したい場面があります。取得は上のスクリプトで済ませ、復旧したHTMLのファイルを Read だけの権限で読ませて、手がかりを表にさせます。

claude --bare -p "out/ の復旧済みHTMLを読み、各ページの見出しと本文の要点を表にして。
現在のURLは推測で作らず、旧ページ内のリンクに書かれているものだけを挙げて。" \
  --allowedTools "Read"

--bare はフックやMCPサーバー、CLAUDE.mdの自動読み込みを省く指定で、スクリプトやCIでの利用に推奨されています。サブスクリプションのログインは使われないため、ANTHROPIC_API_KEY の設定が前提です。

HTMLをパイプで標準入力に流す方法もあります。ただし渡せる標準入力は10MBまでで、超えるとエラーで止まります。上の例の旧ページは約1.4MBでしたが、一覧が大きくなるなら、ファイルに書いてパスで読ませる形が安全です。上限の挙動は、claude -pにログを渡すと10MB上限で止まる理由で扱っています。

プロンプトに「URLは推測で作らない」と入れるのは、旧ページに書かれていない新URLをClaudeが作文するのを防ぐためです。出てきた候補は、実在を確かめてから使います。

付け替えをclaude -pに任せる

対応表ができたら、書き換えは claude -p に任せられます。許可するのは、ファイルの読み取りと編集、それにCDX取得スクリプトの実行だけです。

claude -p "restore-map.tsv の左の旧URLを、docs/ 以下のMarkdownで
右のアーカイブURLに置き換えて。NOCAPTURE の行には触れず、
触れなかった旧URLを一覧で報告して。" \
  --allowedTools "Read,Edit,Bash(./wayback-latest.sh *)" \
  --output-format json | jq -r '.result'

--allowedTools に書いたツールは確認なしで実行されます。末尾の * は前方一致で、Bash(./wayback-latest.sh *) なら、そのスクリプトで始まるコマンドだけが通ります。Bash だけを書くと、任意のシェルコマンドを許すことになります。

リストにないツールは、承認が要る場面で拒否されます。v2.1.302以降の claude -p は、権限モードを何も指定しないとManualモードで始まり、承認が要る呼び出しは拒否されます。夜間の定期実行のように答える人がいないジョブでは、--permission-prompts none を足すと、拒否されることを前提に動きます。このフラグにはv2.1.259以降が必要です。

付け替え前の旧URLが、ページの末尾などに残ってよいかは、運用で決めます。参照元の記事が「当時の版」であることが読者に伝わるよう、リンクの文言に「アーカイブ」と足すよう指示に書いておくと、レビューが楽です。

復旧した版を使う前に見ておくこと

ステータスが200でも、中身が薄い記録があります。手元で、すでに閉鎖されたディレクトリサイトの旧ドメインを調べたところ、2017年末までの最新の200は735バイトのHTMLで、<title> が1語だけのページでした。CDXの length 列を fl に足し、極端に小さい記録を弾くと、取り直しの手間が減ります。

ステータスの列が - で、mimetype が warc/revisit の行もあります。内容が前の記録と同じため、本体を持たない記録です。filter=statuscode:200 では落ちますが、件数を数えるとき、行数と実際の版の数がずれる原因になります。

matchType=prefix と collapse=urlkey を組み合わせると、ディレクトリ配下のURLを1本ずつ洗い出せます。READMEにも「遅いことがある」と書かれていて、手元では504 Gateway Time-outが返りました。数が多い範囲は limit と showResumeKey=true で区切って取るのが現実的です。1回の問い合わせの上限は、READMEの既定で15万件です。

CDX自体はBETAと表記された仕様です。列の追加や挙動の変更があり得るため、スクリプトに入れた fl と filter の結果は、依存先の仕様が変わったときに検査できるよう、先頭の列名の行も見ておくと安全です。

まとめ

CDXで最新の200を選び、id_ 付きURLで元のHTMLを取るところまでは、決まった手順で自動化できます。どの版を採るか、NOCAPTUREをどう扱うかという判断は、人かClaude Codeに残すのが、権限を狭く保てる分担です。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/headless
  2. github.com/internetarchive/wayback/blob/master/wayback-cdx-server/REA…
この記事を共有:XはてブLinkedIn