はじめに
この記事ではPythonでパスワード付きのExcelファイルを生成する方法を説明しています。
実際にはDjangoで利用しています。
openpyxlライブラリはExceファイルの読み書きができますが、パスワードはWorkbookやWorksheetにしか設定できません。
そのため、Excelファイルにパスワードを設定するために「secure-spreadsheet」を使います。
「secure-spreadsheet」はnpmでインストールするJavaScript製のコマンドラインプログラムでPython、PHP、Rubyで使えるようです。
「secure-spreadsheet」の利点はOSに依存しない、Microsoft Offceをインストールしなくても使えることです。
環境
pipenvの仮想環境を使っています。
- Debian bullseye 64bit - Python 3.9.8 - Django 4.0.0
Djangoのディレクトリ構造は以下の通りです。
/home/www/wsgi/proj ┣ -- .venv ┣ -- proj ┣ -- app ┣ -- npm ┣ -- templates ┣ -- static ┣ -- db.sqlite3 (以下、省略)
secure-spreadsheetのインストール
Djangoのプロジェクトディレクトリにnpmというディレクトリを作成してからインストールします。
ローカルにインストールするので「-g」オプションは付けません。
OSがDebianなのでユーザとグループはwww-dataです。
sudo apt update sudo apt install npm cd /home/www/wsgi/proj sudo mkdir npm cd npm sudo npm install secure-spreadsheet cd .. sudo chown -R www-data.www-data /home/www/wsgi/proj/npm
適当なExcelファイル(test.xlsx)を準備して、パスワードを「sss」として動作テストを行います。
生成した「output.xlsx」をパスワードを入力して開ければ成功です。
cd /home/www/wsgi/proj cat test.xlsx | ./npm/node_modules/.bin/secure-spreadsheet --password sss --input-format xlsx > output.xlsx
secure-spreadsheetをアップデートするには以下のコマンドを実行します。
cd /home/www/wsgi/proj sudo npm update secure-spreadsheet
Excelのパスワードに使える文字列や記号
Excelのパスワードに使える最大の文字数は15文字です。
また、使える文字種は以下のようです。
・文字: A~Z、a~z
・数字: 0~9
・記号: ! @ # $ % ^ & * – _ + = [ ] { } | \ : ‘ , . ? / ` ~ ” ( ) ; < >
Django(Python)で使ってみる
パスワード生成方法はPythonのドキュメントにサンプルがあります。

本格運用するなら、パスワードの生成方法は不完全です。
以下が参考になるかもしれません。

試したところ、コマンドラインで起動するせいか一部の記号が使えませんでした。
回避方法があれば良いのですが調査中です。
(いろいろ省略) # Excelファイルの書き込み from openpyxl import Workbook from openpyxl.styles.fonts import Font #from openpyxl.styles.colors import Color from openpyxl.styles.borders import Border, Side from openpyxl.styles import PatternFill # パスワード生成 import string import secrets import subprocess SECURE_SPREADSHEET = '/home/www/wsgi/proj/npm/node_modules/.bin/secure-spreadsheet' # settings.pyに記述すると汎用性が上がる #from django.conf import settings #SECURE_SPREADSHEET = getattr(settings, "SECURE_SPREADSHEET", None) (いろいろ省略) class SampleView(ListView): (いろいろ省略) # # Excelファイル作成 # 行と列の指定は「1」から始まる。 # def create_excelfile(self, fname): wb = Workbook() ws = wb.active # 印刷設定 # 用紙の向き:横 # 縦・横の印刷とも1ページにまとめる ws.page_setup.orientation = ws.ORIENTATION_LANDSCAPE ws.page_setup.fitToWidth = 1 ws.page_setup.fitToHeight = 1 ws.sheet_properties.pageSetUpPr.fitToPage = True ws.cell(row=1, column = 1).value = 'テストデータ' wb.save(fname) # # パスワード生成 # def pass_gen(self, size): chars = string.ascii_uppercase + string.ascii_lowercase + string.digits #chars += '!@#$%^&*()-_+=[]{}<>?' chars += '!#^*()-_+=[]{}' #return ''.join(secrets.choice(chars) for x in range(size)) # アルファべットと数字からなり、小文字を少なくとも1つと数字を少なくとも3つ含む。 while True: password = ''.join(secrets.choice(chars) for x in range(size)) if ( any(c.islower() for c in password ) and any(c.isupper() for c in password) and sum(c.isdigit() for c in password) >= 3): break return password # # Excelファイルにパスワードを設定 # def password_excelfile(self, fname, passwd): cmd = 'cat ' + str(fname) + ' | ' + str(SECURE_SPREADSHEET) + ' --password ' + '"' + passwd + '"' + ' --input-format xlsx' #cmd = 'cat ' + str(fname) + ' | ' + str(SECURE_SPREADSHEET) + ' --password ' + passwd + ' --input-format xlsx' result = subprocess.check_output(cmd, shell=True) with open(fname, 'wb') as f: f.write(result) # # Submitの処理 # def post(self, request, *args, **kwargs): fname_excel = '/home/www/wsgi/proj/media/test.xlsx' (いろいろ省略) # Excelファイルを生成する。 self.create_excelfile(fname_excel) # パスワードを12文字で生成するように指定。 self.password_excelfile(fname_excel, self.pass_gen(12)) (いろいろ省略)
Windowsの場合(win32com)
OSがWindowsの場合は、Excelがインストールされていればwin32comモジュールを使うとパスワードが設定できるようです。

その他
popenを使ってもできるとの情報がありますが試していません。

さいごに
生成したExcelにパスワードを設定することができました。
お役に立てば幸いです。
なお、「secure-spreadsheet」のサイトに掲載されているサンプルは、テキスト(CSVファイル)をbytes文字列として入力して、パスワード付きのExcelファイルに変換するものです。
Comments