サイトをそのままローカルに保存する方法(wget を用いる)

一番最初に注意点を1つ

保存したサイトが外部CDNなどを利用している場合は、wgetしたファイルをローカルファイルシステム上ではなく、Webサーバ経由で閲覧しないと部分的に正しく表示されません。file:// としてWebブラウザで開いた場合は正しく動作しないからです。

å‹•æ©Ÿ

方法

wget を用います。オプションがやたらめったら多いので、どのオプションを使うのかを検討します。末尾に 1.20.1 の --help で表示されるヘルプを添付しますので(長いですが)ご参照ください。

追記結論: 結局、こちらのコマンドのほうが良さそう

  • もろもろハマった結果、@suin さんが示しているコマンド(をベースにして)が手っ取り早そうです……
    • 取得漏れも見た感じなかったので……
    • で、できたコマンドが次のとおりです
$ wget --mirror --page-requisites --span-hosts --show-progress --no-parent --convert-links --adjust-extension --execute robots=off --verbose --output-file=wget_log.log --tries=2 --no-if-modified-since --random-wait --waitretry=5 --backup-converted --domain=example.com https://foobar.example.com/

最初に出した結論

結論ですが、私の場合は以下のようなコマンドになりました。

$ wget --output-file=wget_log.log --verbose --tries=2 --timestamping --no-if-modified-since --random-wait --waitretry=5 --adjust-extension --referer="http://www.geocities.co.jp/" --recursive --level=5 --convert-links --backup-converted --page-requisites "http://www.geocities.co.jp/FOOBAR/"

ひとつひとつ説明します。

--output-file

ログを明示的に残すためです。このオプションを指定すると標準出力にはログが出力されなくなります。

--verbose

ログをどれだけ詳細に出すかです。デフォルトのままでいいでしょう。明示的に指定しておきます。

--tries

リトライをする状況になった場合、最高で何回リトライするかを指定します。リトライしたことで正しく保存できることというのは現代ではあまりないと思いますが、念のためにつけています。

--timestamping と --no-if-modified-since

ファイルのタイムスタンプを見て、前回からの更新があるものだけを保存するためのオプションです。継続的に保存しないのならば実質的には不要です。

--random-wait

リクエスト間での待ち時間をランダムの値にします。固定にすることなどもできるので詳しくはヘルプをご覧下さい。

--waitretry

リトライをする状況になった場合、ここで指定された秒数だけ待ってからリトライをします。

--adjust-extension

拡張子を適切な名前に変更してくれます。

--referer

参照元を明示的に設定します。参照元が適切でないと弾かれるページや画像などに役に立ちます。実際にはほぼ意味はないと思いますが、念のためにつけた感じです。

--recursive と --level

リンクをたどっていくためのオプションであり、必須です。--level はたどる階層数を表します。0 で無限にたどることを示していますが、リンクのリンクをたどっていってしまい永久ループに陥る可能性があるので、ある程度控えめな値を指定したほうがいいでしょう*1。

ただ、控えめすぎると希望のコンテンツにたどり着けなくなることもあります。そして 0 だと --convert-links が正しく処理されない現象を確認しているので、無限ループ防止の意味も含めて 0 は指定しないほうがよさそうです。

--convert-links

各リンクがローカルの相対パスに変換されます。必須です。

--backup-converted

このオプションは最強だと思います。--convert-links を用いると、各リンクがローカル内での相対パスに変換されますが、このオプションを付与することにより変換前のファイルも保存してくれ、万が一のときの保険として有用です。

--page-requisites

外部サイトの画像を含めた、Webページ内の画像を取得します。必須でしょう。

