Information High

勉強に使ったり、仕事で詰まったIT技術の備忘録

Pythonでrarファイルを解凍する

はじめに

rarfileのパッケージを使ってPythonでrarファイルを展開する方法の紹介です。
いろいろ嵌ったので、自分の備忘録も含んでいます。

ソースコード

とりあえず、結論としてはこんな感じで動きます。
extract(), extractall()は引数に展開先フォルダを指定できるので、必要に応じて設定してください。

import rarfile

# Set to full path of unrar.exe if it is not in PATH
# rarfile.pyでは unrar.exe があることを前提に動くので、
# パスを切っていなければパラメーターとして設定する。
rarfile.UNRAR_TOOL = r"D:\Unrar\Unrar.exe"

# Set to '\\' to be more compatible with old rarfile
rarfile.PATH_SEP = '/'

rf = rarfile.RarFile('D:\\Work\\TEST.rar')
# rarファイルを展開する
rf.extractall()

# ファイル情報を取得する場合
for f in rf.infolist():

    # rarファイルの内容を出力
    print(f.filename, f.file_size)

    # 1ファイルずつ展開
    # ただし、ディレクトリを展開すると、フォルダ構成がおかしくなるので、
    # 必要に応じて展開をスキップするようにすること。
    rf.extract(f.filename)

嵌ったこと(その他メモ)

その1. rarfileパッケージのインストール

rarfule.pyを使うためには、パッケージのインストールが必要です。以下のコマンドでインストールしてください。

pip install rarfile

そうすると、以下のようなメッセージが表示されてインストールが完了します。

Downloading https://files.pythonhosted.org/packages/de/a4/8b4abc72310da6fa53b6de8de1019e
100% |████████████████████████████████| 112kB 1.4MB/s
Building wheels for collected packages: rarfile
Running setup.py bdist_wheel for rarfile ... done
Stored in directory: C:\Users\XXXXX\AppData\Local\pip\Cache\wheels\dc\84\da\8aff50941f5
Successfully built rarfile
Installing collected packages: rarfile
Successfully installed rarfile-3.0

その2. unrar.exeのインストール

rarfileパッケージはファイルの展開時にunrar.exeがあることを前提に動作します。
unrar.exeへのパスを切っていない場合はパスを切るか、"rarfile.UNRAR_TOOL"パラメータでunrar.exeへのフルパスを定義する必要があります。
そもそも 「unrar.exeをインストールしていないよ」という方は以下からダウンロードしてください。
www.rarlab.com
・左のメニュー [ RAR ] - [ extras ] から [ UnRAR for Windows ] をクリックするとダウンロードできます。
・unrarw32.exe は自己解凍形式の圧縮形式のファイルなので、ダブルクリックして展開してください。unrar.exeが展開されます。

その3. rarfile.pyが動かない

今回一番嵌ったことです。本記事を書いている2019/2/19時点では、unrar.exeへのパスを定義してもunrar.exeの実行でエラーが発生します。
rarfile.pyの中でunrar.exeのコマンドラインを生成しているのですが、そのロジックに誤りがありそうです。
具体的には、860行目あたりの_extract()メソッドが以下のようになっているのですが、

    # call unrar to extract a file
    def _extract(self, fnlist, path=None, psw=None):
        cmd = [UNRAR_TOOL] + list(EXTRACT_ARGS)
        # pasoword
        psw = psw or self._password
        add_password_arg(cmd, psw)
        cmd.append('--')

こうしないと動きません。

    # call unrar to extract a file
    def _extract(self, fnlist, path=None, psw=None):
        #EXTRACT_ARGS = ('x', '-y', '-idq')
        cmd = [UNRAR_TOOL]
        cmd.append('x')
        cmd.append('-y')
        cmd.append('-idq')
        # pasoword
        psw = psw or self._password
        add_password_arg(cmd, psw)
        cmd.append('--')

listでEXTRACT_ARGSを設定しますが、Popen()にわたるときにcmdのリストの中にEXTRACT_ARGSのリストが設定され、リストが二重になっている状態と思われます。
そのため、python側でunrar.exeに渡す引数を上手く設定できていないようです。
回避策として、cmdにひとつづつ引数を渡すように変更しています。もう少し良い方法があるかもしれませんが、個人利用なので暫定対応としています。

・・・本当に最後の問題には時間をとられました・・・