【Selenium】Googleスプレッドシートを読み込んで会員登録フォームに入力する

# はじめに
@dauuricus さんの[「Googlecolab を使って iRuby で selenium と chromium で徘徊する為に」](https://qiita.com/dauuricus/items/0717c8a08aba7abc7575)の記事で、Google Colaboratory（Google Colab）というGoogleが提供する環境の存在を知りました。やはりGoogleさんはすごいですね。
本記事では、Google Colabに加えて、Googleスプレッドシートも使って、スプレッドシートの内容を読み込み、SeleniumでWebサイトのフォームへ入力することを実現してみます。

　※ 本記事の内容を利用したことによるいかなる損害・不利益について、筆者は一切の責任を負いません

# やること・対象サイト
Seleniumのテスト用サイトとして提供されているホテルの予約サイトを模したサイトの会員登録ページを対象にします。
https://hotel.testplanisphere.dev/ja/

スプレッドシートのデータをもとにして、Seleniumでこのフォームへの入力を行います。
![スクリーンショット 2021-05-23 2.37.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1423540/f7f4eaf2-856b-e5cb-9488-f3ec888c2e5c.png)

流れとしては、以下の感じで行います。
1. Googleスプレッドシートから1人分の情報を取得
2. Seleniumでフォームの各項目に入力し、会員を登録
3. 登録完了後のページのスクリーンショットをGoogleドライブに保存
4. 1.〜3.を人数分繰り返し

# Google Colaboratoryの準備
以下の記事にしたがって、Google Colabを使えるようにしました。
詳細は割愛します。
[【10分で完了】Google Colabのインストール法・使い方](https://dreamer-uma.com/google-colab/)

# Seleniumの準備
[「Googlecolab を使って iRuby で selenium と chromium で徘徊する為に」](https://qiita.com/dauuricus/items/0717c8a08aba7abc7575)の記事前半のPythonコードを利用させていただき、Google Colab上でSeleniumを利用できるようにします。
日本語のサイトを表示するために、日本語フォントも導入しておきます。

```python
# ChromeDriver、日本語フォントの導入
!apt-get update
!apt-get install -y chromium-chromedriver fonts-ipafont fonts-ipaexfont
!ln -s /usr/lib/chromium-browser/chromedriver /usr/bin/chromedriver
!pip install selenium

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from selenium.webdriver.chrome.options import Options

# ノートブック上に画像を表示するために導入
from IPython.display import Image, display_png

# WebDriverを準備
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1280,1400')
driver = webdriver.Chrome(executable_path="/usr/bin/chromedriver", options=options)
driver.implicitly_wait(10)
```

# スプレッドシートからのデータの読み込み
事前に「member」というファイル名でスプレッドシートに5人分のダミー登録データを用意しました。
先頭はヘッダ行です。IDはフォームの入力項目にありませんが、スクリーンショットのファイル名に使うために付与しています。
![スクリーンショット 2021-05-23 3.19.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1423540/9a9f15ee-69a8-b87e-ed02-54acaec94005.png)

スプレッドシートからのデータの読み込みは、以下の記事を参考にさせていただきました。
詳細はリンク先をご参照ください。
　[Google ColaboratoryでGoogleスプレッドシートを読み書きしてみる](https://uepon.hatenadiary.com/entry/2018/04/08/110916)

関係する部分のコードを抜き出すと以下です。

```python
from google.colab import auth
from oauth2client.client import GoogleCredentials
import gspread
# 認証処理
auth.authenticate_user()
gc = gspread.authorize(GoogleCredentials.get_application_default())
# 「member」という名前のスプレッドシートの先頭ワークシートをオープンして、値を読み込み
worksheet = gc.open('member').get_worksheet(0)
member_list = worksheet.get_all_values()
```

# Googleドライブのマウント・ファイルの保存
Googleドライブのマウントについては、次の記事を参考にさせていただきました。
　[「ColaboratoryでのGoogle Driveへのマウントが簡単になっていたお話」](https://qiita.com/kado_u/items/45b76f9a6f920bf0f786)

マイドライブ内に`Colab`というフォルダを事前に作成しておき、そこにスクリーンショットを保存します。

```python
from google.colab import drive
# Googleドライブをマウント
drive.mount('/content/drive')

# スクリーンショットをGoogleドライブの「Colab」というフォルダに保存(idはデータ中のIDの値)
driver.save_screenshot('/content/drive/MyDrive/Colab/screenshot_' + id + '.png')
```

# フォームへの入力
フォームへの入力については、ページから地道に要素のname属性やxpath等を調べていき、値をセットしていくコードを作成しました。
会員登録ページを開き、1人分のデータを登録する関数は以下の感じです。

```python
# 1人分の情報を登録する関数
def register_member(member):
    id, email, password, username, rank, address, tel, gender, birthday, notification \
      = member[0], member[1], member[2], member[3], member[4], \
        member[5], member[6], member[7], member[8], member[9]
    # 性別用
    gender_index = {"男性": "1", "女性": "2", "その他": "9"}
    if gender in gender_index:
        gender_value = gender_index[gender]
    else:
        gender_value = "0"
    # 会員種別用
    rank_value = 'normal' if rank == '一般会員' else 'premium' 
    # テスト用の会員登録フォームに移動
    driver.get('https://hotel.testplanisphere.dev/ja/signup.html')
    # 各項目の入力
    driver.find_element_by_name("email").send_keys(email)
    driver.find_element_by_name("password").send_keys(password)
    driver.find_element_by_name("password-confirmation").send_keys(password)
    driver.find_element_by_name("username").send_keys(username)
    driver.find_element_by_xpath('//input[@name="rank" and @value="' + rank_value + '"]').click()
    driver.find_element_by_name("address").send_keys(address)
    driver.find_element_by_name("tel").send_keys(tel)
    # 性別の入力
    select = Select(driver.find_element_by_id("gender"))
    select.select_by_value(gender_value)
    # 生年月日の入力
    driver.find_element_by_name("birthday").clear()
    driver.find_element_by_name("birthday").send_keys(birthday)
    # お知らせを受け取る場合にチェックボックスにチェック
    if notification == "受け取る":
        driver.find_element_by_name("notification").click()

    driver.find_element_by_xpath('//button[text()="登録"]').click();
    # スクリーンショットをGoogleドライブの「Colab」というフォルダに保存
    driver.save_screenshot('/content/drive/MyDrive/Colab/screenshot_' + id + '.png')

    driver.find_element_by_xpath('//button[text()="ログアウト"]').click();
```

# コード全体
最後に、コード全体は以下のようになります。
実行するとGoogleドライブに登録した人数分のスクリーンショットが保存されました。

```python:colab.py
# ChromeDriver、日本語フォントの導入
!apt-get update
!apt-get install -y chromium-chromedriver fonts-ipafont fonts-ipaexfont
!ln -s /usr/lib/chromium-browser/chromedriver /usr/bin/chromedriver
!pip install selenium

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from selenium.webdriver.chrome.options import Options
from google.colab import auth, drive
from oauth2client.client import GoogleCredentials
import gspread
# ノートブック上に画像を表示するために導入
from IPython.display import Image, display_png

# 認証処理
auth.authenticate_user()
gc = gspread.authorize(GoogleCredentials.get_application_default())
# Googleドライブをマウント
drive.mount('/content/drive')

# WebDriverを準備
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1280,1400')
driver = webdriver.Chrome(executable_path="/usr/bin/chromedriver", options=options)
driver.implicitly_wait(10)

# 1人分の情報を登録する関数
def register_member(member):
    id, email, password, username, rank, address, tel, gender, birthday, notification \
      = member[0], member[1], member[2], member[3], member[4], \
        member[5], member[6], member[7], member[8], member[9]
    # 性別用
    gender_index = {"男性": "1", "女性": "2", "その他": "9"}
    if gender in gender_index:
        gender_value = gender_index[gender]
    else:
        gender_value = "0"
    # 会員種別用
    rank_value = 'normal' if rank == '一般会員' else 'premium' 
    # テスト用の会員登録フォームに移動
    driver.get('https://hotel.testplanisphere.dev/ja/signup.html')
    # 各項目の入力
    driver.find_element_by_name("email").send_keys(email)
    driver.find_element_by_name("password").send_keys(password)
    driver.find_element_by_name("password-confirmation").send_keys(password)
    driver.find_element_by_name("username").send_keys(username)
    driver.find_element_by_xpath('//input[@name="rank" and @value="' + rank_value + '"]').click()
    driver.find_element_by_name("address").send_keys(address)
    driver.find_element_by_name("tel").send_keys(tel)
    # 性別の入力
    select = Select(driver.find_element_by_id("gender"))
    select.select_by_value(gender_value)
    # 生年月日の入力
    driver.find_element_by_name("birthday").clear()
    driver.find_element_by_name("birthday").send_keys(birthday)
    # お知らせを受け取る場合にチェックボックスにチェック
    if notification == "受け取る":
        driver.find_element_by_name("notification").click()

    driver.find_element_by_xpath('//button[text()="登録"]').click();
    # スクリーンショットをGoogleドライブの「Colab」というフォルダに保存
    driver.save_screenshot('/content/drive/MyDrive/Colab/screenshot_' + id + '.png')
    # ノートブック上にも画像を表示
    display_png(Image('/content/drive/MyDrive/Colab/screenshot_' + id + '.png'))

    driver.find_element_by_xpath('//button[text()="ログアウト"]').click();

# 「member」という名前のスプレッドシートの先頭ワークシートをオープンして、値を読み込み
worksheet = gc.open('member').get_worksheet(0)
member_list = worksheet.get_all_values()

for i, member in enumerate(member_list):
    if i != 0: # 先頭のヘッダ行は読み飛ばす
        register_member(member)

driver.close()
driver.quit()
```

# 参考

- [Googlecolab を使って iRuby で selenium と chromium で徘徊する為に | Qiita](https://qiita.com/dauuricus/items/0717c8a08aba7abc7575)
- [【10分で完了】Google Colabのインストール法・使い方](https://dreamer-uma.com/google-colab/)
- [Google ColaboratoryでGoogleスプレッドシートを読み書きしてみる](https://uepon.hatenadiary.com/entry/2018/04/08/110916)
- [ColaboratoryでのGoogle Driveへのマウントが簡単になっていたお話 | Qiita](https://qiita.com/kado_u/items/45b76f9a6f920bf0f786)
- [Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ](https://www.codexa.net/how-to-use-google-colaboratory/)
- [Google Colaboratory(Colab)でスクレイピングするときのチートシート | Qiita](https://qiita.com/plumfield56/items/f344b23e453ed8a628b6)
- [HOTEL PLANISPHERE - テスト自動化練習サイト](https://hotel.testplanisphere.dev/)