Pythonでexe化 PyInstallerを極める

2020-08-06

プログラミング言語Python(パイソン)に関する話です。

Python

.exeにしたい

Pythonで書いたスクリプトを.exeファイルにしたいという人はそこそこいるかと思います。

Pythonの場合は「作成したスクリプトを他のパソコンで動作させたい」と思った場合にも(他のプログラミング言語に比べれば)比較的楽に実現できるかと思います。

それでも作成したスクリプトを.exeにすることができれば、もっと楽に他のパソコンで動作させることができます。

また、.py形式で配布するとスクリプト自体が丸見えですので簡単に改変されてしまいますが、.exeにすれば(コピーは可能ですが)改変は難しくなります。

ここでは、パイソンで作成したスクリプトを「PyInstallerでexe化する」について詳しく記載します。PyInstaller以外の方法についても軽く触れています。

確認環境

  • Windows 10 Home(64bit)
  • Python 3.8.2(64bit)
  • PyInstaller 3.6

Pythonをexe化する方法と特徴

Pythonスクリプトをexe化する方法にはいくつかあり、その代表的なものと大雑把な特徴を以下にまとめます。

方法特徴
PyInstaller・比較的新しいバージョンのPythonに対応
・1本のexeにすることができる
・わりと簡単に(スクリプト1本だけでも)exeにできる
・作成したexeのファイルサイズが大きくなりがち
・作成したexeの起動が遅い
・Mac用に.appが作成できる
Py2exe・1本のexeにすることができない
・作成したexeの起動が比較的速い
cx_Freeze・1本のexeにすることができない
・作成したexeの起動が比較的速い
・Mac用に.appが作成できる

(ちなみに、.exeではありませんがMac用の.appを作成するためのPy2appというものもあるようです。)

個人的には以下の価値観となっています。

  • ファイルサイズはあまり気にしない
  • 起動時間はあまり気にしない
  • exeは1本にしたい

そしてPyInstallerの前に上記の価値観について少し補足させてください。

「ファイルサイズはあまり気にしない」

出来上がるexeのファイルサイズについて。

フロッピーディスクを使用していた時代であれば分かりますが、USBメモリで渡せるのであれば100MB程度であればまったく気にならないかと思います。

ただし、メールでは渡しづらいですね。

私がPyInstallerで作成したテストプログラムは15~20MBでした。

もしメールで送りたいとしたら、15MBでも厳しいです。

セキュリティ意識の向上なのでしょうか、最近はメールでもウェブでも.exe等プログラムの受け渡しがかなり難しくなっている気がします。

「起動時間はあまり気にしない」

Eclipseなどの使用経験があるからでしょうか、個人的には起動時間はあまり気にしません。起動してからの動作の方が大事だと考えています。

私がPyInstallerで作成したテストプログラムは起動までが10~20秒でした。

(wxPythonで作成したGUIプログラムについても起動後の動作のスピードは気になりませんでした。)

ちなみにEclipseでは毎回1~2分程度の起動時間だったと記憶しています。

「exe化の手間」

PyInstallerでは一番シンプルにいくなら以下だけでexe化することができます。

Py2exeやcx_Freezeでは「setup.py」というスクリプトを作成する必要はありますが、実はそんなに手間ではないと思います。

一度 理解・作成すれば使い回しができるわけですし。

そして、PyInstallerの「specファイル方式」に比べると簡単だったりします。

なので、手間という点ではどれも似たようなものかと思います。

「exeの1本化」

「exeにしたい」ということは大体の場合「プログラミングしない人に配布したい」ということかと思います。そして配布先の人のITレベルはまちまちです。

そんなとき「複数のファイルを配布する」ということになれば、階層になることもありますし複雑さが一気に増します。「どこのどのファイルをダブルクリックすればいいの?」となってしまうことが容易に想像できます。

ただし、

UPXなどのexeファイルを実行可能な形式で圧縮するソフトを使うと、1ファイルで配布できます。

ぱーくん plus idea

とのことです。

であれば、Py2exeもcx_Freezeが候補に入ってくる人もいそうですね。

私はまだUPXというものを使用したことがありませんが、できたとしても、もしこのUPXで手間がかかるようだと厳しいです。