補足

  • 昔のサイトに付きものの「BBS」はすでに存在しないかSPAMだらけのことがおおいですし、無限ループに陥りやすいので、--exclude-directories=cgi,cgi-bin などを指定するのがよいです
    • 妙に時間がかかっているようならログを見てこの点を疑ってみましょう
  • 昔のサイトに付きものの「フレーム」を使われていると結構大変で、 -follow-tags=frame オプションを対象ページごとに実行する必要があります
    • そこからさらに掘っていくためにコマンドの実行が必要になる場合もあります
  • 昔のサイトに付きものの ~ (チルダ)込みの URL が正しく取得できない場合は --no-iri をつけると改善する場合があります
  • --mirror オプションは後述のとおり、--timestamping --recursive --level=0 --no-remove-listing と同値です
  • とあるWebページ(サイト)のHTMLのタグがすべて大文字で書かれていた(<A href>、<IMG src>、<P> など)のですが、それだとリンクを拾ってくれなかったのでそういうときはお手上げっぽいです*2。wget を内部的に用いている他のアプリケーションでも同じくお手上げでしょう。
    • →その後、タグの大文字が原因で無いことが分かりましたが、ときに wget だと取得漏れがあることは認識していおいたほうが良さそうです
  • ダウンロードの URL を指定する際には極力ファイル名までを含めたほうがいいです(index.html などまで)
    • 状況*3によっては --convert-links が正常に動作しませんでした
      • --span-hosts と --level=2 と --no-parent と -Dfoo.example.com,bar.example.com を付与したらうまくいきましたが、確定的ではないです
      • -D オプションはサブドメインを指定してもだめ?(ドメインのみが対象?)

参考

wget --help (1.20.1)

$ wget --help
GNU Wget 1.20.1, 非対話的ネットワーク転送ソフト
使い方: wget [オプション]... [URL]...

長いオプションで不可欠な引数は短いオプションでも不可欠です。

