Python + Seleniumで久しぶりにスクレイピングする人用のメモ
久しぶりにSeleniumでスクレイピングしようとしたエラーが出たので、サクッとエラー内容とその原因、解決策をまとめます。
ライブラリのインストール
何はともあれ、selenium
のアップグレードと追々必要になるライブラリをインストールしてください。
python3 -m pip install -U selenium python3 -m pip install webdriver-manager
pip install -U
はライブラリをアップグレードするコマンドです。まだselenium
をインストールしていない人は-U
を省略してください。
DeprecationWarning
executable_path has been deprecated
DeprecationWarning: executable_path has been deprecated, please pass in a Service object
driver = webdriver.Chrome(executable_path='/usr/local/bin/chromedriver', options=options)
Selenium 4.0 から、webdriver
のインスタンスを生成する時にService
オブジェクトとObject
オブジェクト以外が使えなくなりました。Selenium 4.0 Beta 1 Changes
それに従い、excecutable_path
を使うとDeprecationWarning
が出ます。
解決策
from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.get("https://...") driver.quit()
No module named 'webdriver_manager'
とでたなら、今度から人の話を聞くようにしましよう。ライブラリのインストール
ライブラリをインストールしてから初めてChromeDriverManager().install()
を呼び出すと、セットアップに必要なChrome Driverの情報がダウンロード&インストールされます。
また、このスクリプトを実行すると毎回以下のようなログが出力されます。
# [WDM] - Downloading: 100%|████████████████████████████████████████████████████████████████████| 8.00M/8.00M [00:00<00:00, 19.4MB/s]
selenium.webdriver.ChromeOption
は、selenium.webdriver.chrome.option.Option
に変わりました。
本記事後半で紹介しているオプションを使ったテンプレートコードを参考にしてください。
AttributeError
‘WebDriver’ object has no attribute ‘find_element_by_css_selector’
Traceback (most recent call last):
File "/Users/.../sample.py", line N, in <module>
element = driver.find_element_by_css_selector(selector)
AttributeError: 'WebDriver' object has no attribute 'find_element_by_css_selector'
Selenium 4.3.0からfind_element_by_XXX
やfind_elements_by_XXX
(find_element_by_css_selector
/ find_elements_by_css_selector
) などは使えなくなりました。Selenium 4.3.0 Changes
解決策
代わりに、find_element
/ find_elements
関数にselenium.webdriver.common.by.By
オブジェクトを指定します。
from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.get("https://...") element = driver.find_element(By.ID, "sample-id") driver.quit()
テンプレコード
最も単純に、最も短く
from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.get(url) # By.CSS_SELECTOR, .ID, .NAME, ,.TAG_NAME, .XPATH element = driver.find_element(By.CSS_SELECTOR, selector) # elements = driver.find_elements(By.CSS_SELECTOR, selector) driver.quit()
以前はwebdriver
だけインストールすればよかったのに…
もりもり
import time from selenium import webdriver from selenium.common.exceptions import TimeoutException from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from webdriver_manager.chrome import ChromeDriverManager if __name__ == "__main__": url = "" selector = "" options = Options() options.add_argument("--headless") driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options) driver.get(url) # By.CSS_SELECTOR, .ID, .NAME, ,.TAG_NAME, .XPATH try: element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, selector)) # EC.presence_of_all_elements_located((By.CSS_SELECTOR, selector)) ) except TimeoutException: print("The element(s) couldn't be found.") finally: driver.quit()
まぁ僕が使うならこっちですかね。
オプション追加
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager options = Options() options.add_argument("--headless") driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options) driver.get("https://...") element = driver.find_element(By.CSS_SELECTOR, "#sample-id") driver.quit()
以前のChromeOptionと同様に以下のようなオプションが使えます。
--headless
: ブラウザを表示しない--window-size=width,height
: ブラウザのウィンドウサイズを指定する--start-maximized
: スクリーンサイズを最大にする--kiosk
: フルスクリーンモードで起動する--hide-scrollbars
: スクロールバーを隠す--incognito
: シークレットモードでWebを検索する- 履歴が残らない
- 拡張機能が使えない
--disable-extensions
--disable-popup-blocking
: ポップアップブロックを無効化する--disable-infobars
: インフォバーを無効化する--version
: Chromeのバージョンを表示する
Explicitly Wait
Webページに指定した要素がロードされるまで(明示的に)待機させることをExplicitly Waitと言います。
例えば、壁紙まとめサイトで複数の画像がロードするまで最低10秒待つコードは以下のように書けます。
from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.get("https://") try: element = WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, "img.preview")) ) except Exception as e: print(e) finally: driver.quit()
presence_of_all_elements_located
は誤解させやすい名前なので注意しましょう。
まず、細かいですがこれは関数ではなくクラスです。タプルのlocator
を渡してあげる必要があります。(上の例では(By.CSS_SELECTOR, "img.preview")
)
関数自身を渡す訳でも、関数のreturn内容を渡す訳でもありません。しっかりタプルの引数をコンストラクターに渡してインスタンス化しましょう。
次に、all_elements_located
とあるとまるで「全ての要素がロードされるまで」のように勘違いしてしまいますが、正確には「1つでも要素が見つかった時に待機を終了し、同条件で見つかった全ての要素を取得する」と説明した方が正しいです。
同様に、find_elements
関数の待機版が上のようなコードでしたが、find_element
関数の待機版を作りたい場合はEC.presence_of_element_located
を使いましょう。
element = WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, "img.preview")) )
指定された時間待ったのに要素を見つけれなかった時、WebDriverWait
はselenium.common.exceptions.TimeoutException
を投げます。
個別に例外をキャッチしたい時に参考にしてください。
Implicitly wait
driverの設定として、要素を見つけるために何秒待つか設定するのがimplicitly_wait
です。
以下のサンプルコードのようにimplicitly_wait
をすれば、指定された要素を探すときは常に最低10秒間ロードを待つようにできます。
from selenium import webdriver from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) driver.implicitly_wait(10) driver.get("https://") try: element = driver.find_element(By.CSS_SELECTOR, "#sample-id") except NoSuchElementException: print("The element couldn't be found.") finally: driver.quit()