そしてUPXで1本のファイルにした場合には、結局「展開してから実行」になるので起動時間は少し遅くなるようです。

あなたに合った方法を選びましょう

何を大事にするかは人それぞれかと思います。

まずはいろいろやってみるのがよいかと思います。

予想していなかった問題点が出てきて軌道修正が発生するかもしれません。

やる前に、exe化しようとしている方法が対応している「Pythonのバージョン」を確認してから始めましょう。

(Python関係について「対応バージョン」に関する情報が少ないと感じるのは私だけでしょうか。)

PyInstallerの準備

PyInstallser-Python対応バージョン

Pythonは3.5以上を使っていることがほとんどだと思いますので、PyInstallerは3.6を使うのがよいかと思います。(2020年08月)

PyInstaller VerPython Ver
PyInstaller 3.4サポート終了
PyInstaller 3.5不明…
PyInstaller 3.6Python 2.7, 3.5–3.7 ※1
PyInstaller 4.0Python 3.5以降 ※2
2020年08月時点

※1 当方の環境では、PyInstaller 3.6がPython 3.8.2の環境で利用できています。また、Python 2.7のサポートはPyInstaller 3.6で終了となります。

Python 3.8と3.9への(公式的な)対応はいつになるのでしょうか。

※2 対応バージョン「3.5以降」って… 2020年10月リリース予定のPython 3.9に対応済ってことなんでしょうね。

システムbit数

(32bitや64bitのことをまとめて何と言えばいいのでしょう。ここではシステムbit数とさせていただきます。)

32bitと64bitについて「システム」「プログラム」で考えると対応関係は以下のようになります。

32bit
プログラム
64bit
プログラム
32bitシステム×
64bitシステム

つまり64bitのプログラムは32bitのシステムで動かせませんが、それ以外は動作します。PyInstaller自体の話ではありませんが、一応基本について確認させていただきました。

で、PyInstallerで32bit/64bitどちらのプログラムを作るのか考えておきましょう。

自分のパソコンで動かすだけなら気にすることでもありませんが「配布」ということを考えると32bitで作るのが安全ではあります。ただし32bit版のサポートを切っているパッケージ・ライブラリもあるかもしれません。

時代の流れは64bitです。

また、32bit版で作成したexeはNortonによってウイルスとして誤判定される可能性もあります。(以下記事参照)

PyInstallerでは(当然とも言えますが)

  • 32bit版のPython/PyInstallerで作成したものは32bitプログラム
  • 64bit版のPython/PyInstallerで作成したものは64bitプログラム

になります。

開発しているパソコンの環境ではなく、exe化するときに使用するPython/PyInstallerのシステムbit数になります。

64bitのパソコンでは、32bit/64bit両方のプログラムが作成できます。

32bitのパソコンでは、64bitのプログラムは作成できません。

作成したexeのシステムbit数を確かめるには、64bit環境で、exeを実行した状態で[タスクマネージャー]-[プロセス]で目的のexeを探し、名前の後ろに(32 ビット)と表示されていれば32bitで、表示されていなければ64bitです。

作成したいexeのシステムbit数のPython/PyInstallerが必要となります。

PyInstallerのインストール

以下のようにインストールします。

Pythonが複数(バージョン、32bit、64bit)インストールされている場合は、以下のようにインストールします。

(↑は32bit版Python 3.8の例です。)

PyInstallerの使い方

PyInstallerでexe化する3つの方法

PyInstallerを使って.pyファイルを.exe化するには以下の3つの方法があります。

  1. .specファイルを使用しない方法
  2. .specファイルを使用する方法
  3. PythonスクリプトからPyInstallerを実行する方法

.specファイルを使用してもしなくてもできることは同じです。

最初は.specファイルを使用せずにやってみて、慣れてきたりオプション指定でコマンドが長くなったら.specファイルに移行するのがよいかと思います。

ただし、.specファイルを使用せずにコマンドライン実行すると、存在していた.specファイルが上書きされますのでバックアップを取る等、気をつけてください。

まずは簡単な「.specファイルを使用しない方法」について説明します。

.specファイルを使用しない方法

試しにexeファイルを作成してみましょう。

以下のコマンドラインを実行することで.exeファイルが作成されます。