スタートアップ:
  -V,  --version                   バージョン情報を表示して終了する
  -h,  --help                      このヘルプを表示する
  -b,  --background                スタート後にバックグラウンドに移行する
  -e,  --execute=COMMAND           `.wgetrc'形式のコマンドを実行する

ログと入力ファイル:
  -o,  --output-file=FILE          ログを FILE に出力する
  -a,  --append-output=FILE        メッセージを FILE に追記する
  -q,  --quiet                     何も出力しない
  -v,  --verbose                   冗長な出力をする (デフォルト)
  -nv, --no-verbose                冗長ではなくする
       --report-speed=TYPE         帯域幅を TYPE で出力します。TYPE は 'bits' が指定できます。
  -i,  --input-file=FILE           FILE の中に指定された URL をダウンロードする
  -F,  --force-html                入力ファイルを HTML として扱う
  -B,  --base=URL                  HTML で入力されたファイル(-i -F)のリンクを
                                   指定した URL の相対 URL として扱う
       --config=FILE               設定ファイルを指定する
       --no-config                 設定ファイルを読みこまない
       --rejected-log=FILE         拒否された理由をログ FILE に保存する

ダウンロード:
  -t,  --tries=NUMBER              リトライ回数の上限を指定 (0 は無制限).
       --retry-connrefused         接続を拒否されてもリトライする
       --retry-on-http-error=ERRORS    コンマ区切りで指定したHTTPのエラーの場合リトライする
  -O,  --output-document=FILE      FILE に文書を書きこむ
  -nc, --no-clobber                存在しているファイルをダウンロードで上書きしない
       --no-netrc                  .netrc から認証情報を取得しない
  -c,  --continue                  部分的にダウンロードしたファイルの続きから始める
       --start-pos=OFFSET          OFFSET からダウンロードを開始する
       --progress=TYPE             進行表示ゲージの種類を TYPE に指定する
       --show-progress             どのモードでも進捗バーを表示する
  -N,  --timestamping              ローカルにあるファイルよりも新しいファイルだけ取得する
       --no-if-modified-since      タイムスタンプモードの時に、
                                     if-modified-since get リクエストを使わない
  --no-use-server-timestamps       ローカル側のファイルのタイムスタンプに
                                   サーバのものを使わない
  -S,  --server-response           サーバの応答を表示する
       --spider                    何もダウンロードしない
  -T,  --timeout=SECONDS           全てのタイムアウトを SECONDS 秒に設定する
       --dns-timeout=SECS          DNS 問い合わせのタイムアウトを SECS 秒に設定する
       --connect-timeout=SECS      接続タイムアウトを SECS 秒に設定する
       --read-timeout=SECS         読み込みタイムアウトを SECS 秒に設定する
  -w,  --wait=SECONDS              ダウンロード毎に SECONDS 秒待つ
       --waitretry=SECONDS         リトライ毎に 1〜SECONDS 秒待つ
       --random-wait               ダウンロード毎に 0.5*WAIT〜1.5*WAIT 秒待つ
       --no-proxy                  プロクシを使わない
  -Q,  --quota=NUMBER              ダウンロードするバイト数の上限を指定する
       --bind-address=ADDRESS      ローカルアドレスとして ADDRESS (ホスト名か IP) を使う
       --limit-rate=RATE           ダウンロード速度を RATE に制限する
       --no-dns-cache              DNS の問い合わせ結果をキャッシュしない
       --restrict-file-names=OS    OS が許しているファイル名に制限する
       --ignore-case               ファイル名/ディレクトリ名の比較で大文字小文字を無視する
  -4,  --inet4-only                IPv4 だけを使う
  -6,  --inet6-only                IPv6 だけを使う
       --prefer-family=FAMILY      指定したファミリ(IPv6, IPv4, none)で最初に接続する
       --user=USER                 ftp, http のユーザ名を指定する
       --password=PASS             ftp, http のパスワードを指定する
       --ask-password              パスワードを別途入力する
       --use-askpass=COMMAND       認証情報(ユーザ名とパスワード)を取得するハンドラを指定します。
                                     COMMAND が指定されない場合は、
                                     環境変数 WGET_ASKPASS か SSH_ASKPASS が
                                     使われます。
       --no-iri                    IRI サポートを使わない
       --local-encoding=ENC        指定した ENC を IRI のローカルエンコーディングにする
       --remote-encoding=ENC       指定した ENC をデフォルトのリモートエンコーディングにする
       --unlink                    上書きする前にファイルを削除する
       --xattr                     turn on storage of metadata in extended file attributes

ディレクトリ:
  -nd, --no-directories            ディレクトリを作らない
  -x,  --force-directories         ディレクトリを強制的に作る
  -nH, --no-host-directories       ホスト名のディレクトリを作らない
       --protocol-directories      プロトコル名のディレクトリを作る
  -P,  --directory-prefix=PREFIX   ファイルを PREFIX/ 以下に保存する
       --cut-dirs=NUMBER           リモートディレクトリ名の NUMBER 階層分を無視する

HTTP オプション:
       --http-user=USER            http ユーザ名として USER を使う
       --http-password=PASS        http パスワードとして PASS を使う
       --no-cache                  サーバがキャッシュしたデータを許可しない
       --default-page=NAME         デフォルトのページ名を NAME に変更します
                                   通常は `index.html' です
  -E,  --adjust-extension          HTML/CSS 文書は適切な拡張子で保存する
       --ignore-length             `Content-Length' ヘッダを無視する
       --header=STRING             送信するヘッダに STRING を追加する
       --compression=TYPE          圧縮アルゴリズムの指定: autoかgzipかnone(デフォルトはnone)
       --max-redirect              ページで許可する最大転送回数
       --proxy-user=USER           プロクシユーザ名として USER を使う
       --proxy-password=PASS       プロクシパスワードとして PASS を使う
       --referer=URL               Referer を URL に設定する
       --save-headers              HTTP のヘッダをファイルに保存する
  -U,  --user-agent=AGENT          User-Agent として Wget/VERSION ではなく AGENT を使う
       --no-http-keep-alive        HTTP の keep-alive (持続的接続) 機能を使わない
       --no-cookies                クッキーを使わない
       --load-cookies=FILE         クッキーを FILE から読みこむ
       --save-cookies=FILE         クッキーを FILE に保存する
       --keep-session-cookies      セッションだけで用いるクッキーを保持する
       --post-data=STRING          POST メソッドを用いて STRING を送信する
       --post-file=FILE            POST メソッドを用いて FILE の中味を送信する
       --method=HTTPMethod         "HTTPMethod" をヘッダのメソッドとして使います
       --body-data=STRING          STRING をデータとして送る。--method を指定してください。
       --body-file=FILE            ファイルの中味を送る。--method を指定してください。
       --content-disposition       Content-Disposition ヘッダがあれば
                                   ローカルのファイル名として用いる (実験的)
       --content-on-error          サーバエラー時に受信した内容を出力する
       --auth-no-challenge         サーバからのチャレンジを待たずに、
                                   Basic認証の情報を送信します。

HTTPS (SSL/TLS) オプション:
       --secure-protocol=PR        セキュアプロトコルを選択する (auto, SSLv2, SSLv3, TLSv1, TLSv1_1, TLSv1_2, PFS)
       --https-only                安全な HTTPS のリンクだけたどる
       --no-check-certificate      サーバ証明書を検証しない
       --certificate=FILE          クライアント証明書として FILE を使う
       --certificate-type=TYPE     クライアント証明書の種類を TYPE (PEM, DER) に設定する
       --private-key=FILE          秘密鍵として FILE を使う
       --private-key-type=TYPE     秘密鍵の種類を TYPE (PEM, DER) に設定する
       --ca-certificate=FILE       CA 証明書として FILE を使う
       --ca-directory=DIR          CA のハッシュリストが保持されているディレクトリを指定する
       --crl-file=FILE             CRL ファイルを指定する
       --pinnedpubkey=FILE/HASHES  公開鍵 (PEM/DER) ファイル、もしくは、base64でエンコードした
                                   sha256ハッシュ値(sha256//で始まりセミコロン区切り)を指定して、
                                   相手を認証します。
       --random-file=FILE          SSL PRNG の初期化データに使うファイルを指定する
       --egd-file=FILE             EGD ソケットとして FILE を使う

       --ciphers=STR           GnuTLSの優先度かOpenSSLの暗号リストを直接指定する
                                   注意して使ってください。--secure-protocol を上書きします。
                                   フォーマットや文法は SSL/TLS 実装に依存します。
HSTS オプション:
       --no-hsts                   HSTS を使わない
       --hsts-file                 HSTS データベースのパス (デフォルトを上書き)

FTP オプション:
       --ftp-user=USER             ftp ユーザとして USER を使う
       --ftp-password=PASS         ftp パスワードとして PASS を使う
       --no-remove-listing         `.listing' ファイルを削除しない
       --no-glob                   FTP ファイル名のグロブを無効にする
       --no-passive-ftp            "passive" 転送モードを使わない
       --preserve-permissions      リモートのファイルパーミッションを保存する
       --retr-symlinks             再帰取得中に、シンボリックリンクでリンクされた先のファイルを取得する

