2009年11月3日火曜日

ファイルを一括ダウンロード&一括ファイル名編集

最近、ある会員用ページで200曲以上の mp3 データをダウンロードできる権利を手に入れました。ところが、まとめてダウンロードできずに一つ一つリンクが貼ってありました。しかも、そのダウンロードしたファイルが全てローマ字表記で、"hajime.mp3", "tsugi.mp3" といったように非常にわかりにくかった。

番号タイトルファイル名
1始めの曲hajime.mp3
2次の曲tsugi.mp3
3三番目の曲sanbanme.mp3

そこで、データを丸ごとダウンロードし、このダウンロードした mp3 をローマ字じゃなくて全部日本語の曲名のファイル名にしたい、というお話。例えば、「hajime.mp3」は「1-始めの曲.mp3」に、「tsugi.mp3」は「2-次の曲.mp3」に変換といった具合。もし数曲程度なら手動で整理した方が早いけれど、3桁もの数の曲データの手動ダウンロードと手動ファイル名修正はいかがなものかと。

とりあえず、ファイルの一括ダウンロードはすぐにできそうなので、ダウンロードツールを探しました。

あ…会員ページにログインする機能のないダウンロードツールは利用できないんだった。。そこで、Firefox のアドオンで、表示されているコンテンツを丸ごとダウンロードできる DownThemAll をインストールしてデータを一括ダウンロードしました。

ダウンロード設定。しばらく待つと、全部のファイルが無事ダウンロード完了。

さて、次はファイル名編集。幸い、HTML のテーブルの形でデータが整理されてたので、HTMLのソースから該当のテーブル部分を抜き出して、XML パーサにかけたらできそうですね。HTML の元のソースから table タグを見つけて抜き出し、拡張子 XML で保存。

ブラウザで開いてみると…構文エラーが発生。

エラー: invalid attribute value
<td width=5%>

…そうか、XML の構文はかなり厳格で、属性値にダブルクォーテーションがないとか、閉じ括弧があらゆるタグについてないとエラーになるとか、色々あったんですね。HTML 構文はそのあたり適当でもブラウザが適宜解釈してくれるんだけど、XML ではそうは行かない。HTML を XML に変換するのに躓くのは想定外というか、そういうことがあったって忘れてた。

そこで、このあたりを修正できるツールを探していると、Tidy というツールを発見。XML ドキュメントのエラーを探して自動で修正してくれるツールみたいです。早速 Windows 版をインストールして試してみる。

お…文字化けで肝心の日本語を変換できず…。あれこれ他のページを探してみるとこのページを発見!

http://html.idena.jp/program/index.shtml

このツールを使うと、無事に XML 構文が修正されて、XML として読み込むことができました。

ちなみに、Linux にても Tidy は利用できました。文字コードを UTF-8 にしておいて、次のコマンドを使いました。

% tidy -utf8 -asxml input_file > output_file

ただし、この方法で生成した出力ファイルには1つ問題がありました。&nbsp; が存在した場合、これは XML で定義されていないためにこの後使う expat でエラーになるようです。あまり綺麗ではないのですが、結局ストリーミングエディタを使って無理やり矯正してみました。

% sed -e 's/&nbsp;/ /g' output_file2 > out.xml

さて、この XML を構文解析して、ファイル修正スクリプトを組む必要がありますね。XML パーサだったらどこにでもあるだろう。なんとなく、python で作る方法を調べる。xml.parsers.expat あたりが軽量で使いやすそうなことがわかったので、このあたりを使うことにした。

…ソースを載せようと思ったけど、よく考えたら会員じゃない人は試せないわけね。雰囲気だけ載せときます。

#!/usr/bin/python
# coding: utf-8

import sys
import os
import xml.parsers.expat

# XMLファイルをパース
def parseDocument(p, file):
    f = open(file, 'r')
    p.ParseFile(f) 
    f.close()

# 要素の開始(<td> とかのときに呼ばれる。attrs には td の属性値)
def start_element(name, attrs):
    # if name == 'td': とかの処理

# 要素の終了(</td> とかのときに呼ばれる)
def end_element(name):
    # if name == 'td': とかの処理

def char_data(data):
    # data に値が入る

# パーサの作成
p = xml.parsers.expat.ParserCreate()

# ハンドラの設定
p.StartElementHandler = start_element
p.EndElementHandler = end_element
p.CharacterDataHandler = char_data

# table.xmlをパース
parseDocument(p, 'table_new.xml')

ここまで結構長かった。。

0 件のコメント:

コメントを投稿