WARC (Web ARChive) ファイルフォーマット入門

This article was generated by AI. Please be cautious about the accuracy of its content.

Webアーカイブの標準ファイルフォーマットであるWARC (Web ARChive) の基礎を解説する。ISO 28500で規格化されたフォーマットの構造、レコード型、そして実際にWARCを生成・読み取るためのツールの使い方までを扱う。一方、大規模クローラーの設計やWARCを用いたリプレイシステムの実装は扱わない。

TL;DR

  • WARCは複数のHTTPリクエスト/レスポンスを1ファイルにまとめて保存するための標準フォーマットで、2009年にISO 28500として規格化されている
  • ファイルはレコードの連結として構成され、各レコードはHTTPライクなヘッダーとコンテンツブロックを持つ。warcinfo/request/response/revisitなど複数のレコード型を使い分ける
  • Internet ArchiveやCommon Crawlといった主要なWebアーカイブプロジェクトが採用しており、wget --warc-fileやPythonのwarcioを使えば手元でも簡単に生成・解析できる

背景: なぜWebアーカイブに専用フォーマットが必要か

Webページを保存したいだけなら、単純にHTMLをcurlでダウンロードしてディレクトリに並べればよさそうに見える。しかし実際のアーカイブには以下の要件がある。

  • リクエスト/レスポンスのペアを保持する — あるコンテンツがどのURL・どのヘッダーで返されたかを後から検証できる必要がある
  • HTTPヘッダーを含めるContent-TypeContent-Encoding、リダイレクトのLocationなど、再現には生のレスポンスヘッダーが必須
  • メタデータを記録する — クロール日時、クローラーの識別子、ペイロードのハッシュ値といったアーカイブ固有の情報
  • 大量のリソースを効率的に格納する — 1クロールで数百万〜数十億のリソースが生成されるため、ファイルシステムに散らばらせると扱いきれない
  • 重複排除 — 同一のリソースが繰り返し取得された場合、2回目以降は参照だけ記録したい

WARCはこれらの要件を満たすために設計されている。

ARCフォーマットとの関係

WARCの直接の前身はInternet Archiveが1996年から使っていた ARC形式 にあたる。ARCはURL、日時、Content-Length、そしてレスポンス本体を連結しただけの素朴なフォーマットで、クローラーのログ出力に近い。ARCはメタデータの種類が限定的で、リクエストヘッダーや重複排除情報を表現できなかった。

WARCはARCを一般化し、任意の種類のレコードを同じファイルに混在させられるようにした点が最大の違い。ARCのレコードは実質的にレスポンスのみだが、WARCでは後述の通り複数のレコード型が定義されている。

WARCファイルの全体像

WARCファイルはレコードの連結として構成される。1つのファイルには、しばしば数千〜数百万のレコードが順に詰め込まれる。

+------------------+
| warcinfo record  |   ← ファイルの先頭。クローラーや設定の情報
+------------------+
| request record   |   ← 送信したHTTPリクエスト
+------------------+
| response record  |   ← 受信したHTTPレスポンス
+------------------+
| request record   |
+------------------+
| response record  |
+------------------+
|      ...         |
+------------------+

実運用ではファイル全体をgzipで圧縮して.warc.gzとして配布するのが一般的。ここで重要なのは、gzipをレコード単位でかけるという点。ファイル全体を1つのgzipストリームにするのではなく、各レコードを個別に圧縮して連結する。gzip仕様はストリームの連結を許容しているため、通常のgzipデコーダで全体を展開できる。この仕組みのおかげで、ファイルの途中から特定のレコードだけを展開できる(ランダムアクセスが可能)。

CDXインデックス

WARCそのものには目次がない。特定URLのレコードを高速に引くには、別途CDXインデックスを作成する。CDXはURL、タイムスタンプ、WARCファイル名、そしてレコードのバイトオフセットと長さを記録したテキストファイルで、Wayback Machineのようなリプレイシステムはこのインデックスを引いて該当レコードにシークする。CDX自体はWARC規格の外にあるが、事実上の標準となっている。

レコードの構造

1つのレコードは3つの要素で構成される。

  1. バージョン行WARC/1.1 のようにフォーマットのバージョンを宣言する
  2. WARCヘッダー — HTTPヘッダーに似たKey: Value形式のメタデータ
  3. コンテンツブロック — 実際のペイロード(HTTPリクエスト本体やレスポンス本体など)

