@kyanny's blog

My thoughts, my life. Views/opinions are my own.

wget が HTML 内のリンクをたどるとき EUC-JP でパーセントエンコーディングされている URL を UTF-8 でパーセントエンコーディングし直してアクセスしてしまう問題

ややこしくて正しく説明するのが難しい(現象を正しく把握できてる自信が無い)。要するに、はてなダイアリーを wget で丸ごとダウンロードしようとしたら、 EUC-JP なせいで面倒なことになった、という話。

http://d.hatena.ne.jp/WizDiary/ のような URL を以下のコマンドで再帰的にダウンロードする。

wget \
  --recursive \
  --no-clobber \
  --page-requisites \
  --html-extension \
  --convert-links \
  --no-parent \
  --domains=d.hatena.ne.jp,d.st-hatena.com \
  --span-hosts \
http://d.hatena.ne.jp/WizDiary/

このページには http://d.hatena.ne.jp/WizDiary/searchdiary?word=%2A%5B%B9%F5%C5%C4%C1%EF%5D のようなリンクが含まれている。これは「黒田聡」という文字列を EUC-JP でエンコードしたものをパーセントエンコーディングしている。

irb(main):002:0> CGI.unescape("%2A%5B%B9%F5%C5%C4%C1%EF%5D")
=> "*[\xB9\xF5\xC5\xC4\xC1\xEF]"
irb(main):003:0> CGI.unescape("%2A%5B%B9%F5%C5%C4%C1%EF%5D").force_encoding('euc-jp').encode('utf-8')
=> "*[黒田聡]"

wget には EUC-JP を尊重したままリクエスト(ダウンロード)して欲しいが、困ったことにリクエストするとき http://d.hatena.ne.jp/WizDiary/searchdiary?word=*%5B%E9%BB%92%E7%94%B0%E8%81%A1%5D のような URL に勝手に変換してしまう。これは「黒田聡」という文字列を EUC-JP でエンコードしたものをパーセントエンコーディングしている。

irb(main):002:0> CGI.unescape("http://d.hatena.ne.jp/WizDiary/searchdiary?word=*%5B%E9%BB%92%E7%94%B0%E8%81%A1%5D")
=> "http://d.hatena.ne.jp/WizDiary/searchdiary?word=*[黒田聡]"

これによって、 wget が http://d.hatena.ne.jp/WizDiary/searchdiary?word=*%5B%E9%BB%92%E7%94%B0%E8%81%A1%5D をダウンロードすると、その HTML は大部分が EUC-JP だが一部に UTF-8 の文字列が含まれたファイルとして保存される。 URL 中のクエリストリングの部分が HTML 内の INPUT タグに埋め込まれるのだが、 URL 中にあるのは UTF-8 のパーセントエンコーディングなので、デコードされて UTF-8 文字列が埋め込まれる。このようなファイルを ruby や sed でテキスト処理しようとすると、エンコーディングがおかしくてエラーになり処理できない(wget の --convert-links オプションがうまく変換できなかったリンクを書き換えるフィルタ処理をしたいが、 nokogiri などが使えない)

wget には --remote-encoding --local-encoding --restrict-file-names などのオプションがあるが、これらの様々な指定を組み合わせても、この挙動を変える方法は発見できなかった。

wget のバグトラッカーを眺めたところ、 https://savannah.gnu.org/bugs/?50383 が近そうに思えたが、よくわからない。

思いつく回避策は、

  • wget の --spider オプションでクロールさせつつログを吐き、ログからダウンロード対象の URL を抜き出して curl などを使ってダウンロードする
  • 頑張って文字化けを手作業で、またはエンコーディングの問題をどうにか回避するプログラムを書いて直す。きれいなミラーを手に入れることは諦める

問題は、はてなダイアリーはまもなく終了するので、残り猶予時間が限られていること。どうしたもんか。