[Python] Seleniumの記述を簡単にするクラスを試作してみた

# はじめに
seleniumって書くのめんどくさいくないですか？

「入力やクリックみたいな簡単な操作でも要素取得->入力・クリックみたいに2手間かかる」
「サイトの表示速度に応じてtime.sleep()に入れるパラメータの値を調整する（表示されるまで待機するメソッドもあるが記述がめんどくさい）」
「ActionChainsは記述がめんどくさい」

などで自分は結構面倒に感じてしまいます

こういった面倒を改善したかったので、各操作を1つのメソッドで実行できるクラスを試しに作ってみました。

# クラス

```python:wait_driver.py
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
import time


class WaitDriver:

    TIMEOUT = 20
    SLEEP = 1

    def __init__(self, driver):
        if not isinstance(driver, webdriver.Chrome):
            raise("Invalid driver type, expected for Chrome")
        self.__driver = driver

    def get(self, url):
        self.__driver.get(url)

    def quit(self):
        self.__driver.quit()

    def find(self, xpath, timeout=TIMEOUT):
        wait = WebDriverWait(self.__driver, timeout)
        element = wait.until(
            expected_conditions.visibility_of_element_located(
                (By.XPATH, xpath)
            )
        )
        return element

    def send_keys(self, xpath, text, timeout=TIMEOUT, sleep=SLEEP):
        element = self.find(xpath, timeout)
        time.sleep(sleep)
        element.send_keys(text)
        return element

    def click(self, xpath, timeout=TIMEOUT, sleep=SLEEP):
        wait = WebDriverWait(self.__driver, timeout)
        element = wait.until(
            expected_conditions.element_to_be_clickable(
                (By.XPATH, xpath)
            )
        )
        time.sleep(sleep)
        element.click()
        return element

    def move_to_element(self, xpath, timeout=TIMEOUT, sleep=SLEEP):
        element = self.find(xpath, timeout)
        actions = ActionChains(self.__driver)
        actions.move_to_element(element)
        time.sleep(sleep)
        actions.perform()
        return element

    def drug_and_drop(self, draggable_xpath, droppable_xpath, timeout=TIMEOUT, sleep=SLEEP):
        source = self.find(draggable_xpath, timeout)
        target = self.find(droppable_xpath, timeout)
        actions = ActionChains(self.__driver)
        actions.click_and_hold(source)
        actions.move_to_element(target)
        time.sleep(sleep)
        actions.perform()
        return source, target
```

# 使い方
windowsでのselenium環境構築方法は[こちら](https://qiita.com/yoshi-kin/items/e0a7336a288188913097)を参考にしてください。

###1. インスタンス

```python:test_selenium.py
from selenium import webdriver
import chromedriver-binary
from wait_driver import WaitDriver

driver = Webdriver.Chrome()
# 作ったやつをインスタンス化
wd = WaitDriver(driver)
```

###2. メソッド
```python:test_selenium.py
# 指定のURLにアクセス
wd.get(url)

#（指定の要素が表示されるまで待機して）取得、要素返却
element = wd.find(xpath)

#（指定の要素がクリックできるまで待機して）クリック、要素返却
wd.click(xpath)

#（指定の要素が表示されるまで待機して）入力、要素返却
wd.send_keys(xpath, text)

#（画面外の指定した要素が表示されるまで待機して）スクロール、要素返却
element = wd.move_to_element(xpath)

#（指定したドラッグ要素とドロップ要素が表示されるまで待機して）ドラッグ&ドロップ、2つの要素返却
wd.drug_and_drop(druggable_xpath, droppable_xpath)

# Chromeを閉じる
wd.quit()
```

各メソッドには`timeout`と`sleep`の値を渡すこともできます。

`timeout`は要素取得するまでに許容する時間です。時間内に要素が取得できなければタイムアウトになります。デフォルトは20秒。

`sleep`は要素取得してから次のアクションを起こすまでの時間です。これを設定しないと操作が早すぎてクローリングしていることがばれる可能性があるので1ないしは2秒以上を推奨します。デフォルトは1秒。

また、デフォルトの値自体を変更したい場合はWaitDriverのクラス変数（以下部分）を直接いじります。

```python:wait_driver.py
class WaitDriver:

    TIMEOUT = 20
    SLEEP = 1
    ....
```

# まとめ
まだ十分には使ってないですが、ちょっと試した感じだとなかなかよさげなのでよかったら使ってみてください。まだメソッドが少ないですし、xpathでしか要素を取得できないのでそこは今後ブラッシュアップしていければと思います。