ちなみに「pyinstaller」というのはpyinstaller.exeのことです。

pyinstaller.exeは、python.exe(本体)がある場所のScriptsフォルダ内にあります。

python.exe(本体)
Scripts(フォルダ)
└pyinstaller.exe

Pythonが複数(バージョン、32bit、64bit)インストールされている場合は、目的のPython以下にあるpyinstaller.exeを呼び出してください。

スクリプトの内容ですが、例えば

等、print()のみだとexeを実行してもコンソール画面が一瞬表示されたあとに閉じてしまうので内容が確認しづらいという問題があります。

例えば

などのように後ろに文字入力を待つ処理 input() 等を入れておくとprint()の内容が表示されていることを確認しやすくなります。

上記PyInstallerのコマンドを実行すると、

(PCスペック等にもよりますが)小規模プログラムであれば1~2分程度でexeファイルが完成します。結構かかりますね…

最後に

INFO: Building COLLECT COLLECT-00.toc completed successfully.

と表示されれば成功です。

exeは(今回は)C:\Users\user\dist\testScriptフォルダにできました。

「カレントディレクトリ\dist\スクリプト名」のフォルダ 以下にできるということです。

exe化したい.pyスクリプトの場所にcd(カレントディレクトリを移動)してからexe化するのが分かりやすいかと思います。

実行すると、スクリプトがあるフォルダとカレントディレクトリにそれぞれ多数のファイル・フォルダが作成されます。

カレントディレクトリには(今回の場合)、991のファイルと36のフォルダができました。容量は約40MBでした。

他のPCで実行させるには、カレントディレクトリにできたdist以下のファイル・フォルダをすべて持っていく必要があります。

その他には、カレントディレクトリに.specファイルが、.pyファイルのディレクトリに__pycache__\testScript.cpython-38.pyc的なものができます。

exe化による生成物

PyInstallerでC:\test\sample.pyをexe化した場合の生成物をまとめると

  • カレントディレクトリ\dist\sample\sample.exe(目的のexe)
  • カレントディレクトリ\dist\sample以下(exe実行に必要なその他ファイル)
  • カレントディレクトリ\build\sample以下(作業ファイル)
  • カレントディレクトリ\sample.spec(specファイル 後述)

上記になります。

(上でも書きましたが)個人的にはexe化したいスクリプトがあるフォルダにcdしてからexe化するのがよいかと思います。

で、このままだと「exeが1本」になっていませんので「--onefile」オプションをつける必要があります。

「--onefile」オプションをつけた場合、目的のexeは1階層上になり、以下の場所にできます。

  • カレントディレクトリ\dist\sample.exe(目的のexe)

(例として用いているスクリプト名がsample.pyだったり、testScript.pyだったり、統一されておらず申し訳ありません。)

PyInstallerの主なオプション

PyInstallerのコマンドライン実行でexe化する場合にはオプションが使用できます。

使用できる主なオプションは以下になります。

なお当ブログでは半角ハイフンを連続して表示できない(WordPressの)仕様となっています。そのため連続ハイフンをそれっぽく表示するっため連続ハイフンの変わりに連続em-dashというもので表示している箇所があります。連続ハイフン風に見えるものについてはコピペしないように注意してください。

クリーン
「--clean」

ビルドする前にPyInstallerキャッシュを消去し、一時ファイルを削除します。

リビルドみたいな感じですね。当方の環境では--cleanなしで17秒、--cleanありで47秒でした。参考までに。

上書き確認
「-y」「--confirm」

作業ディレクトリの上書き確認が不要な場合に指定します。

~ALL ITS CONTENTS will be REMOVED! Continue? (y/N)

などと聞かれたくない人は指定してください。

(なんか上書き確認されるときとされないときがあり、条件がよく分かりません…)

1ファイルにする
「-F」「--onefile」

1ファイルのバンドルされた実行可能ファイルを作成します。exe1本にしたい場合はこちらを使用してください。

不要画面を非表示
「-w」「--windowed」「--noconsole」

WindowsおよびMac OS X:標準I / Oのコンソールウィンドウを提供しません。Mac OS Xでは、これによりOS X .appバンドルのビルドもトリガーされます。Windowsでは、最初のスクリプトが「.pyw」ファイルの場合、このオプションが設定されます。このオプションは* NIXシステムでは無視されます。

