【selenium】pythonのseleniumを使いやすくするためのクラス作成

# はじめに

selenium を使いやすくするためのクラス（Seleniumコントローラー）を作成してみました。
selenium の利用初心者です。試しに「[Seleniumを使った楽天ウェブ検索での自動検索](https://note.com/mugi_cat/n/n287f1defc8a3)」を参考に自分のローカル環境で実行してみようと考えて、試行錯誤しました。
参考を作成された当時（2020年2月11日）と現状（2024年10月17日）にて、楽天ウェブの挙動やseleniumのAPIが変わっていたり、実はいろいろとお作法があるっぽので、以下の通り対応しました（対応中です）。


# 動作環境

VScode上のJuypiterNotebookで動作してます。下記の要件を満たせば、どこでも動作可能です。
- Python 3.12.1
- selenium 4.25.0


# 使用例：Pythonスクリプト

```
# seleniumコントローラー生成＝ブラウザ開く
ctrller = selenimuContorller()

# 楽天ウェブ検索
ctrller.getUrl('https://websearch.rakuten.co.jp/')
ctrller.wait(5, By.LINK_TEXT, 'ログイン')
ctrller.click(By.LINK_TEXT, 'ログイン')

# ログインID入力
ctrller.wait(5, By.ID, 'user_id')
ctrller.send_keys(By.ID, 'user_id', RAKUTEN_ID)                  # 楽天IDで書き替えてください
ctrller.click(By.XPATH, '//div[contains(text(),"次へ")]')

# パスワード入力
ctrller.wait(5, By.ID, 'password_current')
ctrller.send_keys(By.ID, 'password_current', RAKUTEN_PASSWORD)   # 楽天パスワードで書き替えてください
ctrller.click(By.XPATH, '//*[@id="cta011"]/div/div')

# 検索キーワード入力＆検索
ctrller.wait(5, By.ID, 'search-input')
ctrller.send_keys(By.ID, 'search-input', '楽天')
ctrller.click(By.ID, 'search-submit')

# ブラウザ閉じる
ctrller.close()
```

※パスワード入力後の「次へ」の画面で許諾画面が挟まれることがあるが未対応
※ときどきハングしてだんまりになる。原因不明

# 使用例：実行結果

```
2024-10-17 11:56:00.184895 #1 [open] ブラウザを開きます。
2024-10-17 11:56:06.233262 #2 [get] [ https://websearch.rakuten.co.jp/ ]を表示します。
2024-10-17 11:56:08.783030 #3 [wait] 要素[link text,ログイン]の表示を最大[5]秒待ちます。
2024-10-17 11:56:08.842105 #4 [click] 要素[link text,ログイン]をクリックします。
2024-10-17 11:56:09.795556 #5 [wait] 要素[id,user_id]の表示を最大[5]秒待ちます。
2024-10-17 11:56:10.046632 #6 [send_keys] 要素[id,user_id]に[**********]を入力
2024-10-17 11:56:10.416637 #7 [click] 要素[xpath,//div[contains(text(),"次へ")]]をクリックします。
2024-10-17 11:56:10.536503 #8 [wait] 要素[id,password_current]の表示を最大[5]秒待ちます。
2024-10-17 11:56:11.108093 #9 [send_keys] 要素[id,password_current]に[**********]を入力
2024-10-17 11:56:11.309555 #10 [click] 要素[xpath,//*[@id="cta011"]/div/div]をクリックします。
2024-10-17 11:56:11.466136 #11 [wait] 要素[id,search-input]の表示を最大[5]秒待ちます。
2024-10-17 11:56:15.082618 #12 [send_keys] 要素[id,search-input]に[楽天]を入力
2024-10-17 11:56:15.953281 #13 [click] 要素[id,search-submit]をクリックします。
2024-10-17 11:56:18.050674 #14 [close] ブラウザを閉じます。
```

※「**********」は伏字にしています。


# Seleniumコントローラー

```
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import datetime

class selenimuContorller():
    driver = ""
    actionCount = 0

    def __init__(self):
        self.log(f'[open] ブラウザを開きます。')
        self.driver = webdriver.Chrome()

    def getUrl(self, url):
        self.log(f'[get] [ {url} ]を表示します。')
        self.driver.get(url)

    def close(self):
        self.log(f'[close] ブラウザを閉じます。')
        self.driver.close()
        self.driver.quit()

    def wait(self, waitTime, elementType, elementValue):
        self.log(f'[wait] 要素[{elementType},{elementValue}]の表示を最大[{waitTime}]秒待ちます。')
        wait = WebDriverWait(self.driver, waitTime)
        element = ""
        try:
            element = wait.until(
                EC.visibility_of_element_located(
                    (elementType, elementValue))
                    )
            
        except TimeoutError as e:
            self.errorlog(f'エラーが発生しました: {e}')

        return element

    def click(self, elementType, elementValue):
        self.log(f'[click] 要素[{elementType},{elementValue}]をクリックします。')

        if len(self.driver.find_elements(elementType, elementValue)) == 0:
            self.errorlog(f'エラーが発生しました: 要素[{elementType},{elementValue}]がありません。')
            return
        
        try:
            self.driver.find_element(elementType, elementValue).click()
        except Exception as e:
            # ToDo:このエラーを適切に表現すること←クリックできない要素のとき
            self.errorlog(f'エラーが発生しました: {e}')

    def send_keys(self, elementType, elementValue, sendValue):
        self.log(f'[send_keys] 要素[{elementType},{elementValue}]に[{sendValue}]を入力します。')

        if len(self.driver.find_elements(elementType, elementValue)) == 0:
            self.errorlog(f"エラーが発生しました: 要素[{elementType},{elementValue}]がありません。")
            return
        
        self.driver.find_element(elementType, elementValue).send_keys(sendValue)

    def log(self, logdata):
        self.actionCount  += 1
        print(f'{datetime.datetime.now()} #{self.actionCount} {logdata}')

    def errorlog(self, logdata):
        print(f'{datetime.datetime.now()} {logdata}')
```


# 参考文献

- [Seleniumを使った楽天ウェブ検索での自動検索](https://note.com/mugi_cat/n/n287f1defc8a3)
- [【全13サイト】各金融機関サイトにPythonとSeleniumを使ってログインするサンプル一覧と注意点](https://qiita.com/yoshi2045/items/a58a7fbebcb9faad3e15#注意点)


# おわりに

このクラス（Seleniumコントローラー）が利用できるようであれば、ご利用ください。また、バグ、改善点などありましたら、フィードバックをお願いします。