Python

Python + Seleniumで久しぶりにスクレイピングする人用のメモ

nariXcode

久しぶりに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_XXXfind_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"))
)

指定された時間待ったのに要素を見つけれなかった時、WebDriverWaitselenium.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()
ABOUT ME
記事URLをコピーしました