こんにちは、WEBサービス開発グループの伊達です。今日はちょっとした小ネタです。
先日、WordPressで運用しているサイトをクローズするのでローカルで閲覧できるようにダウンロードしてほしいという依頼がありました。
この際に、ページングのある記事をうまく保存できない問題があったので、ちょっとした方法で回避しましたという話をします。
wgetでダウンロード
過去にもクローズするサイトを丸ごとダウンロードしたいという依頼を受けたことがあり、そんな時にはwgetを使って、
|
1 |
$ wget -k -m -p -E --restrict-file-name=windows [URL] |
としていました。それぞれのオプションの意味は以下の通りです。
|
1 2 3 4 5 |
-k, --convert-links HTML や CSS 中のリンクをローカルを指すように変更する -m, --mirror -N -r -l 0 --no-remove-listing の省略形 -p, --page-requisites HTML を表示するのに必要な全ての画像等も取得する -E, --adjust-extension HTML/CSS 文書は適切な拡張子で保存する --restrict-file-names=OS OS が許しているファイル名に制限する |
-m で再帰的にダウンロードし、-kと-pでローカルで閲覧が完結できるようにします。-Eで、HTML文書は必ず.htmlという拡張子で保存するようにします。また、コマンドはLinux上で実行しますが閲覧はWindowsですので、Windowsで使用できないファイル名を避けるために --restrict-file-name=windows を付けます。
問題発覚
今回も同じようにダウンロードして、zipで固めたファイルを渡したところ、
「ダウンロードできていない記事があります。ページングのある記事があったりなかったりします」
との連絡が。
よくよく確認してみますと、ページングのURL仕様が原因だとわかりました。
- 該当の記事のURL
http://example.org/blog/2011/01/1.html - 該当の記事の2ページ目のURL
http://example.org/blog/2011/01/1.html/2
わかりますでしょうか?
問題の詳細
wgetが上の記事をクローリングしますと、
http://example.org/blog/2011/01/1.htmlをクロールしてリンクを探す- 2ページ目である
http://example.org/blog/2011/01/1.html/2をクロールする http://example.org/blog/2011/01/1.html/2をexample.org/blog/2011/01/1.html/2.htmlとして保存する- そのために
example.org/blog/2011/01/1.html というディレクトリを作成する
- そのために
example.org/blog/2011/01/1.htmlというディレクトリがすでにあるため、http://example.org/blog/2011/01/1.htmlがexample.org/blog/2011/01/1.htmlとして保存できない……
という挙動になります(逆にディレクトリの作成できないケースもあり)。
解決案を考える
さて、この問題をどうやって解決するとよいでしょうか。私はwgetのmanpageを眺めながら以下のような案を考えました。
- wgetのオプションにHTMLファイルの拡張子を
.htmlではなく、.htmにするものはないか- (これができれば、ディレクトリは
1.html、記事本体は1.htmと別の名前になります) - →
-Eは.html固定だった
- (これができれば、ディレクトリは
- wgetのオプションにファイルの保存時にコマンド実行を挟むようなコールバックがないか
- → さすがにない
- 1度wgetした後に該当の記事のディレクトリ名を変更し、影響を受けるHTMLをsedを使って修正、その後にもう一度wget
- → サイト全体のダウンロードに1時間以上かかるため、試行錯誤は避けたい
簡単に解決できる方法はなさそうです。
いえ、本当にそうでしょうか。
最初にあたりを付けていた-EはHTMLファイルの拡張子を .htmlに強制するオプションです。
コマンドラインから拡張子を指定する方法はありませんが、ソースコードにはその処理が書かれているはずです。
処理を少し変更すれば、拡張子を.htmにしてファイル名とディレクトリ名の重複を避けられるのでは。
アドホックなworkaround
ということで、
- -Eオプション指定時の挙動を修正して、HTMLファイルは .htm で保存されるようにする
ことができないか試してみることにしました。
コードを調べる
まずはソースコードをダウンロードして展開します。
|
1 2 3 |
$ wget https://ftp.gnu.org/gnu/wget/wget-1.19.tar.gz $ tar fxz wget-1.19.tar.gz $ cd wget-1.19/src |
-Eオプションの挙動を素直に実装しているなら.htmlという文字列がソースコード上に登場するだろうとあたりを付けてgrepします。
|
1 2 3 4 5 6 |
$ grep -n -r -F ".html" . ... ./http.c:3772: tack on ".html". */ ./http.c:3774: ensure_extension (hs, ".html", dt); ./http.c:5086: /* Resize the local file, allowing for ".html" preceded by ./http.c:5092: exists, tack on ".NUMBER.html" instead. */ |
様々なファイルでマッチしますが、ensure_extensionという関数名と文字列".html"の存在から、http.cの3774行目がどうも怪しい!
前後のコードを見てみるとコメンドで-Eオプションについて言及しており当たりのようです(実際には、もう少しあちこち調べましたが……)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
if (opt.adjust_extension) { if (*dt & TEXTHTML) /* -E / --adjust-extension / adjust_extension = on was specified, and this is a text/html file. If some case-insensitive variation on ".htm[l]" isn't already the file's suffix, tack on ".html". */ { ensure_extension (hs, ".html", dt); } else if (*dt & TEXTCSS) { ensure_extension (hs, ".css", dt); } } |
修正してビルド
|
1 2 3 4 5 6 7 8 9 10 11 |
--- wget-1.19/src/http.c.orig 2017-02-03 20:24:12.000000000 +0900 +++ wget-1.19/src/http.c 2017-09-19 10:30:36.790358624 +0900 @@ -3771,7 +3771,7 @@ variation on ".htm[l]" isn't already the file's suffix, tack on ".html". */ { - ensure_extension (hs, ".html", dt); + ensure_extension (hs, ".htm", dt); } else if (*dt & TEXTCSS) { |
http.cを上記の通りに修正してからビルドします。
|
1 2 3 4 |
$ vi src/http.c $ ./configure --prefix=/home/date/wget $ make $ make install |
これで、/home/date/wget/binに改造したwgetコマンドがインストールされます。
結果
改造したwgetでダウンロードし直したところ、懸案の記事は以下の通りにダウンロードされるようなりました。
- 該当の記事
http://example.org/blog/2011/01/1.html→example.org/blog/2011/01/1.html.htm - 該当の記事の2ページ目
http://example.org/blog/2011/01/1.html/2→example.org/blog/2011/01/1.html/2.htm
拡張子が重複しているのが気になりますが、リンクはちゃんとされていますし、ローカルで閲覧ができるという要件は満たしていますから問題ないでしょう。
結論
ソースコードはエンジニアの強い味方です。