FTPS オプション:
       --ftps-implicit                 implicit FTPS を使う (デフォルトポートは 990)
       --ftps-resume-ssl               制御接続で開始した SSL/TLS セッションを
                                         データ接続で再開する
       --ftps-clear-data-connection    制御チャネルだけ暗号化する(データは平文になる)
       --ftps-fallback-to-ftp          サーバが FTPS に対応していない場合は FTP にする
WARC オプション:
       --warc-file=FILENAME        リクエスト/レスポンスデータを .warc.gz ファイルに保存する
       --warc-header=STRING        warcinfo record に STRING を追加する
       --warc-max-size=NUMBER      WARC ファイルのサイズの最大値を NUMBER に設定する
       --warc-cdx                  CDX インデックスファイルを書く
       --warc-dedup=FILENAME       指定した CDX ファイルに載っている record は保存しない
       --no-warc-compression       WARC ファイルを GZIP で圧縮しない
       --no-warc-digests           SHA1 ダイジェストを計算しない
       --no-warc-keep-log          WARC record にログファイルを保存しない
       --warc-tempdir=DIRECTORY    WARC 書込時の一時ファイルを置くディレクトリを指定する

再帰ダウンロード:
  -r,  --recursive                 再帰ダウンロードを行う
  -l,  --level=NUMBER              再帰時の階層の最大の深さを NUMBER に設定する (0 で無制限)
       --delete-after              ダウンロード終了後、ダウンロードしたファイルを削除する
  -k,  --convert-links             HTML や CSS 中のリンクをローカルを指すように変更する
       --convert-file-only         URLのファイル名部分だけ変換する (いわゆるbasename)
  --backups=N                      ファイルに書きこむ時に N ファイルのバックアップをローテーションさせる
  -K,  --backup-converted          リンク変換前のファイルを .orig として保存する
  -m,  --mirror                    -N -r -l 0 --no-remove-listing の省略形
  -p,  --page-requisites           HTML を表示するのに必要な全ての画像等も取得する
       --strict-comments           HTML 中のコメントの処理を厳密にする

