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()