作成したexeを実行して「なんだこの不要なコンソールウィンドウは?」と思ったらこのオプションを指定しましょう。GUIプログラムでは大体不要になるはずです。

アイコン設定
「-i=アイコン名」「--icon=アイコン名」

exeファイルに組み込むアイコンファイルを指定します。

ウィンドウタイトル左側のアイコンを設定するwxPythonのframe.SetIcon()などとは別物になります。

エクスプローラー上などで表示されるアイコンです。

サンプルコマンド

オプションは複数同時に使用できます。

サンプルコマンドとしては以下のようになります。

オプションは順不同です。

「--onefile」でexe化すると

当方の環境では、onefileオプションを使わなかったときには約40MB(991ファイル)でしたが、onefileオプションを使ったときには約16MB(もちろん1ファイル)となりました。

.specファイルを使用する方法

かなりのボリュームになってしまいましたので後日書きます。

★ここから執筆作業中★

★ここまで執筆作業中★

PythonスクリプトからPyInstallerを実行する方法

Pythonコード内からPyInstallerを実行する場合、__main__.run()関数を使用して、すべてのコマンドライン引数をリストとして渡します。たとえば、以下のようになります。

複数Pythonがインストールされている環境でこちらの方法を実行する場合、開発環境の「どのPyInstallerが動作するのか」把握しておきましょう。

このスクリプトで動作するのは、.pyを動かしているPythonに付随するPyInstallerではなく、デフォルトで起動するPythonに付随するPyInstallerです。

例えば、

  • Python 3.8(64bit)(デフォルト起動)
  • Python 3.7(32bit)

の2つがインストールされている場合、

PyInstaller.__main__.run()を記述したスクリプトをPython 3.7(32bit)で動作させた場合にも、PyInstaller.__main__.run()で動作するのはPython 3.8(64bit)に付随するPyInstallerです。

複雑にして良いことはないので、確固たる理由がないのであれば同じPythonで動作させるのがよいかと思います。

デフォルト起動するPythonを変更したい場合は、環境変数の順番を変更します。

複数スクリプトのexe化について

プログラムとして複数の.pyスクリプトを使用している場合もあるかと思います。

そのようなプログラムをexe化したい場合は、main関数が記載されている.pyファイルのみを指定してください。そこから使用されている.pyファイルは自動で読み込まれてexe化されます。

exeファイルの肥大化を避けるために

PyInstallerは必要なファイルをアーカイブするような機能ですが、スクリプト内でimportしているライブラリ(パッケージ)だけではなく環境にインストールされているライブラリをすべてアーカイブするような動作となっています。

その方が安全確実なのは分かりますが、そんな弱気で百害な仕様ってあります?

そのため、様々なライブラリをインストールしている環境ではexeが肥大化してしまうことになります。

exeの肥大化を防ぐためには2つの方法があります。

  1. exe化するときに無視するライブラリを指定する
  2. virtualenv等でPyInstaller用の仮想環境を用意する

1については「--exclude-module EXCLUDES」オプションでexeに含めない(無視する)ライブラリを指定することができます。大量にあると面倒ではありますが、もちろん複数回使用できます。(後日詳しく書きます。)

exe化後の__debug__やassert()

PyInstallerでexe化したときに、Pythonバイトコード最適化メカニズムがどのように機能するかに注意する必要があります。

Pythonで-Oを使用すると、__ debug__がFalseに設定され、アサートステートメントがバイトコードから削除されます。 -OOフラグはさらにdocstringを削除します。

これらのオプションをPyInstallerで作成するexeに反映させる場合、以下のようなコマンドで実現できます。

注意点としては、上記コマンドの「python」はフルパスでも構いませんが「PyInstaller」はそのままにしてください。最初の「python」から「PyInstaller」を自動で探してくれるようです。

もちろん「-F」などのオプションを付加することも可能です。

PyInstallerのデバッグ方法

「PyInstallerのデバッグ」ということで考えると、「exe作成時」「exe動作時」の2つがあるかと思います。それぞれのデバッグ方法は以下になります。

exe「作成時」のデバッグ

まずは、コンソールに表示されるWARNINGやERRORを確認しましょう。

