あなたの代わりに勤怠ボタン押す太郎くん

# はじめに
AdventCalendar初参加です。
拙いですが誰かのお役に立てば嬉しいです:nerd:

本題ですが、皆さんの会社の勤怠システムはなんですか？
うちはWeb勤怠（e-clocking）を使っています。
出勤してPC起動してブラウザ立ち上げてe-clockingにアクセスしてログインして出勤！:runner:
と割と手間がかかります。
毎朝これはしんどいなーと思ってPythonとSeleniumのライブラリを使って自動化してみました。
これ覚えるとブラウザベースの単純なオペレーションは自動化できるので、E2Eテストとか細々したツールとかに使えます。

# 前提
Python3を実行できる環境とChromedriverがあることが前提です。

# 実装
いきなりソース

```syukkin.py
# 勤怠押す太郎
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import configparser
import os
import datetime

inifile = configparser.ConfigParser()
path = os.getcwd()
# ログイン用設定ファイルの読込
inifile.read(path+'\\kintai.ini', 'UTF-8')

# Chrome起動
options = webdriver.ChromeOptions()
options.add_argument('--headless')                  # ヘッドレスモード
options.add_argument('--disable-gpu')               # 昔必要だったらしい引数
options.add_argument('--ignore-certificate-errors') # SSLエラー無視
# chromedriverのバージョンを指定
driver = webdriver.Chrome(options=options, executable_path='chromedriverを置いた場所\\chromedriver.exe')

# 勤怠へアクセス
driver.get('https://cl.i-abs.co.jp/e-clocking/login.asp')
print(driver.title)
# フレームを取得して切替
frame=driver.find_elements_by_xpath("//frame")[1]
driver.switch_to.frame(frame)
time.sleep(1)

driver.find_element_by_name('DataSource').send_keys(inifile.get('user_info', 'company'))
driver.find_element_by_name('LoginID').send_keys(inifile.get('user_info', 'id'))
driver.find_element_by_name('PassWord').send_keys(inifile.get('user_info', 'pass'))
driver.find_element_by_name('btnLogin').click()

time.sleep(1)
driver.find_element_by_name('IN').click()
driver.find_element_by_name('punch').click()
time.sleep(1)
driver.save_screenshot(str(datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S")) + '.png')
driver.find_element_by_name('OK').click()
driver.quit()

```

```kintai.ini
[user_info]
company=DataSourceに入力する値
id=ユーザーID
pass=パスワード
```

## 解説
driver起動前のオプション設定と自動操作部分と分けて解説します。
### オプション設定
```
# Chrome起動
options = webdriver.ChromeOptions()
options.add_argument('--headless')                  # ヘッドレスモード
options.add_argument('--disable-gpu')               # 昔必要だったらしい引数
options.add_argument('--ignore-certificate-errors') # SSLエラー無視
```
人間が操作する場合画面が必要ですが、プログラムはHTMLから該当タグが特定できれば問題なく操作できるので画面無しのヘッドレスモードのオプションを渡します。
動作が軽くなります:muscle:

#### ChromeDriverのバージョン指定
```
# chromedriverのバージョンを指定
driver = webdriver.Chrome(options=options, executable_path='chromedriver.exe')
```
webdriverにchromedriver.exeの場所ををわざわざ指定しています。
これはChromeブラウザのアップデートによってSeleniumがサポートしてるバージョン外となり、ある日突然プログラムが動作しなくなるのでこうしています。
昨日まで景気よく動いていたのに！！と知らない頃はパニックになりました:fearful:
このエラーについてはこちらの記事が参考になります
[ChromeDriverとChromeバージョン違いで起こるエラー](https://qiita.com/H2O_HoriHori/items/ad7f9855cfc20b995c42)

**追記 2020/01/30**
ライブラリのサポート外バージョンになる以外だと、使ってるChromeとChromedriverのバージョンが乖離しても発生するようです。
一度Chromeのバージョンを確認して同じバージョンのものを取得しなおしましょう。
[ChromeDriver](https://chromedriver.chromium.org/downloads)

### 自動操作
```
# 勤怠へアクセス
driver.get('https://cl.i-abs.co.jp/e-clocking/login.asp')
print(driver.title)
# フレームを取得して切替
frame=driver.find_elements_by_xpath("//frame")[1]
driver.switch_to.frame(frame)
time.sleep(1)
```
e-clockingのURLへアクセスします。
早速ここで一癖あります。
Frameタグを使われているので太郎くんは全然勤怠ボタンが無い方のファイルに対して操作をし始めます。
なので2番目に読み込んでる方で作業してくれと指示を出します。

```
driver.find_element_by_name('DataSource').send_keys(inifile.get('user_info', 'company'))
driver.find_element_by_name('LoginID').send_keys(inifile.get('user_info', 'id'))
driver.find_element_by_name('PassWord').send_keys(inifile.get('user_info', 'pass'))
driver.find_element_by_name('btnLogin').click()

time.sleep(1)
driver.find_element_by_name('IN').click()
driver.find_element_by_name('punch').click()
time.sleep(1)
driver.save_screenshot(str(datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S")) + '.png')
driver.find_element_by_name('OK').click()
driver.quit()
```
あとはname属性を頼りにfind_element_by_nameで入力ボックスとボタンを探して操作するだけです。
入力値をinifileからgetして入力。
ログインボタンをclickしてログイン。
その後は出勤ボタンを押して、確認ボタンを押すといった流れです。
一切画面が表示されないので念のためsave_screenshotで打刻画面のキャプチャを撮ります。

本当は画面の読込まで待機する処理を入れた方がいいのですが、sleep(1)を入れてお茶を濁しています。
（画面の読込に時間がかかった時はここら辺で落ちるかもしれないです）


ちなみに、退勤したいときはelement_by_name('IN')のところを
`driver.find_element_by_name('OUT').click()`
とするだけです。

## バッチ化
面倒なのでバッチファイルをダブルクリックするだけで終わらせたいのでこうします。

```syukkin.bat
cd pyファイルを置いてるフォルダ
python syukkin.py
```
デスクトップあたりにbatファイルを置いておくとすぐ使える。
exeファイルにしたほうがもっといいかも。

# おわりに
凄くシンプルなのですが作るときに結構躓いた（特にChromedriverのバージョン…）ので参考になれば幸いです。
補足情報や改善点ありましたら是非編集リクエストお願いします:bow_tone1: