前回、Python + openpyxlで、ブックやシートの保護・解除を試しました。
Python + openpyxlで、ブックやシートの保護・解除を試してみた - メモ的な思考的な
その際、openpyxlではExcelの読み取りパスワードを解除できませんでした。
ただ、読み取りパスワード設定済のExcelファイルを読み込みたいことがあったため、別のライブラリがないかを調べてみました。
なお、今回もパスワードは把握している前提です。パスワードのクラックではありません。
目次
環境
調査したライブラリ
xlwings
- リポジトリ
- ドキュメント
Web上に、xlwings
を使って読み取りパスワードを解除する事例がありました。
Load password protected Excel files into Pandas DataFrame - David Hamann
ただ、 wb = xw.Book(PATH)
にてExcelファイルを開く際に、読み取りパスワード入力ダイアログが表示されました。
手動でパスワード入力するのが手間なため、他のライブラリを探すことにしました。
pywin32
読み取りパスワードを解除するよう
excel = win32com.client.Dispatch('Excel.Application') book = excel.Workbooks.Open('対象のファイル', False, False, None, '読み取りパスワード') book.SaveAs('解除後のファイル', None, '') book.Close()
と実装したところ、読み取りパスワード入力ダイアログが表示されずに解除できました。
しかし、 pywin32
はWindows上でしか動作せず、Macでは利用できないため、他のライブラリを探すことにしました。
ちなみに、最近の pywin32
は、 pypiwin32
としてPyPIからインストールできるようです。
https://pypi.org/project/pypiwin32/
名前などが怪しいですが、メンテナーが pywin32
とほぼ同じなので、大丈夫な気がします。
https://pypi.org/project/pywin32/
msoffcrypto-tool
GitHubのREADMEに従い、Macのターミナルから実行したところ、読み取りパスワード入力ダイアログが表示されずにパスワードが解除されました。
README上ではライブラリとして使う方法も記載されていたため、 msoffcrypto-tool
を使うことにしました。
msoffcrypto-toolの実装
実装の流れは以下です。
# 対象のExcelを開く with file.open(mode='rb') as locked: # OfficeFileオブジェクトにする office_file = msoffcrypto.OfficeFile(locked) # パスワードを設定する office_file.load_key(password=PASSWORD) # 解除する(ファイルは上書き保存される) office_file.decrypt(unlocked)
これにより、Excelファイル(xlsx, xlsの両方)とも、読み取りパスワードを解除できました。
ただ、読み取りパスワードが設定されていないExcelファイルに対して実行すると、例外が発生しました。
xlsx
と xls
では、例外が発生する箇所が異なりました。
- xlsx
office_file = msoffcrypto.OfficeFile(locked)
のタイミング
- xls
office_file.load_key(password=PASSWORD)
のタイミング
なお、msoffcrypto.OfficeFileには、読み取りパスワードが設定されているかをチェックするメソッド is_encrypted()
があります。
ただし、 xlsx
形式では常に True
が返ってくる実装になっていることに注意が必要です。
https://github.com/nolze/msoffcrypto-tool/blob/v4.6.3/msoffcrypto/format/ooxml.py#L143
以上より、読み取りパスワードが設定されていないファイルでも動作するように修正してみました。
import msoffcrypto import pathlib BASE_DIR = pathlib.Path(__file__).resolve().parents[0] PASSWORD = '12345' UNLOCKED_FILE = BASE_DIR.joinpath('unlocked.xlsx') def unlock(): for file in BASE_DIR.iterdir(): # Excelファイルだけ対象 if not file.is_file() or file.suffix not in ['.xlsx', '.xls']: continue with file.open(mode='rb') as locked: # xlsxファイルの場合、読み取りパスワード無しのファイルは例外が発生する # is_encrypted()には以下の記載がある # # https://github.com/nolze/msoffcrypto-tool/blob/v4.6.3/msoffcrypto/format/ooxml.py#L143 # def is_encrypted(self): # # olefile cannot process non password protected ooxml files. # # Hence if it has reached here it must be password protected. # return True try: office_file = msoffcrypto.OfficeFile(locked) except OSError: if file.suffix == '.xlsx': continue raise # 読み取りパスワードが設定されているかをチェック(xlsxはチェックできないので、xls向け) if not office_file.is_encrypted(): continue # パスワードをセット # xlsでパスワードが設定されていない場合、load_key()時にエラーが出るため、事前にチェックが必要 # File "python3.6/site-packages/msoffcrypto/format/xls97.py", line 479, in load_key # # Skip to FilePass; TODO: Raise exception if not encrypted # num, size = workbook.skip_to(recordNameNum['FilePass']) # File "python3.6/site-packages/msoffcrypto/format/xls97.py", line 428, in skip_to # raise Exception("Record not found") # Exception: Record not found office_file.load_key(password=PASSWORD) # 読み取りパスワード解除後のファイルは、拡張子の前に '_unlocked' を付けて保存する unlocked_file = BASE_DIR.joinpath(f'{file.stem}_unlocked{file.suffix}') with unlocked_file.open(mode='wb') as unlocked: # パスワードを解除 office_file.decrypt(unlocked) if __name__ == '__main__': unlock()
ソースコード
GitHubに上げました。 msoffcrypto_tool
ディレクトリの中が今回のファイルです。
https://github.com/thinkAmi-sandbox/python_excel_libraries-sample