見づらい場合はすべてをコピーして、エディタに貼り付けてから検索するのもいいでしょう。

表示するログのレベルを調整したい場合はexe作成時のオプションを以下のように指定してください。

<LEVEL>にはTRACE、DEBUG、INFO、WARN、ERROR、CRITICALのいずれかを指定してください。(デフォルト:INFO)

こちらで指定した<LEVEL>でログがコンソールに表示されます。(このオプションは、ログファイルを作成するオプションではありません)

何も書かない

が同じ、ということになります。

なお、このオプションとは関係ありませんがexe化が成功するとbuildフォルダ以下に「warn-testScript.txt」等のログが作成されます。

exe「動作時」のデバッグ

exe作成時のオプションを以下のように指定してください。

<option>にはall、imports、bootloader、noarchiveのいずれかを指定してください。

ちなみに「-w」「--windowed」「--noconsole」のオプションをつけると、大量のウインドウが開くことがありますので、デバッグ時には使用しない方がよいでしょう。

「--noconsole」を指定したときだけ動作がおかしい、という厄介な場合もありますが…

そのような場合には、パッケージのimportの場所をズラしたり、(できればtkinter以外で)メッセージを表示したり、自分でログを出力したりして追いかけていきましょう。

オプションについて

一部記載済みですが、PyInstallerのオプションについて記載します。

オプションは順不同で複数利用可能です。同じところに書いているのは同じ働きですのでどれを使用しても同じです。

オプションの中で「=」が必要だったり不要だったりするので気をつけてください。

クリーン
「--clean」

ビルドする前にPyInstallerキャッシュを消去し、一時ファイルを削除します。リビルドです。

上書き確認
「-y」「--noconfirm」

作業ディレクトリの上書き確認が不要な場合に指定します。

~ALL ITS CONTENTS will be REMOVED! Continue? (y/N)

などと聞かれたくない人は指定してください。

1ファイルにする
「-F」「--onefile」

exe1本にしたい場合はこちらを使用してください。

不要画面を非表示
「-w」「--windowed」「--noconsole」

作成したexeを実行して「なんだこの不要なコンソールウィンドウは?」と思ったらこのオプションを指定しましょう。GUIプログラムでは大体不要になるはずです。

よく使うにも関わらず、トラブルの元になりがちなオプションです。

アイコン設定
「-i=アイコン名」「--icon=アイコン名」

exeファイルに組み込むアイコンファイルを指定します。

ウィンドウタイトル左側のアイコンを設定するwxPythonのframe.SetIcon()などとは別物になります。

ファイルバージョン設定
「--version-file バージョン情報.txt」

生成するexeファイルに付加するバージョン情報を設定します。

「バージョン情報.txt」について、詳しくは以下記事を参照願います。

★他については、確認してから追記します。★

リバースエンジニアリング対策

作成したexeのスクリプトを読まれたくないという、逆コンパイル対策、デコンパイル対策、難読化について。

exe作成時に「--key」を付加することでスクリプトを暗号化することができます。Using PyInstallerを参照願います。

また、以下のブログも参考になるかと思います。

PyInstallerの注意点

PyInstallerにはいくつか注意点があります。

まずは再起動

基本と言えば基本なのですが、何か解決できない不可思議と思える問題が発生した場合にはパソコンを再起動してみましょう。

それで解決となる可能性もあります。

バージョンを確認

2020年08月現在で言うと最新のPyInstaller 3.6はPython 3.7まで対応しています。

例えば私の環境ではPython 3.8でも動作しているように見えますが、公式に対応が表明されている組み合わせではありません。

原因不明な問題が発生した場合は、Python 3.7等、公式に対応されていると言われる組み合わせまでPython側のバージョンを下げてみましょう。

PyWin32 or pywin32-ctypes

PyInstallerを使用するためには「PyWin32」または「pywin32-ctypes」というもののインストールが必要らしいです。

私の場合、特に何もせずに動作していたのですがある日、スクリプト内に

PyInstaller.__main__.run()

という記述をして、出来上がったexeを実行してみたときに

PyInstaller cannot check for assembly dependencies.
Please install pywin32-ctypes.

pip install pywin32-ctypes

と言われました。

↓のように言われる人の方が多いようですね。