ヘッダーとコンテンツブロックは空行(\r\n)で区切られ、各レコードの末尾には\r\n\r\nが付く。以下は最も単純なresponseレコードの例。

example.warc

WARC/1.1
WARC-Type: response
WARC-Record-ID: <urn:uuid:c34a9a2e-6f1d-4b8b-9d5c-2b4c9e0f1a2b>
WARC-Date: 2026-04-10T12:00:00Z
WARC-Target-URI: https://example.com/
Content-Type: application/http;msgtype=response
Content-Length: 142

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 42

<!doctype html><title>Hello</title><p>ok</p>

ポイントは、レコードのContent-Typeapplication/http;msgtype=responseとなっていること。つまりレコードのコンテンツブロックはHTTPメッセージそのものであり、ステータス行からレスポンスボディまで生の形で格納される。WARCはその上にもう1段ヘッダーを被せてメタデータを付けているだけ、と考えるとわかりやすい。

必須ヘッダー

仕様で全レコードに必須とされるヘッダーは以下の4つ。

ヘッダー内容
WARC-Typeレコードの種類。warcinfo/response/requestなど
WARC-Record-IDレコードを一意に識別するURI。通常は<urn:uuid:...>
WARC-Dateレコード作成日時。ISO 8601のUTC
Content-Lengthコンテンツブロックのバイト数

加えて、レコード型に応じて以下のようなヘッダーが使われる。

  • WARC-Target-URI — アーカイブ対象のURL(request/responseなど)
  • WARC-Payload-Digest — ペイロード(HTTPボディ)のハッシュ値。重複排除の判定に使う
  • WARC-Block-Digest — コンテンツブロック全体のハッシュ値
  • WARC-Concurrent-To — 同じ取得イベントに属する別レコード(例えばresponseと対になるrequest)を指す
  • WARC-Refers-To — 別のレコードを参照する(revisitで元のレコードを指す)

主要なレコード型

WARC/1.1では8種類のレコード型が定義されている。実務でよく目にするのは以下の通り。

warcinfo

WARCファイル自身のメタデータを記述するレコード。通常はファイルの先頭に1つ置く。コンテンツブロックはプレーンテキストで、クローラー名、バージョン、設定ファイルの内容などを自由形式で書く。

WARC/1.1
WARC-Type: warcinfo
WARC-Record-ID: <urn:uuid:...>
WARC-Date: 2026-04-10T12:00:00Z
Content-Type: application/warc-fields
Content-Length: 86

software: Heritrix/3.4.0
format: WARC/1.1
robots: classic
hostname: crawler01.example.org

request / response

HTTPのリクエストとレスポンスをそのまま格納する。実際のクロールデータの大部分はこの2つで占められる。WARC-Concurrent-Toで対応するrequestresponseを結びつける。

resource

HTTPレイヤーの情報が不要な、生のリソースだけを格納するレコード。例えばFTPから取得したファイルや、クローラーが生成した派生物(スクリーンショットなど)をresourceとして記録する。

revisit

重複排除のためのレコード。同じペイロードがすでにアーカイブされている場合、レスポンス本体を再度保存する代わりにrevisitを記録する。WARC-Refers-ToWARC-Refers-To-Target-URIで元のレコードを指し、ペイロードのハッシュ値(WARC-Payload-Digest)で同一性を保証する。

大規模クロールでは同じ画像やCSSが何度も取得されるため、revisitの有無でアーカイブサイズが大きく変わる。Common Crawlの統計ではrevisitレコードが全体の相当部分を占めている。

metadata

関連付けたい補助情報を格納するレコード。例えば抽出したリンク一覧、文字コード検出結果、言語判定の結果など、クロール後の処理で生成した情報を元のレコードに紐づけて保存する用途で使う。

conversion / continuation

  • conversion — 元レコードを別の形式に変換した結果を格納する(例: HTMLからプレーンテキストへの変換結果)
  • continuation — 大きなペイロードを複数のレコードに分割したい場合に使う。巨大ファイル向けだが、実運用ではあまり見ない

実例: wgetでWARCを作る

WARCを作る最も手軽な方法はwget--warc-fileオプションを使うこと。以下のコマンドはhttps://example.com/を取得し、example.warc.gzとして保存する。

