49
89

PyInstallerによるPythonスクリプトのexe化とエラー対処方法まとめ

Last updated at Posted at 2021-07-25

この記事では、PythonモジュールのひとつであるPyInstallerを用いたPythonスクリプトのexe化方法および各種エラー対処方法について説明します。PyInstallerでexe化することで、Pythonをインストールしていない環境においてもPythonスクリプトを実行させることができるようになります。業務においてちょっとしたツールを作成しチームメンバーに配布したり、Tkinterと組み合わせてGUIを実装すれば、スタンドアロンで動くデスクトップアプリケーション (前回記事を参考) を作成することも可能です。

  • 作成の動機
  • PyInstallerとは
  • exe化の一連の流れ
  • 1.仮想環境作成
  • 2.PyInstaller実行
  • 3.exeファイルの軽量化
  • 4.各種エラー対処方法
  • 作成の動機

    PyInstallerに関する記事についてはQiita内やWEB上を探せばいくつか見つけることができますが、exe化の一連の流れや注意点、作成したexeファイルの軽量化方法についてひとつの記事にまとめているものはなく、過去PyInstallerの使い方を調べる際に点在した情報を探すのにとても苦労したことから、今回PyInstallerの使用方法をまとめた記事を作成することにしました。

    また、以前書いた記事 「Python未導入環境においてPandasGUIとpandas-profilingを使用可能なEDAツール『Pandas Anywhere』を作ってみた」 ではPandasGUI、pandas-profillingモジュールを含むスクリプトのexe化に取り組みましたが、多くのエラーに遭遇し、解消に苦労したので、この際に出てきたエラーとその対処方法についても併せて述べたいと思います。

    PyInstallerとは
  • Pythonスクリプトをexe化するためのPythonモジュール。ver4.3ではPython3.9までがサポートされています。
  • 実行時のオプション指定で1つのexeファイルにまとめたり、exeファイル実行時にコンソール画面を非表示にすることができる(詳細は後述)。
  • スクリプトをexe化するモジュールとしては、PyInstaller以外にも「cx_Freeze」がある。PyInstallerとの違いは、PyInstallerはオプション指定で1つのexeファイルにまとめられるが、cx_Freezeでは複数ファイルに分かれた形でのexe化となる。exeファイルの起動速度についてはcx_Freezeのほうが速い模様。
  • exe化の一連の流れ
  • 仮想環境の作成
  • PyInstallerの実行
  • exeファイルの軽量化
  • エラー対応
  • ※ 3.以降は必要に応じて実行

    1.仮想環境作成

    PyInstallerを用いたexe化の初期手順として必ず仮想環境を作成して、仮想環境上で行います。 PyInstallerでexe化する際は、スクリプトに記載されているモジュールがexeファイルに同封されるわけではなく、開発環境上に存在しているすべてのモジュールが同封されます。 つまり、exe化するスクリプト上ではpandasやnumpyしか使用していないとしても、例えば開発環境上にTensorFlowやPyTorchがインストールされているのであればこれらのモジュールも同封されてしまいます(その結果、ものすごい大容量のexeファイルができあがります)。そのため、exe化する際は必ず仮想環境上で実行するようにして下さい。

    ※作者実行環境
    OS: Windows10
    Python: Python3.7.3
    Pyinstaller: ver4.2
    PCユーザ名: username

    例で「testvenv」という名前の仮想環境を作成

    console
    python -m venv testvenv
    

    作成した仮想環境の起動

    console
    C:\Users\username\testvenv\Scripts\activate
    # Windows10環境
    # PCユーザ名: username
    

    pipの最新化

    console
    pip install -U pip
    

    Pythonスクリプト上で使用するモジュールのインポート
    ※ファイル容量を少なくするために、exe化するスクリプトで使用しない余計なモジュールはインストールしないこと。

    console
    pip install pyinstaller   # pip  installの一例
    2.PyInstaller実行
    

     Desktop 上の「test」フォルダ内に作成した「main.py」というスクリプトをexe化する場合

    console
    cd C:\Users\username\Desktop\test
    pyinstaller main.py
    

    ※実行環境によっては「PyInstaller」のように「P」と「I」を大文字で指定しないと上手く実行できない場合があるようです。

    上記操作の実行によりtestフォルダ内に以下の通りフォルダ、ファイルが作成されます。

    ディレクトリ構成  ├ main.py … スクリプトファイル(自身で用意したファイル)
     ├ main.spec … PyInstaller実行後に作成される中間ファイル、エラー発生時等にPyInstaller実行時の設定をここに記述
     ├ dist … ここにexeファイルや関連ファイルが格納されたフォルダが作成、配布時はこのフォルダごと配布するイメージ
     └ build … このフォルダの中身を触ることは特にありません

    以下のようにdistフォルダ内にexeファイルと関連ファイルが作成されます。
    オプションの指定 --onefile

    onefileオプションを指定することで、スクリプト実行に関連するファイルを1つのexeファイルにまとめてくれます。私がツールを配布する際は基本的にこのオプションを指定しています。

    console
    pyinstaller main.py --onefile
    

    以下のようにdistフォルダ内に1つのexeファイルが作成されます。
    --noconsole

    noconsoleオプションを指定することで、exeファイル実行時に表示されるコンソール画面を非表示にできます。ツール配布時に余計な画面を表示させたくない場合には指定すると良いですが、コンソール画面に表示されるエラー内容が見れなくなるのでデバッグ中は指定しないほうが良いです。

    console
    pyinstaller main.py --noconsole
    3.exeファイルの軽量化
    

     仮にスクリプト内でpandas、numpyしか使用しない簡単なデータ集計ツールを作ったとしても、作成されたexeファイルは300MB程度にはなってしまうと思います。原因としてはpandasやnumpyモジュール内に含まれているIntel MKLというIntel CPUに特化した高速線形演算ライブラリが原因。これを除外することでexeファイルの容量を30MB程度にすることができます。

    specファイルに以下の破線の範囲を追記します。

    main.spec
    # -*- mode: python ; coding: utf-8 -*-
    block_cipher = None
    a = Analysis(['main.py'],
                 pathex=['C:\\Users\\username\\Desktop\\test'],
                 binaries=[],
                 datas=[],
                 hiddenimports=[],
                 hookspath=[],
                 hooksconfig={},
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher,
                 noarchive=False)
    pyz = PYZ(a.pure, a.zipped_data,
                 cipher=block_cipher)
    #---------------追加ここから---------------
    Key = ['mkl']
    def remove_from_list(input, keys):
        outlist = []
        for item in input:
            name, _, _ = item
            flag = 0
            for key_word in keys:
                if name.find(key_word) > -1:
                    flag = 1
            if flag != 1:
                outlist.append(item)
        return outlist
    a.binaries = remove_from_list(a.binaries, Key)
    #---------------追加ここまで---------------
    exe = EXE(pyz,
              a.scripts, 
              exclude_binaries=True,
              name='main',
              debug=False,
              bootloader_ignore_signals=False,
              strip=False,
              upx=True,
              console=True,
              disable_windowed_traceback=False,
              target_arch=None,
              codesign_identity=None,
              entitlements_file=None )
    coll = COLLECT(exe,
                   a.binaries,
                   a.zipfiles,
                   a.datas, 
                   strip=False,
                   upx=True,
                   upx_exclude=[],
                   name='main')
    

    ★追記後にbuild、distフォルダを一度削除し、以下の通りPyInstallerを実行するとdistフォルダ内に新しくexeファイルが作成されます(pyファイルではなく、specファイルを指定していることに注意)。

    console
    pyinstaller main.spec --onefile
    

    MKLを除外したことによりexeファイルが軽量化されていることが確認できるはずです。

    4.各種エラー対処方法 ①ModuleNotFoundError
    ModuleNotFoundError: No module named 'pandas_profiling.describe'  # エラーの一例
    

    このエラーが出た場合は、エラーに記載のモジュールが上手くexeファイルに同封できていません。そのため、specファイル内の「hiddenimports」の項に以下の通り、モジュールインポートが上手くできていないことを明示的に記述してあげます。

    hiddenimports=['pandas_profiling.describe']
    

    記述後に、上述の★の操作でPyInstallerを再度実行すると新しくexeファイルが作成されます。

    このexeファイルを実行して、再度別のModuleNotFoundErrorが出る場合は、先ほどのhiddenimportsの項目にカンマ区切りでエラーに該当するモジュールを付け足していって下さい。

    ②FileNotFoundError
    FileNotFoundError: [Errno 2] No such file or directory:
    'C:\Users\username\AppData\Local\Temp\_MEI103442\wordcloud\ipaexg.ttf'  # エラーの一例
    

    このエラーが出た場合は、エラーに記載のファイルが上手くexeファイルに同封できていません。そのため、specファイル内の「datas」の項に以下の通り、ファイルインポートが上手くできていないことを明示的に記述してあげます。

    datas = [('C:\\Users\\username\\testvenv\\lib\\sitepackages\\wordcloud\\ipaexg.ttf','\\wordcloud\\')]
    

    記述後に、上述の★の操作でPyInstallerを再度実行すると新しくexeファイルが作成されます。

    このexeファイルを実行して、再度別のFileNotFoundErrorが出る場合は、先ほどのdatasの項目にカンマ区切りでエラーに該当するファイルを付け足していって下さい。

    ③Recursion error
    Recursion error : maximum recursion depth exceeded
    

    このエラーは、関数内で再帰のしすぎでPythonのデフォルト再帰回数の1000回を超えてしまう際に発生します。このエラーは、スクリプト内で再帰関数を使っていない場合にも発生するため、PyInstaller内で再帰関数が使われていると考えられます。対処方法としては、スクリプト内に以下の記述を追加し、許容する再帰回数上限を1000回から上げます。

    import sys
    sys.setrecursionlimit(10000) # 再帰回数の上限を10000回に設定
    

    エラーについては①と②がほとんどだと思われるので、この2つのエラーの対処方法を押さえておけばまずは大丈夫だと思います。

    以前書いた記事「Python未導入環境においてPandasGUIとpandas-profilingを使用可能なEDAツール『Pandas Anywhere』を作ってみた」では、ModuleNotFoundErrorが100回以上発生したため、ModuleNotFoundErrorが発生する特定モジュール内のサブモジュール名をすべて取得するスクリプトを作成して対応しました(エラー発生に切りがなかったので取得したすべてのサブモジュール名をhiddenimportsに記述。具体的にはPlotlyモジュール内のlayout配下のサブモジュールがすべてModuleNotFoundErrorになりました)。

    参考に、サブモジュール名を取得するスクリプトを以下に載せておきます。

    ※plotly.validators配下のlayoutフォルダ内のサブモジュール名をすべて取得する場合の例で、スクリプトと同一ディレクトリ内にlayoutフォルダを丸ごとコピーして実行すると結果がprint出力されます。

    import os
    sbfolder = []
    for fd_path, sb_folder, sb_file in os.walk('layout'):  # os.walk()の括弧内は格納するモジュールのフォルダ名を記載
        for fol in sb_folder:
            path = fd_path + '\\' + fol
            path2 = 'plotly.validators.' + path  # 必要に応じて書き換え
            path2 = path2.replace("\\" , ".")
            if 'pycache' not in path2:
                sbfolder.append(path2)
    print(sbfolder)
    # 以下のprint結果を、specファイルのhiddenimportsに記入
    # ['plotly.validators.layout.activeshape', 'plotly.validators.layout.angularaxis', 
    # ...
    # 'plotly.validators.layout.yaxis.title', 'plotly.validators.layout.yaxis.title.font']
    

    以下はPyInstallerを用いて作成したexeファイルの使用時の注意点です。

    ・exeファイルを実行できるOS環境は、PyInstallerを実行したOS環境のみ
    例えば、Window10 64bit環境で作成したexeファイルを実行できるのはWindows10 64bit環境のみです。
    ・起動に時間がかかる
    実行するPCのスペックやexeファイルの中身にも依りますが、exeファイルを起動するのに簡易なもので30秒、重いもので1分以上かかることがあります。

  • 【Python】TIPS:pyinstallerの軽量化【exe化】
  • pyinstaller によるexe化のエラーメッセージと対処方法
  • 以上、PyInstallerの使用方法になります。他にもいろいろとエラーはあったので、思い出し次第、追記していきたいと思います。

    Register as a new user and use Qiita more conveniently

    1. You get articles that match your needs
    2. You can efficiently read back useful information
    What you can do with signing up
    49
    89