0
2

More than 1 year has passed since last update.

Selenium(4.1.3)のまとめ書き

Last updated at Posted at 2022-05-09

はじめに

どうも。最近Selenium 4.1.3(Python 3.9.7)をよく使っていたので、そのときに気づいたことなどをまとめました。

注意事項

スクレイピングしている方ならご存知だと思いますが、念のため置いておきます。
岡崎市立中央図書館事件

ドライバーはwebdriver_managerを使用する

バージョンが変わるごとにドライバーを入れ替えるのは大変ですよね。
そこで役立つのが、このwebdriver_managerです。

pip install webdriver_manager

Chromeのドライバーを入れたい場合、このように記述します。

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)

非常に便利です。

class化したほうが使い勝手がよい

classにしたほうが圧倒的に使いやすい。オブジェクト指向に基づいて考えると当たり前ですが。
以下が私がよく使用しているclassです。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.common.exceptions import *

import time
import os
from datetime import datetime
from logging import getLogger, Formatter, FileHandler, StreamHandler, INFO, DEBUG

class Driver_Format():
    """
    Driver Format
    """
    def __init__(self, headless:bool=False, class_name:str=__name__) -> None:

        if not os.path.exists('log'):
            os.makedirs('log')

        self.logger = getLogger(class_name)
        self.logger.setLevel(INFO)

        self.file_name = './log/log_' + datetime.now().strftime('%Y%m%d_%H%M%S')

        self.fh = FileHandler(self.file_name+'.log')
        self.fh.setLevel(INFO)
        fh_formatter = Formatter('%(asctime)s - %(levelname)s - %(name)s - %(funcName)s - %(message)s')
        self.fh.setFormatter(fh_formatter)

        self.ch = StreamHandler()
        self.ch.setLevel(INFO)
        ch_formatter = Formatter('%(asctime)s - %(levelname)s - %(name)s - %(funcName)s - %(message)s')
        self.ch.setFormatter(ch_formatter)

        self.logger.addHandler(self.fh)
        self.logger.addHandler(self.ch)

        self.logger.info('Initialization Logger')
        self.Initialization_Driver(headless)        

    def Initialization_Driver(self, headless:bool=False):

        options = Options()
        options.add_argument("--window-size=1920,1080")
        if headless:
            options.add_argument('--headless')
        self.driver = webdriver.Chrome(ChromeDriverManager().install(), options=options, service_log_path=self.file_name+'_driver.log')        
        self.driver.implicitly_wait(10)
  
    
    def close_driver(self, removeHandler_tf = True):
        if removeHandler_tf:
            self.logger.removeHandler(self.fh)
            self.logger.removeHandler(self.ch)
        self.driver.quit()

    def wait_and_get_element(self, tag, path:str):
        """
        Wait and return element from any tags
        """
        for _ in range(10):
            try:
                WebDriverWait(self.driver, 10).until(
                    EC.visibility_of_element_located((tag, path))
                )
                return self.driver.find_element(tag, path)
            except:
                self.driver.refresh()
        else:
            st = 'Time OUT : ' + path
            raise Exception(st)
    
    def wait_and_click(self, tag, path:str):
        
        for _ in range(10):
            try:
                WebDriverWait(self.driver, 10).until(
                    EC.element_to_be_clickable((tag, path))
                )
                element = self.wait_and_get_element(tag, path)
                ActionChains(self.driver).move_to_element(element).perform()
                element.click()
                break
            except ElementClickInterceptedException:
                time.sleep(1)
            except StaleElementReferenceException:
                time.sleep(1)
            except TimeoutException:
                time.sleep(1)
        else:
            st = 'Time OUT : ' + path
            raise Exception(st)

スクレイピングをしていると、やはりログが必要であることが大半(個人的感想)なのでこのようにログを混ぜて定義すると、非常に使いやすいです。

要素の選択にはWebDriverWaitを使うべき

スクレイピングが速すぎると要素を取得できず、よくエラーが発生します。time.sleep()を使用してやっていたとしても、ページのロードに時間が掛かると結局はエラーが発生します。そこで、このWebDriverWaitを使用することでわざわざtime.sleep()を使用しなくても待ってくれます。
以下のように使用します。

# 要素が見つかるまで待つ
WebDriverWait(self.driver, 待機時間).until(EC.visibility_of_element_located((tag, path)))
# クリックできるようになるまで待つ
WebDriverWait(self.driver, 待機時間).until(EC.element_to_be_clickable((tag, path)))
# フレームが使用可能になるまで待機し、移動する
WebDriverWait(self.driver, 待機時間).until(EC.frame_to_be_available_and_switch_to_it((tag, path)))

他にもありますが、私が最近使用したのはこの3つです。これで大体のことは可能かと思われます。
フレーム移動に関する注意事項ですが、私がスクレイピングしていたページの問題かもしれませんが、EC.frame_to_be_available_and_switch_to_itを使用した後、すぐにframe内の要素を取得しようとする(WebDriverWaitを使用)とselenium.common.exceptions.NoSuchWindowExceptionというエラーが発生しました。結局原因は分からず、time.sleep(1)を使用することでなんとか解消しました。

frame内にいるときにrefreshを行うともとに戻る

ただの豆知識程度ですが、iframe内などにいるときにdriver.refresh()を行うと一番外側のframeに戻されます。

Google Chromeのユーザーデータ(履歴、ブックマーク、パスワード等)を使用する方法

optionsでユーザーデータのフォルダやProfileを指定することで、ログイン情報などを引き継いで使用することが出来ます。
次のように設定します。

from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--user-data-dir=E:\python\Google_Chrome\locals') # Profileが格納されているフォルダの指定
options.add_argument('--profile-directory=Profile1') # Profileの指定

注意事項として、--user-data-dir=はフルパスで指定する必要があります。(相対パスではエラーが発生する)
Windowsでしか確認が取れていませんが、--user-data-dir=--profile-directory=で指定したフォルダが存在しない場合、自動でフォルダが作成されます。
自分が使用しているユーザーデータを使用することも可能ですが、同時に使用するとエラーが発生するためそこは注意しましょう。

最後に

最後まで読んでいただきありがとうございます。
何かご指摘や質問などありましたら、コメントをよろしくお願いいたします。
それでは失礼いたします。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2