質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.62%

pyinstallerでcsvファイルを読み込む方法がわからない

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,535

vaitarika

score 23

pyinstallerを用いてpythonスクリプトをアプリ化しようとしています。
そこで自分がアプリ化するプログラム内ではcsvファイルを用いるのですが、csvファイルをアプリ化した後に読み込む方法がわかりません。
pyinstallerでアプリ化する前はcsvファイルを読みこみプログラムを実行できるのですが、アプリ化した後では実行する事ができません。

自分の作っているプログラムのディレクトリ構成は以下の通りです。
file
|-main.py
|-sub.py
|-sub2.py
|-*.csv

mainスクリプトの中でsub.pyとsub2.pyをimportして使っており、sub.py内でcsvファイルを読み込ませて動作を行なっています。
csvファイルをsub.pyにおいて読み込ませるには下記のように記述しており、カレントディレクリからそのまま読み込むことを想定しています。

with open("*.csv", "r") as f:


pyinstallerの性質から、main.pyのみを指定して下記のように入力してアプリ化させました。
コンパイルは以下のように行なっています。

pyinstaller --windowed main.py


これでdistファイル内に生成されたアプリを実行しようとすると何もおこらないため、アプリの「パッケージの内容を表示」させ、その中にあるUnix実行ファイルのmainを開いてやると、次のエラーがまず表示されました。

File "site-packages/PyInstaller/loader/rthooks/pyi_rth__tkinter.py", line 28, in <module>
FileNotFoundError: Tcl data directory "/Users/~~~/Desktop/file/dist/main.app/Contents/MacOS/tcl" not found.
[68860] Failed to execute script pyi_rth__tkinter


これと同様に、tclがtkに変わったエラーも表示されたので、MacOS下にtclとtkという名前のファイルを作ったところこのエラーは消えました。次に出てきたエラーがcsvファイルの読み込みに関するもので以下のエラーが表示されました。

File "main.py", line 5, in <module>
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/PyInstaller/loader/pyimod03_importers.py", line 627, in exec_module
    exec(bytecode, module.__dict__)
  File "sub.py", line 4, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '*.csv'


*.csvファイルを読み込むことができていないようだったので様々なディレクトリにcsvファイルを配置したのですが、このエラーは消えませんでした

sub.py内で同じ階層のディレクトリからcsvファイルを読み込むようにしていることが原因でしょうか。
どのように書き換えればcsvファイルをアプリでも読み込む事ができるようになるでしょうか。

pythonは3.6.6を使用しています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • tachikoma

    2019/03/18 17:12

    ディレクトリ構成とファイル読み込みが失敗するコード、コンパイル方法、実行方法を質問に追記お願いします。

    キャンセル

  • vaitarika

    2019/03/18 18:04

    修正依頼ありがとうございます。
    質問に追記させていただきました。

    キャンセル

回答 3

checkベストアンサー

+1

下記のようにsubからファイルを読み込ませると、うまくmain.pyの内容を読み込めてるんですよね。このあたりの確認から初めてみてはどうでしょうか。

main.py

from sub import load_mainpy

if __name__ == "__main__":
    contents = load_mainpy()
    print(contents)

sub.py

def load_mainpy():
    with open("main.py") as f:
        contents = f.read()
    return contents

コンパイルと実行

$ tree -L 1 .
.
├── main.py
└── sub.py

$ pyinstaller --windowed main.py
...
$ ./dist/main/main
from sub import load_mainpy

if __name__ == "__main__":
    contents = load_mainpy()
    print(contents)

OSX 10.14.3
Python 3.7.0
PyInstaller 3.4

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/18 21:35

    教えていただいたmainとsubはどのようにcsvファイルの読み込みと関係させればよいのでしょうか。

    キャンセル

  • 2019/03/19 09:32

    上記の内容は、次にmain.pyの階層にsample.csvを作ってそれを読み込めるか確認してみる。うまくいけばさらに実際の条件に近づけていって、どこで失敗するかを探る、という具合です。

    キャンセル

  • 2019/03/22 16:36

    返信ありがとうございます。
    回答していただいたmainとsubのプログラムでsub.pyの
    with open("main.py") as f:
    の「main.py」の部分を自分の持っているcsvファイルの「sample.csv」に変えて

    ├── main.py
    └── sub.py
    └──sample.csv
    というような階層を作り、main.pyを実行したところ、csvファイルは読み込まれており、csvファイルの内容が表示されました。
    しかし、このmain.pyをpyinstallerで
    pyinstaller --windowed main.py
    としてアプリ化して、distファイル内に生成されたアプリを実行しようとすると何もおこらないため、アプリの「パッケージの内容を表示」させ、その中にあるUnix実行ファイルのmainを開いてやると、次のエラーが表示されました。
    File "main.py", line 4, in <module>
    File "sub.py", line 2, in load_mainpy
    FileNotFoundError: [Errno 2] No such file or directory: 'sample.csv'
    やはりアプリ化するとcsvファイルは読み込まれていないようでした。

    キャンセル

