Pythonスクリプトのexe化

## はじめに
先人に作ってもらったデータ変換用のPythonスクリプトを部署内で使用していたのだが、Python環境のないメンバーに展開するためにexe化することにした。 
ひとまずPyinstallerを試したのだがエラー出るし重いしで、Py2exeも試すことにした。  
結論から言うと、Excelの範囲で済むようなデータ処理は、マクロを組んだ方が…

## 前提
配布先はPython環境のない、プログラムになじみのないメンバーとする。(＝簡単にexeを起動するだけにしたい)
exe作成側の環境は以下の通り。

- OS：Windows10
- Docker：v20.10.6
- Python：3.9.5
- Pyinstaller：4.3
- Py2exe：0.10.4.0

## exe作成環境の準備
### Anaconda環境でのexe作成はやめたほうがよい
Anacondaで多数のライブラリをインストールした状態だと、あちこちのライブラリを取り込んで巨大化したexeが生成される。  
[【悲報】PyInstallerさん、300MBのexeファイルを吐き出すようになる](https://lets-hack.tech/programming/languages/python/pyinstaller-issue/)  
  
一度自分の現環境で試したら500MBを超えた。
![Inked無題の画像_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/76592fa6-928c-42d9-52b9-a76668df434d.jpeg)

ので、Anacondaを削除するか、クリーンなWindows環境を用意するべき。
(Anaconda全削除は避けたい…)

### Docker上のWindows環境を用意する
Docker上に、Python以外インストールされていない環境を用意し、随時必要なライブラリをインストールするのが無難。  
主な手順は以下の通り。  

- Docker Desktopのインストール(＋Windowsモードに切り替え)
- Pythonイメージの取得
- Docker上でpip、Pyinstaller、Py2exeをインストール
- exeを作成しながら、都度ライブラリを追加

Docker環境については[こちらの記事](https://qiita.com/ybsh2891/items/894ba00a738b768794ce)参照。


## Pyinstallerで作成
### インストール
Docker上で以下を実施。

```
pip install pyinstaller
```
### 実行
Pyinstallerを実行するフォルダにexe化したいPythonコードを集めておき、メインのコードに対してPyinstallerを実行する。

```
pyinstaller hogehoge.py --onefile --noconsole
```
--onefileで、一つのexeとして出力するよう指示する。ないとpy2exeのように大量のファイル＋起動用のexeの形式で出力されるので注意。(exe単体では動作しない)  
--noconsoleは、exe起動時にプロンプト画面を同時に開かないようにする。

### エラーが出る
```
Failed to execute script 
```
pip listでコードでimportしているライブラリが全てインストールされているか確認し、不足しているものをpipでインストールする。  
  
原因不明な場合、一応2回実行してみるのもいいかもしれない。
一度目失敗したが、pyinstaller実行後に.specファイルができた後だとなぜか成功したということがあったので。

参考：
[“Failed to execute script XX” エラー対処: PyinstallerでPygameをexe化するときの注意点](https://www.bioerrorlog.work/entry/pygame-pyinstaller-error)
[【自動化】Pythonコードをexeファイル化する](https://qiita.com/konitech913/items/6259f13e057bc25ebc23)

### 成功したら
Pythonのソースファイルと同じフォルダにdistフォルダが出来ており、そこにexeファイルが入っているので、それを配布すればOK。

### exeがうまく動かない
--noconsole"を外して、--debug allをつけた状態でexeを作成し、exe実行時のコンソールのメッセージからerrorを探すのが早い。  
意外とコード上のエラーが出てくる。  
  
参考：
[pyinstallerでpythonファイルをexeファイルにする](https://yasulab-pg.com/pyinstaller%E3%81%A7python%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92exe%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E3%81%99%E3%82%8B/)
[PyInstallerで作成した実行可能ファイル(.exe)が動かないときの調査方法](https://qiita.com/q_masa/items/93872744f63ed97a54d8)


## Py2exeで作成
参考：
[py2exeのインストール【Pythonプログラムのexe化】](https://self-development.info/py2exe%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%80%90python%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AEexe%E5%8C%96%E3%80%91/)

### インストール
```
pip install py2exe
```

### setup.pyを作成して保存
以下のコードをsetup.pyとして、py2exeを実行するフォルダに保存する。

```setup.py
from distutils.core import setup
import py2exe

setup(console=['hogehoge.py'])
```
console=[ ]の部分にexe化したいメインのソースファイル名称を入れる。

### 実行
exe化したいPythonコード一式＋setup.pyが入った状態で以下を実行

```
python setup.py py2exe
```
### 成功したら
実行フォルダにできたdistの中にあるexeファイルを実行するとアプリが起動する。
exe単体では動かないので、distフォルダを丸ごと配布する必要がある。

### exeが動かない
日本語名のフォルダの中で実行するとうまく動かないことがあるので、distフォルダをデスクトップ(C:\Users\username\Desktop)などに置いて起動するのが無難。


## 比較

PyinstallerとPy2exeでできたexeの比較。

1. 生成するファイルの総サイズはほぼ同じ。  
だいたい30MB程度だったが、これは何のパッケージを使うか次第。Pandasが入ったので重くなったかもしれない。
2. 起動は圧倒的にPy2exeで生成したexeが早い  
Py2exeだとすぐ起動するが、Pyinstallerは30秒くらいかかる。
3. Pyinstallerはexe一つの配布で済むがPy2exeはフォルダごと配布が必要

3.について、exeのショートカットをフォルダの外に作ってもらえばよいのだが、プログラムになれていない人に敷居が高いと感じたので、今回はPyinstallerで作ったexeを配布した。(週一くらいしか使わないので。毎日使う、とかなら話は別だが)
  
Pyinstallerでも軽量・高速化はできるらしいが、今回は行なっていない。  
参考：
[pyhon.py→exe化→軽量化→高速化 忘備録（pyinstaller py2exe）](https://medium.com/lsc-psd/pyhon-py-exe%E5%8C%96-%E8%BB%BD%E9%87%8F%E5%8C%96-%E9%AB%98%E9%80%9F%E5%8C%96-%E5%BF%98%E5%82%99%E9%8C%B2-pyinstaller-py2exe-4cd7c5f70c99)

## (参考)インストールしたライブラリ
```
C:\>pip list
Package                   Version
------------------------- ---------
altgraph                  0.17
cachetools                4.2.2
configparser              5.0.2
DateTime                  4.3
future                    0.18.2
numpy                     1.21.0
pandas                    1.2.5
pefile                    2021.5.24
pip                       21.1.2
py2exe                    0.10.4.0
pyinstaller               4.3
pyinstaller-hooks-contrib 2021.1
python-dateutil           2.8.1
pytz                      2021.1
pywin32-ctypes            0.2.0
setuptools                57.0.0
six                       1.16.0
tk                        0.1.0
wheel                     0.36.2
xlrd                      2.0.1
zope.interface            5.4.0

```