SeleniumのChromeでexecutable_pathを毎回指定するのが面倒だから改善した

SeleniumのChromeでexecutable_pathを毎回指定するのが面倒だから改善した

Seleniumを使うときに、大体使うことになるのは私の場合、Chromeです。

Safariの場合はJSがちゃんと処理されていないのにPython側が実行しようとしたりするという問題があるらしいし、Firefoxはそもそも入れていない。

それならば、いつもSafariと同程度使っているChromeを使うのが良いな、という感じです。

しかし、こういうふうに書くと時折、こんなエラーが出てきます。

from selenium import webdriver
webdriver.Chrome()

# selenium.common.exceptions.WebDriverException: Message: Service 'chromedriver' unexpectedly exited. Status code was: 1

これ、毎回、chromedriverを実行ファイルと同じ場所に置く or executable_path=/path/to/chromedriverで指定する、ということをしないといけないんですね。

だから今回は小さいレベルですが、ライブラリーを書き換えてゆくことにしました。

環境

  • Python 3.9.7
  • Selenium 3.141.0
  • macOS Big Sur 11.5.2
  • chromedriverは解凍後にホームディレクトリー(ls ~/で出てくるところ)に入れてある
  • 普段はterminal + neovimでファイル編集をしている。

とりあえず、Seleniumの中身

これはPython上で以下のように書けば出力されます。

import selenium 

selenium.__file__
# '/path/to/versions/3.9.7/lib/python3.9/site-packages/selenium/__init__.py'

ではパッケージの中身を見てみます。

cd /path/to/versions/3.9.7/lib/python3.9/site-packages/selenium/でディレクトリー移動して中身を見てみると、このようになっています。

├── __init__.py
├── common
│   ├── __init__.py
│   └── exceptions.py
└── webdriver
    ├── __init__.py
    ├── ...

from selenium import webdriverから考えて、webdriver/__init__.pyの中に該当箇所がありそうです。

書き換え

# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

from .firefox.webdriver import WebDriver as Firefox  # noqa
from .firefox.firefox_profile import FirefoxProfile  # noqa
from .firefox.options import Options as FirefoxOptions  # noqa
from .chrome.webdriver import WebDriver as Chrome  # noqa
from .chrome.options import Options as ChromeOptions  # noqa

見てみると、Chromeに関しては18行目にありました。ここから見てみると、さらにchrome/webdriver.pyというファイルがありそうなので見てみます。

# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
import warnings

from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
from .remote_connection import ChromeRemoteConnection
from .service import Service
from .options import Options


class WebDriver(RemoteWebDriver):
    """
    Controls the ChromeDriver and allows you to drive the browser.

    You will need to download the ChromeDriver executable from
    http://chromedriver.storage.googleapis.com/index.html
    """

    def __init__(self, executable_path="chromedriver", port=0,
                 options=None, service_args=None,
                 desired_capabilities=None, service_log_path=None,
                 chrome_options=None, keep_alive=True):

案の定、36行目にexecutable_pathのデフォルト引数にカレントディレクトリーのchromedriverの指定がありました。

ここをこのように書き換えてゆきます。ちゃんとimport osも忘れずに。

import warnings
import os

from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
from .remote_connection import ChromeRemoteConnection
from .service import Service
from .options import Options


class WebDriver(RemoteWebDriver):
    """
    Controls the ChromeDriver and allows you to drive the browser.

    You will need to download the ChromeDriver executable from
    http://chromedriver.storage.googleapis.com/index.html
    """

    def __init__(self, executable_path=f'{os.environ["HOME"]}/chromedriver', port=0,
                 options=None, service_args=None,
                 desired_capabilities=None, service_log_path=None,
                 chrome_options=None, keep_alive=True):

これで、どのディレクトリーにいてもホームディレクトリーのchromedriverを参照してくれるようになりました!

さいごに

ライブラリーの書き換えは一見すると危険に見えますが、危険なことをしない限り、危険ではないです。ただ危険かどうかの判断がつかない場合はやめておいたほうが良いと思います。

一方で、pip install -U <package name>とかで更新してしまうと、書き換えた部分が消えてしまうので、また設定し直さないといけなくなります。

それでも、毎回呼び出すのであれば、書き換えてしまったほうが楽。