+1

元のコードが読むとおりの場所にCSVファイルを置けばいいのでは

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/27 18:02

    それはCSVファイルが読めない、というのとはまた別のエラーです。
    ファイルはきちんと読んでるのでは。

    キャンセル

  • 2019/02/27 18:03

    sys には_MEIPASSってのはない、というエラーに見えますが。
    そのエラーの該当行をチェックしてみては。

    キャンセル

  • 2019/02/27 18:38

    上記のエラーを検索して見たところ
    def resourcePath(filename):
    if hasattr(sys, "_MEIPASS"):
    return os.path.join(sys._MEIPASS, filename)
    return os.path.join(filename)
    この関数を定義すればよいとあったので定義したのですが、次は
    Error loading Python lib image not found
    というエラーが出てしまいました。
    やはりcsvファイルは読み込めていないようです

    キャンセル

0

PyInstallerは--onefileオプションを指定したときと、指定していないときで挙動が違います(挙動と言うより、exeファイルのパスが異なります)。どちらを想定していますでしょうか?

大まかには以下URLにも書いているのですが
PyInstallerを使ってみた - Qiita
PyInstallerを使うと、__file__sys.argv[0]sys.prefixの値が変わりますので、適宜上手く使い分ける必要があります(PyInstallerで実行ファイル作成済みかどうかは、hasattr(sys, "frozen")で確認できます)。

PyInstallerのonefileを使っていない場合、Path(sys.argv[0]).parentでexeのフォルダパスを取得できます。
onefileオプションを使っている場合、Path(sys.prefix)でexeのフォルダパスを取得できます(onefileオプションを使った場合、Tempフォルダに一度ファイルを展開してから実行することになるため)。
※ Pathは事前にfrom pathlib import Pathなどとしてインポート済みであるものとします。

まずは、リソースパスの取得処理を見直されると良いかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/18 15:58

    回答ありがとうございます。
    自分はonefileを使用しないで行なっています。
    自分は下記のようなディレクトリ構造でプログラムを書いています。
    examファイル
    |--main.py
    |--sub.py
    |--*.csv
    内容は、mainスクリプトが、同じディレクトリ内にあるcsvファイルを読み込み動作を行うsubスクリプトをimportして使うという形になっています。
    この場合、exeのフォルダパスを取得するPath(sys.argv[0]).parentをsubスクリプトに書いてあげればいいのでしょうか。
    Pyinstallerを使用した後は、distファイル内にアプリ化されたものと他の諸々が入っているファイルが出来上がると思うのですが、ここでのexeのフォルダパスとはdistというディレクトリのパスということでしょうか。

    キャンセル

  • 2019/03/20 20:31 編集

    > exeのフォルダパスを取得するPath(sys.argv[0]).parentをsubスクリプトに書いてあげればいいのでしょうか。
    sys.argv[0]の値はsubで使用してもmainで使用しても変わらないですから、どちらでも良いです(エントリポイントとなるソースファイルの値(PyInstaller利用後はexeのあるフォルダパス)となります)。
    どこかでメソッドを定義しておくと良いでしょう。

    以下は上記の条件で、親フォルダのPathを取得する例です(すみません、上記では`Path(sys.argv[0]).parent`と書きましたが`Path(sys.argv[0])`の誤りでした)。

    ```python
    def mypath():
    import sys
    return Path(sys.argv[0]) if hasattr(sys, "frozen") else \
    Path(__file__).parent
    ```

    > distファイル内にアプリ化されたものと他の諸々が入っているファイルが出来上がると思うのですが、ここでのexeのフォルダパスとはdistというディレクトリのパスということでしょうか。
    exeファイルがあるフォルダのパスとなります(初期設定の場合、dist/dist/APPのAPPディレクトリのパス)

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる