Excelには、ブックやシートを保護するための機能があります。
- 読み取りパスワード
- 書き込みパスワード
- ブックの保護
- シートの保護
それらをopnepyxlでやるにはどうしたら良いかを試した時のメモです。
なお、パスワードは把握している前提です。パスワードのクラックではありません。
また、公式ドキュメントだとこのあたりのことです。
Protection — openpyxl 2.5.12 documentation
目次
環境
読み取りパスワード・書き込みパスワードの設定は不可
openpyxlでは読み取りパスワードと書き込みパスワードは設定できないようです。
issueによると
because it is proprietary MS code not covered by the OOXML specification.
https://bitbucket.org/openpyxl/openpyxl/issues/193/can-i-open-password-protected-excel-files
とのことです。
ブックの保護
パスワード無しでブックの保護を行う
Workbookオブジェクトには security
属性があり、それに対して保護の設定を行うことで実現できます。
ブックの保護なしのファイルを読み込むと、 security
属性は None
です。
そのため、 openpyxl.workbook.protection.WorkbookProtection
オブジェクトを設定した上で、 lockStructure = True
とします。
# ブックを読み込み wb = _load(NO_PROTECTION_FILE) # ブックを保護 wb.security = WorkbookProtection() wb.security.lockStructure = True # 保存 _save(wb, f'No_1_PROTECT_book_using_{NO_PROTECTION_FILE}') # _load()と_save()は以下の関数(以降のソースコードも同様) # ディレクトリ名を付けると一行が長くなって見づらかったので、関数化した def _load(file_name): return openpyxl.load_workbook(BASE_FILE_DIR.joinpath(file_name)) def _save(workbook, file_name): workbook.save(RESULT_FILE_DIR.joinpath(file_name))
結果です。
パスワードありでブックを保護する
パスワード無しに加え、 wb.security.workbook_password = PASSWORD_FOR_BOOK
を追加します。
wb = _load(NO_PROTECTION_FILE) # ブックを保護 wb.security = WorkbookProtection() wb.security.lockStructure = True wb.security.workbook_password = PASSWORD_FOR_BOOK # 保存 _save(wb, f'No_2_PROTECT_book_using_{NO_PROTECTION_FILE}')
結果です。ブックの保護を解除しようとすると、パスワード入力が求められます。
シートの保護
パスワード無しでシートを保護する
シートオブジェクトの protection
属性にある enable()
メソッドを使います。
なお、公式ドキュメントではブックオブジェクトに対して設定するよう記載されていましたが、シートオブジェクトが正しそうです。
https://openpyxl.readthedocs.io/en/stable/protection.html#worksheet-protection
wb = _load(NO_PROTECTION_FILE) # 対象のワークシートオブジェクトを取得する ws = wb['Sheet1'] # パスワード無しで保護 ws.protection.enable() # 保存 _save(wb, f'No_3_PROTECT_sheet_without_password_{NO_PROTECTION_FILE}')
結果です。
パスワードありでシートを保護する
パスワードなしに加え、 ws.protection.password = PASSWORD_FOR_SHEET
を使います。
# 対象のワークシートオブジェクトを取得する ws = wb['Sheet1'] # パスワードをセット ws.protection.password = PASSWORD_FOR_SHEET # シートを保護 ws.protection.enable()
シートの保護時に「このシートのすべてのユーザーに許可する操作」を設定する
Excelでは、シートの保護時に このシートのすべてのユーザーに許可する操作
というチェックボックスがいくつかあります。
openpyxlでそれらを有効化する場合は以下となります。
# 対象のワークシートオブジェクトを取得する ws = wb['Sheet1'] # このシートのすべてのユーザーに許可する操作 ws.protection.objects = True # オブジェクトの編集 ws.protection.scenarios = True # シナリオの編集 ws.protection.formatCells = True # セルの書式設定 ws.protection.formatColumns = True # 列の書式設定 ws.protection.formatRows = True # 行の書式設定 ws.protection.insertColumns = True # 列の挿入 ws.protection.insertRows = True # 行の挿入 ws.protection.insertHyperlinks = True # ハイパーリンクの挿入 ws.protection.deleteColumns = True # 列の削除 ws.protection.deleteRows = True # 行の削除 ws.protection.selectLockedCells = True # ロックされたセルの選択 ws.protection.selectUnlockedCells = True # ロックされていないセルの選択 ws.protection.sort = True # 並べ替え ws.protection.autoFilter = True # フィルター ws.protection.pivotTables = True # ピボットテーブルレポート # パスワード無しで保護 ws.protection.enable()
セルをロックしないで、シートを保護する
Excelのデフォルトでは、シートを保護すると全セルにロックがかかり、セルへの入力ができなくなります。
一部のセルのみ入力可能にしてシートを保護したい場合、事前にセルのロックを解除します。
openpyxlでは、以下のようにします。
from openpyxl.styles import Protection wb = _load(NO_PROTECTION_FILE) ws = wb['Sheet1'] # ロックを外したい(保護されない)セルを選ぶ unlock_cells = ws['A1:B3'] # 取得したデータや型を見ると、行ごとにタプルでセルが入っている print(f'type: ({type(unlock_cells)}), values: {unlock_cells}') # => type: (<class 'tuple'>), values: ((<Cell 'Sheet1'.A1>, <Cell 'Sheet1'.B1>), # (<Cell 'Sheet1'.A2>, <Cell 'Sheet1'.B2>), # (<Cell 'Sheet1'.A3>, <Cell 'Sheet1'.B3>)) # chain.from_iterable()でネストタプルを平坦にしてから処理 (使ってみたかっただけ) # 普通は for の2重ループで良いのかな for cell in chain.from_iterable(unlock_cells): # 念のための確認 print(f'type: ({type(cell)}), values: {cell}') # => type: (<class 'openpyxl.cell.cell.Cell'>), values: <Cell 'Sheet1'.A1> # ロックを解除 cell.protection = Protection(locked=False) # シートを保護 ws.protection.enable()
結果です。
ロックされたセルの場合、入力しようとすると、以下のようにメッセージが表示されます。
一方、ロックされていないセルの場合、入力が可能です。
ブックの保護を解除
ブックの保護時とは逆で、 wb.security.lockStructure = False
とします。
また、パスワード付きブックの保護の場合は、 wb.security.workbook_password = PASSWORD_FOR_BOOK
を設定します。
wb = _load(BOOK_PROTECTION_FILE) # 保護したときのパスワードをセット wb.security.workbook_password = PASSWORD_FOR_BOOK # ブックの保護を解除 # wb.security.lock_structureでも良い:Aliasが設定されている wb.security.lockStructure = False _save(wb, f'No_6_UNPROTECT_{BOOK_PROTECTION_FILE}')
結果です。ブックの保護が解除されています。
シートの保護を解除
シートの保護とは別のメソッド ws.protection.disable()
を使います。
また、パスワードで保護している場合は、 ws.protection.password = PASSWORD_FOR_SHEET
で設定する必要があります。
wb = _load(SHEET_PROTECTION_WITH_PASSWORD_FILE) ws = wb['Sheet1'] # シートを保護したときのパスワードをセット ws.protection.password = PASSWORD_FOR_SHEET # シートの保護を解除 ws.protection.disable() _save(wb, f'No_8_UNPROTECT_{SHEET_PROTECTION_WITH_PASSWORD_FILE}')
結果です。指定したシート Sheet1
のみシートの保護が解除されています。
ソースコード
GitHubに上げました。openpyxl/protection/
ディレクトリの中が今回のファイルです。
https://github.com/thinkAmi-sandbox/python_excel_libraries-sample