(python番外編)pyinstallerとseleniumとの併用で無駄に表示されるコンソールを消す方法

これがオープンソースか

前回のpyhon番外編で書いていた、pyinstallerでpythonスクリプトを実行ファイル化したとき、--noconsoleを指定しいるのに、webドライバの読み込みあたりで「黒いコンソールが表示」されてしまう件ですが、結局原因はseleniumにありました。解決方法はなんと「seleniumのソースコードに手を入れる」です。

この問題「これがオープンソース」って感じですね。問題があった場合、誰かが解決方法を考えてWeb上にアップしたり、修正提案で修正してもらう。しかしそれがなければ「自分で原因を見つけて自分でソースコードを治さなくてはいけない」ということかと思います。

今回は、解決方法をWeb上へアップしてくれた人をなんとか見つけることが出来たので解決できましたが、この記事は今年の4月に書かれたものです。もし作者さんが解決する前にこの問題に直面していたらと思うととゾッとします。

なおこの記事は、誤った記述があったこと、周囲の解説が少なくわかりにくかったこと、またこの記事自身がGoogle検索でもトップヒットより2〜3ページあとでしか表示されなかったことから、以下に対応方法をおさらいをしておきます。

コンソール表示の問題の詳細

上記にも触れましたがpyinstallerは、pythonプログラムを実行ファイルとして環境ごとパッケージ化することで、pythonをインストールしていない人へもプログラムを提供しやすくするいライブラリのうちの一つです。

このライブラリがメジャーとはいえ、使えるえるようになるまでにいつくかの課題を超えなくてはなりません。プログラム上で指定するバイナリファイル等の実行ファイルの格納位置をコード上でうまく指定しなくてはいけなかったり、実行ファイル生成時にプログラムファイルは全て指定しなくてはいけなかったりと、全てを解析して実行ファイルを作ってくれるものではありません。

また、実行環境の指定に誤りがあるとFatal errorというメッセージボックスだけが表示され、誤りが見つけづらいという特徴もあります。pyinstallerでは、実行時の問題を表示するためにコンソールボックスが表示されるようですが、一瞬表示されて消えてしまうのであまり意味はなく、通常は--noconsoleを指定してコンソールは表示せません。

今回の問題はこのコンソールの表示を消しきれず、実行した当初は問題ありませんが、なぜか途中でコンソールが表示されてしまうというものです。コンソール表示がでてしまうというpyinsallerを使った問題解決のためには、実はpyinstaller側ではなく、ライブラリ側ををいじってあげる必要があったという、本当にやっかいな問題でした。

解決方法(コードを見つける・いじる)

このように解決のためには、先の記事にあるとおり、pyinstaller側ではなく、seleniumのソースコードを改変します。手順を以下に整理します。

(1)seleniumの該当ソースコードを見つける

まずここに苦労しました。元記事には該当ソースコードの場所が「モジュールディレクトリ」とのみ記載されているだけで、記事だけではどこにあるのかが全くわかりません。このモジュールディレクトリがPC上のどのフォルダにあるのかは、結局は以下のプログラムを書き、実行して見つけなくてはいけませんでした。

■modulepath.py(プログラム)

import sys
import pprint
pprint.pprint(sys.path)

■実行(Powershell等からコマンドを打つ)

python modulepath.py

これでコンソール上に、インストールの結果モジュールが格納されたディレクトリ(フォルダ)の一覧が表示されます。この一覧の中からseleniumという文字が書かれたPC上のフォルダへ行き、修正対象ファイルを見つけます。

ちなみに紹介されていた記事では、サブディレクトリやソースコードファイル名がが誤って一つにつながってしまっており、そもそも対象がまったくわかりません。格納ディレクトリがわかって初めて記載内容がわかりましたが、実際には次のファイルになります(windowsの例。C:¥〜はユーザディレクトリ(フォルダ)を意味し、OSやユーザ名などの環境により異なります)。

C:¥〜¥site-packages¥selenium¥webdriver¥common¥service.py

(2)pywin32モジュールをインストールする

次に実行に必要なライブラリ(pywin32)をインストールします。

これもハマりました。いちいちハマるなという感じですが、インストールが必要なモジュールの具体名が書いていないのでいたしかたありません。

なお、インストール方法ですがちなみに前に触れたように自分の環境はインターネットが遮断がされており、pip installができる環境にないので次の手順でインストールしました。
①ブラウザでpip.orgサイトに行く
②pywin32という検索文字を入れてライブラリを検索する
③ライブラリ一覧の中から「pywin32 228」というモジュール選択する
④Download filesをクリック
⑤表示されるパッケージファイルのうち、パッケージファイルの名称をもとに、自分のpythonバージョン(cpNNのNNがpythonバージョンです)かつwindows・cpuにあったもの(自分の場合はwin32)を選択しダウンロード
⑥ダウンロードフォルダへ行き「python -m pip install --no-deps パッケージファイル名」でインストール
詳細なインストール方法はこちらを参照してください。

(3)ソースコードを書き換える

pywin32のインストールが完了したら、元記事にあるとおり(1)で見つけたseleniumのservice.pyをエディタで開き、import文がならぶ最後あたりに次のコードを行追加します。

from win32process import CREATE_NO_WINDOW

また、コード中のstartメソッドにあるsubprocess.Popenへの引数として 

creationflags = CREATE_NO_WINDOW

を追加します。具体的には下記の最終行が追加したイメージです(下記イメージでは一番下にありますが、実際のコードではプログラムの途中になります)。

   def start(self):
       """
       Starts the Service.
       :Exceptions:
        - WebDriverException : Raised either when it can't start the service
          or when it can't connect to the service
       """
       try:
           cmd = [self.path]
           cmd.extend(self.command_line_args())
           self.process = subprocess.Popen(cmd, env=self.env,
                                           close_fds=platform.system() != 'Windows',
                                           stdout=self.log_file,
                                           stderr=self.log_file,
                                           stdin=PIPE, #「)」を削除し代わりに「,」を入れる
                                           creationflags=CREATE_NO_WINDOW) #追記行

この2箇所の追記でOKだそうです(中身はまったくわかっていない)。

(4)pyinstallで実行ファイルを作る

(3)のとおりseleniumのソースコードの書き換えが終わったら、あとはコマンドラインでpyinstallを実行すれば、めでたく問題のない実行ファイルができあがります。

もちろん、全ての*.pyファイルを並べて指定したり、--onefile、--noconsoleや、--add-binaryでWebドライバの相対位置を指定したりすることは必要です。しかし私の最初の法の記事でいっていたpythonのバージョンとpyinstallerのバージョンを3.6に合わせるといった必要はなかったかもしれません。

これで一件落着、といきたいところではありますが、このなかでpywin32モジュールが、OSビット・CPU縛りだったのが気になります。多用な環境上で動作するのか謎な部分が残ります。ひょっとすると環境ごとに実行ファイルを用意してあげる必要があるかもしれません。多用な環境で動作するかどうかは、この他にpyinstaller側の問題もあるようで、広く配布するときは複数の環境でテストしたり、提供先の環境での動作試験が必須になるかもしれません。

いかがでしたでしょうか。この記事がお役に立ちましたら「ハートボタン」「フォロー」「サポート」「コメント」での応援よろしくお願いします。


この記事が気に入ったらサポートをしてみませんか?