再帰ダウンロード時のフィルタ:
  -A,  --accept=LIST               ダウンロードする拡張子をコンマ区切りで指定する
  -R,  --reject=LIST               ダウンロードしない拡張子をコンマ区切りで指定する
       --accept-regex=REGEX        許容する URL の正規表現を指定する
       --reject-regex=REGEX        拒否する URL の正規表現を指定する
       --regex-type=TYPE           正規表現のタイプ (posix)
  -D,  --domains=LIST              ダウンロードするドメインをコンマ区切りで指定する
       --exclude-domains=LIST      ダウンロードしないドメインをコンマ区切りで指定する
       --follow-ftp                HTML 文書中の FTP リンクも取得対象にする
       --follow-tags=LIST          取得対象にするタグ名をコンマ区切りで指定する
       --ignore-tags=LIST          取得対象にしないタグ名をコンマ区切りで指定する
  -H,  --span-hosts                再帰中に別のホストもダウンロード対象にする
  -L,  --relative                  相対リンクだけ取得対象にする
  -I,  --include-directories=LIST  取得対象にするディレクトリを指定する
  --trust-server-names             ファイル名としてリダイレクト先のURLの最後の部分を使う
  -X,  --exclude-directories=LIST  取得対象にしないディレクトリを指定する
  -np, --no-parent                 親ディレクトリを取得対象にしない

バグ報告や質問、議論は<[email protected]>へ
それと(あるいはまたは) https://savannah.gnu.org/bugs/?func=additem&group=wget に登録してください。

対象サイトの数が 10 とか 20 とかになってツラさを感じたのでシュッとコードを書いた

  • geo_downloader.rb
class GeoDownloader
  def initialize(uri, filename_based_on_uri)
    @uri = uri
    @filename_based_on_uri = filename_based_on_uri
  end

  def execute
    command = %Q(mkdir #{@filename_based_on_uri} && cd #{@filename_based_on_uri} && wget --output-file=#{@filename_based_on_uri}.log --verbose --tries=2 --timestamping --no-if-modified-since --random-wait --waitretry=5 --adjust-extension --referer="http://www.geocities.co.jp/" --recursive --level=5 --convert-links --backup-converted --page-requisites "#{@uri}")

    `#{command}`
    puts "#{@uri}: Done!"
  end
end
  • app.rb
require './geo_downloader'

GeoDownloader.new('http://www.geocities.co.jp/FOO-BAR/12345/', 'FOO-BAR_12345').execute

上記の例の場合で実行すると、実行したディレクトリ配下に FOO-BAR_12345 が作られて、その中に入ってから wget します。wget の対象は http://www.geocities.co.jp/FOO-BAR/12345/ です。

コンストラクタに渡す引数をいい感じに配列なりハッシュなりに詰め込めばよいかと思います。

*1:「5」あたり

*2:ホームページビルダー製でした

*3:ジオシティーズで複数のジオシティーズのサイトにまたがったコンテンツがあった場合