Pythonスクリプトを PyInstaller で実行ファイル(.exe)に変換する。
このとき、
- ファイルA … 変更がないためビルド時にexeに含めたい。
- ファイルB … 学習モデルなど、更新がありうるため外部から読み込みたい。
つまり、「一部のファイルだけ外部参照にする」方法のメモ。
PyInstallerとは?
PyInstallerは、Pythonスクリプトとその依存関係をすべて含んだ実行可能ファイルを作成するライブラリ。生成されたexeファイルは、Pythonがインストールされていない環境でも動作する。
pythonで作られたプログラムを動かす場合、python環境が必要になるが、カタギの人間のパソコンにはそういうモンが入ってはいないので、exeにしてやる必要がある。
PyInstallerの使い方インストール
pip install pyinstaller
基本的な使い方
1. 最もシンプルな例
pyinstaller main.py
この場合、dist/main/フォルダにexeファイルと依存関係が配置される。
2. 単一ファイル生成
pyinstaller --onefile main.py
dist/main.exeとして1つのファイルが生成される。
3. よく使用されるオプション
pyinstaller --onefile --noconsole --name myapp main.py
--onefile: 単一ファイル生成--noconsole: コンソールウィンドウを非表示--name: 出力ファイル名を指定
なお、pyで実行したものとビルドしたexeを実行すると違った動作をすることがよくある。
(特に、パス周りではトラブルが発生しがちである。)
EXE でよく使うオプション
| 項目 | 説明 |
|---|---|
name='main' |
出力ファイル名(例:main.exe) |
console=True |
コンソールを表示(GUIアプリなら False) |
upx=True |
UPXによる圧縮を有効化(軽量化) |
strip=False |
デバッグ情報を削除するかどうか |
debug=False |
デバッグモードを有効化するか |
Pythonアプリケーションが設定ファイル、データファイル、画像などの外部ファイルを使用している場合、そのままexe化してもファイルが見つからずエラーになってしまう。
プロジェクト構成例
myproject/
├── main.py
├── resource_utils.py
├── data/
│ ├── sample.txt # exe内蔵
│ ├── config.json # exe内蔵
│ └── user_settings.txt # exe外部
└── assets/
└── logo.png # exe内蔵
解決策1: --add-dataオプション
pyinstaller --onefile --add-data "config.json;." --add-data "data;data" main.py
- Windowsでは
;、Linux/macOSでは:で区切る "元のパス;exeでの配置先"の形式
解決策2: リソースパス取得関数
PyInstallerでビルドした場合とそうでない場合で、ファイルパスを適切に取得する関数を作成する。
import os
import sys
def get_resource_path(relative_path):
"""
PyInstallerでビルドされたexeファイル内のリソースファイルへのパスを取得する
Args:
relative_path (str): リソースファイルの相対パス
Returns:
str: リソースファイルの絶対パス
"""
try:
# PyInstallerでビルドされた場合、sys._MEIPASSが設定される
base_path = sys._MEIPASS
except AttributeError:
# 開発環境では現在のスクリプトのディレクトリを使用
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, relative_path)
使用例
import os
from resource_utils import get_resource_path
def main():
# 設定ファイルの読み込み
config_path = get_resource_path("config.json")
if os.path.exists(config_path):
with open(config_path, 'r') as f:
config = json.load(f)
print("設定ファイルを読み込みました")
else:
print("設定ファイルが見つかりません")
if __name__ == "__main__":
main()
specファイルによる詳細制御
specファイルとは
specファイル(Specification File)は、PyInstallerがexeを作成する際の詳細設定を記述したPythonスクリプト。複雑な設定や、特定のファイルの包含・除外を行う場合に使用する。
specファイルの生成
pyinstaller --name myapp main.py
これによりmyapp.specファイルが生成されます。
specファイルの構造
# -*- mode: python ; coding: utf-8 -*-
# ------------------------------------------------------------
# PyInstaller Spec: 必要最小限構成
# ------------------------------------------------------------
a = Analysis(
['main.py'], # 実行エントリーポイント
datas=[
('test_data.txt', '.'), # exe直下に配置
('data/sample.txt', 'data'), # data/sample.txt として同梱
('data/config.json', 'data'), # data/config.json を含める
# ('data/unsample.txt', 'data'), # 除外する場合はコメントアウト
('assets/logo.png', 'assets'), # assets/logo.png を含める
],
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='main_selective', # 出力exe名
console=True # コンソール表示(GUIならFalse)
)
specファイルでのビルド
pyinstaller myapp.spec
実際のところ、パスはどうなっているのか?
------------------------------
【内部リソースファイル(exe内)】
------------------------------
ファイル: data\sample.txt
実際のパス: C:\Users\[ユーザー名]\AppData\Local\Temp\_MEI233922\data\sample.txt
------------------------------
ファイル: data/config.json
実際のパス: C:\Users\[ユーザー名]\AppData\Local\Temp\_MEI233922\data\config.json
------------------------------
ファイル: assets\logo.png
実際のパス: C:\Users\[ユーザー名]\AppData\Local\Temp\_MEI233922\assets\logo.png
------------------------------
【外部リソースファイル(exeと同じ場所)】
------------------------------
ファイル: data\sample.txt
実際のパス: D:[実際の実行ファイルの位置]
パスはこんな感じになっている。
一時フォルダ(TempのMEIフォルダ)が肥大化してしまうことがある。