[m1Mac]Seleniumのドライバーエラー解決策ValueError: There is no such driver by url
seleniumを使用していて出会ったエラーの対処法です。
【環境】
- MacBook Air (M1, 2020)
- python 3.9.9
- selenium == 4.5.0
- chromedriver-binary == 105.0.5195.52
- webdriver_manager == 3.8.3
エラー内容
ValueError: There is no such driver by url https://chromedriver.storage.googleapis.com/106.0.5249.61/chromedriver_mac64_m1.zip
こんなドライバーないよーとエラーが出てますね。コードは下記のとおりです。
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
# 106.0.5249.61/chromedriver_mac64_m1.zip が自動で入力
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
title = driver.title
driver.implicitly_wait(0.5)
text_box = driver.find_element(by=By.NAME, value="my-text")
submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")
print(title, text_box)
driver.quit()
エラー内容の全文を見てみましょう。
Traceback (most recent call last):
File "/Users/myname/my_dir/selenium_test.py", line 15, in <module>
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
File "/Users/myname/.pyenv/versions/3.9.9/lib/python3.9/site-packages/webdriver_manager/chrome.py", line 39, in install
driver_path = self._get_driver_path(self.driver)
File "/Users/my_name/.pyenv/versions/3.9.9/lib/python3.9/site-packages/webdriver_manager/core/manager.py", line 30, in _get_driver_path
file = self._download_manager.download_file(driver.get_url())
File "/Users/myname/.pyenv/versions/3.9.9/lib/python3.9/site-packages/webdriver_manager/core/download_manager.py", line 28, in download_file
response = self._http_client.get(url)
File "/Users/myname/.pyenv/versions/3.9.9/lib/python3.9/site-packages/webdriver_manager/core/http.py", line 33, in get
self.validate_response(resp)
File "/Users/myname/.pyenv/versions/3.9.9/lib/python3.9/site-packages/webdriver_manager/core/http.py", line 16, in validate_response
raise ValueError(f"There is no such driver by url {resp.url}")
ValueError: There is no such driver by url https://chromedriver.storage.googleapis.com/106.0.5249.61/chromedriver_mac64_m1.zip
どうやら、ChromeDriverManagerのcore/http.pyというファイルでエラーが出ていそうです。
原因
まずはネットで同様のエラーがている人がいないか探してみます。
どうやらchromedriverのURLが
"https://chromedriver.storage.googleapis.com/106.0.5249.61/chromedriver_mac64_m1.zip"
から
"https://chromedriver.storage.googleapis.com/106.0.5249.61/chromedriver_mac_arm64.zip"
に変更されたみたいですね。
ちなみに105.0.5195.52の場合には"https://chromedriver.storage.googleapis.com/105.0.5195.52/chromedriver_mac64_m1.zip"で良いそうです。こちらのリンクから確認することができます。
その他のバージョンに関してはこちらから確認できます。
対処法
対処法1. パッケージの修正で強引に修正
まずは強引に治す方法です。
pipでwebdriver-managerをインストールした後、site-packages/webdriver_manager/core/http.pyを下記のように直します。
import requests
from requests import Response
from webdriver_manager.core.config import ssl_verify
from webdriver_manager.core.utils import show_download_progress
class HttpClient:
def get(self, url, params=None, **kwargs) -> Response:
raise NotImplementedError
@staticmethod
def validate_response(resp: requests.Response):
status_code = resp.status_code
if status_code == 404:
raise ValueError(f"There is no such driver by url {resp.url}")
elif status_code == 401:
raise ValueError(f"API Rate limit exceeded. You have to add GH_TOKEN!!!")
elif resp.status_code != 200:
raise ValueError(
f"response body:\n{resp.text}\n"
f"request url:\n{resp.request.url}\n"
f"response headers:\n{dict(resp.headers)}\n"
)
class WDMHttpClient(HttpClient):
def __init__(self):
self._ssl_verify = ssl_verify()
def get(self, url, **kwargs) -> Response:
##### URLが変更されたらしいので対応 ######
if 'chromedriver_mac64_m1.zip' in url:
url = url.replace('chromedriver_mac64_m1.zip', 'chromedriver_mac_arm64.zip')
##### URLが変更されたらしいので対応 ######
resp = requests.get(url=url, verify=self._ssl_verify, stream=True, **kwargs)
self.validate_response(resp)
show_download_progress(resp)
return resp
ですがこれではDocker化するときなどの管理が面倒になります。なので、ドライバーの中身を変更する以外の対処法を考えてみます。
対処法2. 予めchromedriverをインストールしておく方法
pipを用いない場合では、下記のリンクからm1用のzipファイルをインストールすることでエラーなく実行することが可能です。こちらのリンクよりお探しください。
※それぞれの環境に合わせたZipファイルをその場合下記のようにプログラムを修正します。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
options = Options()
options.add_argument('--headless')
# ダウンロード先のpath情報を指定
executable_path = '/Users/myname/Downloads/chromedriver'
driver = webdriver.Chrome(executable_path, options=options)
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
title = driver.title
driver.implicitly_wait(0.5)
text_box = driver.find_element(by=By.NAME, value="my-text")
submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")
print(title, text_box)
driver.quit()
この方法であれば他の人も同様に実行できる様はなりますが、それぞれの環境に合わせてpathを指定しなくてはならないので、誰がどんな環境で実行してもうまくいくように修正する必要があります。
対処法2-2. Chromedriverをインストールしpathはコマンドで通す方法
私は再現性の観点から、今回はこの対処法で対応しました。
まずはpipで「chromedriver-binary」をインストールします。その後、実行するディレクトリにて
export PATH=$PATH:`chromedriver-path`
を実行することでpathをコードに記載することなく実行できます。
コードは下記のようになります。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
options = Options()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
title = driver.title
driver.implicitly_wait(0.5)
text_box = driver.find_element(by=By.NAME, value="my-text")
submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")
print(title, text_box)
driver.quit()
この方法であればDocker化も問題なくできます。
Discussion