【Selenium + Python】カレンダー(bootstrap-datepicker)への自動入力

# はじめに
始めまして , 初心者SEの Nod_Yです.
Python で Selenium を使用して , カレンダ（bootstrap datepicker）へ日付を自動入力するのに少々苦戦したので, Selenium初心者の方への参考になれば良いなと思ってまとめてみました. 

## 環境
* Python Version ... 3.8.3
* pip Version ... 20.1.1
* Selenium Version ... 3.141.0
* 開発環境 ... "vanilla" Python + Atom
*Chrome Version ... 84.0.4147.89

## Selenium  のインストール
ターミナルから `pip install selenium` でインストールします.
pipにパスを通していない方は , `py(python) -m pip install selenium` でいけるはずです.

## コードテストに使用するサイト
カレンダーへの自動入力を試すwebサイトとして , 日本Seleniumユーザーコミュニティ様の練習用のサイトがありましたので , こちらを使わせていただきました.

(参考1)[日本Seleniumユーザーコミュニティ様のホームページ](http://www.selenium.jp/)

(参考2)[日本Seleniumユーザーコミュニティ様  テストサイト用サイトその2](http://example.selenium.jp/reserveApp_Renewal/)

## webDriver のダウンロード
今回は , Chrome を使ってカレンダーへの日付の入力を行いますので , Chrome の自動操作に必要な Driver のダウンロードが必要です.
Chrome Driver　のインストールは[ココ](https://chromedriver.chromium.org/downloads)からできます.
※ダウンロードする Driver のメジャーバージョンは , お使いの Chrome のメジャーバージョンと合わせてください.

## Chrome Driver の利用

### webDriver のインポート
```python
from selenium import webdriver
```
### webDriver の読み込み
```python
driver = webdriver.Chrome("ダウンロードしたchromedriver.exe へのパス")
```
### Driverでテストサイトを開く
Chrome Driver を使用して , テスト用のサイトを開きます.

```python
driver.get("http://example.selenium.jp/reserveApp_Renewal/")
```
## カレンダ（bootstrap datepicker）へ日付自動入力処理

### 処理の説明
今回のテーマである , カレンダへの日付入力処理を行います.入力方法は他にもいろいろあるのですが , 今回は手間の少ない datapicker に直接日付入力する方法を用います.この手法は , 以下の2つの処理からなります.

* 【処理】
 * datapicker の日付のデフォルト値に任意の日付を設定する.
 * datapicker の日付が変更されたというイベントを発生させる.

簡単に見える上記処理についてですが , pythonだけでは難しい（私の技術力がないだけかもしれませんが笑）ので , この部分の処理は JQuery(JavaScript) で行います.
※JQuery を 実行する方法については次項で説明します.

### JQuery(JavaScript) の実行方法
JQuery(JavaScript) の実行は簡単で webDriver のメゾットである , execute_script() を使用します.
使い方は , 以下の通りで引数として JQuery(JavaScript) のコードを入力します.

```python
driver.execute_script("JQuery(JavaScript)のコード")
```

### ①datapicker の日付のデフォルト値に任意の日付を設定する処理
datapicker にはデフォルトの日付を設定することができ , JQuery(JavaScript) を利用してデフォルト値として任意の日付を設定します.

```python
#カレンダーにを任意の日付を設定
driver.execute_script("$('#" + id + "').datepicker('setDate',new Date('" + value + "'));")
```

<br>
説明のために , 上記で実行しようとしている , JQuery(JavaScript)のコードを抜き出すと以下になります.（id , value は変数）
<br>

```js

$('#id').datepicker('setDate',new Date('value'));
```
はじめの `$('#id')` の部分ですがこれは選択する datapicker のIDを指定しています.
次に , `.datepicker('setDate',new Date('value'));` の部分については , JQuery から datepicker のデフォルト値に日付をセットするメゾットで , `datepicker('setDate', Date型で日付)` と書きます. ここでの `value` はDate型ではないため , `new Date('value')` でDate型への変換を行っています.

### ②datapicker の日付変更イベントの手動発生処理
①の処理で , カレンダ(bootstrap datepicker) のデフォルト値を設定することで , カレンダに任意の日付を入力することができました.
この処理により , 下の図のようにデフォルトの日付が入力した任意の日付に設定し直されます.

デフォルトの日付![before_date.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672648/e8bf1b48-4626-6c19-fb5f-a4179bca9497.png)
設定値変更後の日付（処理①のみ実行）![after_date.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672648/95fb8235-80eb-7092-bc52-ad9454b4f77a.png)
これらの図をみると一見これだけで入力が上手くいっているように見えます .しかし , フォームで送る値について考えた時 , datepickerの仕様上フォームで送る値に datepicker からの値が反映されるのは datepicker の値が変更されたというイベントが起きた時になります（今回はデフォルト値として日付を設定したので , 変更のイベントが発生しない）. 

このことから , 上の図の状態でフォームを送信してしまうと , フォームで送る値にデフォルト値として入力されている`2020/07/21`(図の場合)が送信されてしまいます. なので , 設定した datepickerの 値をフォームで送る値にも反映させるため , 手動で datapicker の日付変更イベントを起こす必要があります. 

#### datapicker の日付変更イベントの手動発生
datapicker の日付変更イベントを手動で起こすには , 下記のようにします.

```python
driver.execute_script("$('#" + id + "').datepicker().trigger('changeDate');")
```
<br>
同様に , 上記で実行しようとしている , JQuery(JavaScript)のコードを抜き出すと以下になります.（id , value は変数）
<br>

```js

$('#id').datepicker().trigger('changeDate');
```
このコードは , 指定IDの datepicker で trigger() の引数で指定したイベントを起こすというものです.
`changeDate` イベントは bootstrap-datepicker において日付が変更されたときに起こるもので , これを手動で起こすことにより
今回デフォルト値として設定した値をフォームで送る値に反映させることができます.
(参考3)[bootstrap-datepicker の Events のマニュアル](https://bootstrap-datepicker.readthedocs.io/en/latest/events.html)

## まとめ
以上で , カレンダ(bootstrap-datepicker)への自動入力する方法についての話は終了となります.
今回 , 苦労した点は2点ありまして

* datepicker のデフォルト値を設定する際 , 引数としての日付の入力をDate型で行う必要があるということになかなか気づけなかったこと
* datepicker の値が変更された時に起こる , イベントを見つけるのに時間がかかったこと

です.

反省としましては , めんどくさがって bootstrap-datepicker のマニュアルをしっかり読まなかったことなので次回からはマニュアル等をちゃんと読むようにしようと思います.
ここまで , 付き合っていただいた方はありがとうございました. また , 機会があればメモ程度の気持ちで投稿しようと思います.

## プログラム全体

```python:scraiping_col.py
from selenium import webdriver #webdriverのインポート

id = 'datePick' #操作対象のカレンダー(daterpicker)のID
value = '2020/07/23' #任意の日付

driver = webdriver.Chrome(r"C:\Users\ngold\OneDrive\chromedriver_win32\chromedriver.exe") #Chromeを動かすドライバを読み込み
driver.get("http://example.selenium.jp/reserveApp_Renewal/")
#カレンダーにを任意の日付を設定
driver.execute_script("$('#" + id + "').datepicker('setDate',new Date('" + value + "'));")
#日付変更イベントを強制的に起こす
driver.execute_script("$('#" + id + "').datepicker().trigger('changeDate');")
```

## 参考
文中で記載した以外にも , 以下のサイトを参考にさせていただいています. 参考にさせていただいたサイトの方はありがとうございました.
（参考4）(https://arakan-pgm-ai.hatenablog.com/entry/2019/09/19/000000)
（参考5）(https://stackoverflow.com/questions/45175100/bootstrap-datepicker-trigger-change)