PyInstaller cannot check for assembly dependencies.
Please install PyWin32 or pywin32-ctypes.

pip install PyWin32

言われた通りに必要なものをインストールすれば解決、だと思ったのですが…

私の場合は「Please install pywin32-ctypes.」と言われたのに、確認してみると

  • pywin32-ctypes → インストールされている
  • PyWin32 → インストールされていない

なので、「pywin32-ctypes」はインストールもupgradeもできなかったので「PyWin32」をインストールしてみましたが現象は改善せず。調査中です。

そもそも、個人的には「出来上がったexeを実行したとき」に言われたのが気になります。

普通の感覚では、「exeを作成するとき」に言ってほしいものです。

直接関係はないかもしれませんが一応、こういう記載があります。↓

PyInstaller runs in Windows 8 or newer (Windows 7 should work too, but is not supported). It can create graphical windowed apps (apps that do not need a command window).

PyInstaller requires two Python modules in a Windows system. It requires either the PyWin32 or pypiwin32 Python extension for Windows. If you install PyInstaller using pip, and PyWin32 is not already installed, pypiwin32 is automatically installed. PyInstaller also requires the pefile package.

The pip-Win package is recommended, but not required.

PyInstaller Requirements(Windows)

機械翻訳が理解しづらかったので、頑張って自分で訳してみると

PyInstallerはWindows 8以上(Windows 7でも動作するはずですがサポート外です)で動作します。グラフィカルな(コマンドラインが不要な)ウィンドウアプリを作成できます。
PyInstallerはWinodowsシステム上で2つのPythonモジュールを必要とします。Windows用のPython拡張であるPyWin32またはpypiwin32が必要となります。もしあなたがpipを使用してPyInstallerをインストールしていて、PyWin32がまだインストールされていないなら、pypiwin32は自動でインストールされます。PyInstallerにはpefileパッケージも必要です。

pip-Winパッケージを推奨しますが、必須ではありません。

訳はここまで。

「2つの」モジュールが必要らしいのですが、どれがその2つなのか、結局よく分かりませんね。

予想1

  • PyWin32◎
  • pypiwin32◎
  • pefile〇
  • pip-Win▲

予想2

  • PyWin32 OR pypiwin32◎
  • pefile◎
  • pip-Win▲

どっちなのでしょう。

英語でも日本語でも、人に伝えるというのは難しいですね。

論理的には全部インストールしておけばいいのですが、嫌な予感がします。

32bitはNortonでウィルスと誤判定

当方の環境で、PyInstallerで「32bit版」「--noconsole」でexeを作成した場合にNortonによって「Heur.AdvML.B」というウィルスとして誤検出・誤検知されてしまいました。

すべての環境で発生するかは分かりませんが、発生するなら現状で対応策はありません。

詳しくは当ブログ内の以下の記事をどうぞ。

そこそこアクセスがあるので同じように悩んでいる方はいるのだと思います。

パスについて

.pyスクリプトでは問題なかったのに、exeにすると動かない。ケース1

.exeにしたときにパスがうまく取得できない場合があります。

いつか詳しく書くかもしれませんが、ひとまず以下をお読みください。

subprocessがそのままでは動作しない

.pyスクリプトでは問題なかったのに、exeにすると動かない。ケース2

subprocessを使用するにはちょっとした工夫が必要ですので、使用する方は以下記事を読んでください。

tkinterと相性が悪い?

普通に使っている分には問題ないかもしれませんが、tkinterと相性が(なんとなく)悪い気がします。

詳しくは以下の記事を参照願います。

Anacondaと相性が悪い?

詳しくは以下の記事を参照願います。

そしてAnaconda環境でpandasをインストールするとexeが肥大化し200MBを超えてしまうようです。

【悲報】PyInstallerさん、300MBのexeファイルを吐き出すようになる

参考サイト

まだまだ書くべきことがあるので、どんどん追記していきます。

以上。

後記

最後までお読みくださりありがとうございます。

こんなに更新したのに、現在はほぼ誰も見ていないゴミカスクズ無価値ブログです。

よければ(はてな)ブックマークや拡散をお願いいたします。

更新の原動力として励ましが欲しいのです…<(_ _)>

当ブログ内お薦め記事

記事はここまでです。