TARGET_URL="https://example.com/"
OUTPUT="example"

wget \
  --warc-file="${OUTPUT}" \
  --warc-cdx \
  --delete-after \
  "${TARGET_URL}"

オプションの意味は以下の通り。

  • --warc-file=NAMENAME.warc.gzを出力する(拡張子は自動で付く)。gzip圧縮はデフォルトで有効。無効化したい場合は--no-warc-compressionを追加する
  • --warc-cdx — 同時にCDXインデックスも作成する
  • --delete-after — 取得したファイル本体は残さず、WARCだけを残す

実行するとexample.warc.gzexample.cdxが生成される。未確認だが、Heritrixのような本格的なクローラーを使う場合も出力フォーマットはWARCなので、wgetで作ったファイルとそのまま同じツールで扱える。

実例: warcioで読む

Pythonのwarcioは、WARCの読み書きにもっとも広く使われているライブラリ。以下のスクリプトは先ほど生成したexample.warc.gzを読み、responseレコードだけを取り出してURLとステータスコードを表示する。

read_warc.py

from warcio.archiveiterator import ArchiveIterator

def main(path: str) -> None:
    with open(path, "rb") as f:
        for record in ArchiveIterator(f):
            if record.rec_type != "response":
                continue

            url = record.rec_headers.get_header("WARC-Target-URI")
            status = record.http_headers.get_statuscode()
            content_type = record.http_headers.get_header("Content-Type")

            print(f"{status} {content_type} {url}")

if __name__ == "__main__":
    main("example.warc.gz")

インストールと実行は以下の通り。

pip install warcio
python read_warc.py

出力例:

200 text/html; charset=UTF-8 https://example.com/

ArchiveIteratorはgzipストリームを透過的に扱い、レコード単位で反復する。各recordオブジェクトは2種類のヘッダーを持つ点に注意したい。

  • record.rec_headers — WARCレコードのヘッダー(WARC-Target-URIなど)
  • record.http_headers — コンテンツブロック内のHTTPヘッダー(Content-Typeなど)

先の「レコードはHTTPメッセージをラップしている」という構造が、そのままAPIに反映されている。

ペイロードを取り出す

レスポンスボディを取得するにはrecord.content_stream()を使う。これはgzipなどContent-Encodingを自動でデコードしてくれる。

body = record.content_stream().read()
print(body[:200].decode("utf-8", errors="replace"))

生のバイト列(デコード前)が欲しい場合はrecord.raw_streamを使う。

周辺ツールとエコシステム

WARCを扱うツールは用途別にいくつかの層に分かれる。

  • クローラーHeritrix (Internet Archive)、Browsertrix Crawler (Webrecorder、PlaywrightベースでJavaScriptレンダリングに対応)、wget
  • 読み書きライブラリ — Pythonのwarcio、Javaのjwarc、Rustのwarcクレート
  • リプレイ(再生)pywb — CDXインデックスを引いてWARCから該当レコードを返し、ブラウザ上で過去のWebサイトを閲覧できるようにする。Wayback Machineのオープンソース実装と位置付けられる
  • 大規模データセットCommon Crawlは月次で数十億ページのクロールをWARC形式で公開している。S3上にファイルが置かれており、CDXインデックス経由で必要なレコードだけをダウンロードできる

実務でWARCに触れる場面としては、(1) 自分でクロールしてアーカイブを作る、(2) Common Crawlのような既存アーカイブを解析する、の2つが主。どちらもwarcioのような読み取りライブラリが出発点になる。

まとめ

WARCはWebアーカイブのための地味だが実用的なフォーマットで、HTTPメッセージにもう1段メタデータを被せて連結しただけと理解すると全体像がつかみやすい。レコード単位のgzipとCDXインデックスの組み合わせでランダムアクセスを実現している点、そしてrevisitによる重複排除で大規模クロールの現実的な運用を成り立たせている点が、フォーマットとしての巧さだと思う。

手元で試すにはwget --warc-filewarcioの組み合わせが最短ルート。大規模データが欲しければCommon Crawlを引いてみると、同じフォーマットでTB単位のアーカイブがどう構成されているかを直接観察できる。

参考資料

Create an issue on GitHub about this article

Read Next