【Python】SeleniumでYogiboの半額セールに挑む

# What
*Python Selenium*を利用して`2022年11月25日 00:00`販売開始予定の[**Yogibo**](https://yogibo.jp/midnight2022)半額セール対象商品を自動購入する
![Screen Shot 2022-11-23 at 17.41.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377231/7e00ce0c-d5e0-d947-6681-2bac07f1e8e5.png)

# Why
半額セール開始時に多くの人がアクセスするのでものの数秒で売り切れてしまう．
Pythonによって購入までのプロセスを完全に自動化することで，購入できる可能性を飛躍的に向上させる．

# Env
- macOS Big Sur
- Python 3.9.12
- Chrome version 107.0.5304.110


# Script
下記Pythonスクリプトを作成した
これにより，設定した時刻に自動で商品が購入される
また，購入するYogiboが本当に半額になっているかのチェックもコード内で行っている
```python:yogibo.py
#coding:utf-8
#copyright@shuto.kawabata
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.chrome.options import Options
from time import sleep
import datetime, warnings
warnings.simplefilter('ignore')

# config for login
EMAIL = "taro.gundam@gmail.com"
PASSWORD = "mypassword"

# config for start time
STARTTIME = "2022年11月25日 00:00:00"

# config for credit card
CARDNUMBER = "0000111122223333"
FIRSTNAME = "TARO"
LASTNAME = "RAKUTEN"
EXPIREDMONTH = "03"
EXPIREDYEAR = "23"
SECURITYCODE = "999"

# ChromeDriverの起動
options = Options()
driver = webdriver.Chrome('./chromedriver')
driver.implicitly_wait(10) #最大待機時間10s

# ログイン
driver.get("https://yogibo.jp/mypage/login")
driver.find_element_by_id('login_email').send_keys(EMAIL)
driver.find_element_by_id('login_pass').send_keys(PASSWORD)
driver.find_element_by_xpath('//*[@id="login-input"]/button').click()

prevTime = ""
while True:
    sleep(0.1)

    # 現在時刻の取得
    dt_now = datetime.datetime.now()
    currentTime = dt_now.strftime('%Y年%m月%d日 %H:%M:%S')
    
    if currentTime!=prevTime:
        print(currentTime)

    prevTime = currentTime

    # 販売開始時刻まで待機
    if currentTime == STARTTIME:
        
        # 商品をカートへ入れる
        driver.get("https://yogibo.jp/products/detail/min")
        driver.find_elements_by_tag_name('button')[12].click() #ライトグレー
        driver.find_element_by_xpath('//*[@id="form_cart"]/div[3]/button').click() #購入手続きへボタン

        # 銀行振り込みを選択
        driver.find_element_by_id('shopping_payment_1').click()

        # クレジットカード情報の入力
        #driver.find_element_by_id('gmo_payment_card_no').send_keys(CARDNUMBER)
        #driver.find_element_by_id('gmo_payment_method').send_keys('一括払い')
        #driver.find_element_by_id('gmo_payment_security_code').send_keys(SECURITYCODE)
        #driver.find_element_by_id('gmo_payment_expire_month').send_keys(EXPIREDMONTH)
        #driver.find_element_by_id('gmo_payment_expire_year').send_keys(EXPIREDYEAR)
        #driver.find_element_by_id('gmo_payment_card_name1').send_keys(FIRSTNAME)
        #driver.find_element_by_id('gmo_payment_card_name2').send_keys(LASTNAME)

        # きちんと半額になってるのを確認してから購入確定ボタンをプッシュ
        if  driver.find_element_by_class_name('item-sum').text == "小計：8,900円":
            # 購入確定ボタン
            driver.find_element_by_id('order-button').click() 
        
        driver.close()
        break
```

# How to use
https://chromedriver.chromium.org/downloads

↑から自分のChromeのバージョンに合った***Chrome driver***をダウンロード後，スクリプトと同ディレクトリ内に設置

```python
# config for login
EMAIL = "taro.rakuten@rakuten.com"
PASSWORD = "mypassword"
```
を自分のYogiboアカウントへ置き換える

コマンドライン上で下記コマンド実行

```bash
$ python3 yogibo.py
```

※決済方法として銀行振り込みを指定している．
クレジットカードで支払う場合は


```python
# config for credit card
CARDNUMBER = "0000111122223333"
FIRSTNAME = "TARO"
LASTNAME = "RAKUTEN"
EXPIREDMONTH = "03"
EXPIREDYEAR = "23"
SECURITYCODE = "999"
```
を自身のクレジットカード情報へ修正し，フォーム入力を実行するため以下のようにアクティベート状態にする

```python
# クレジットカード情報の入力
driver.find_element_by_id('gmo_payment_card_no').send_keys(CARDNUMBER)
driver.find_element_by_id('gmo_payment_method').send_keys('一括払い')
driver.find_element_by_id('gmo_payment_security_code').send_keys(SECURITYCODE)
driver.find_element_by_id('gmo_payment_expire_month').send_keys(EXPIREDMONTH)
driver.find_element_by_id('gmo_payment_expire_year').send_keys(EXPIREDYEAR)
driver.find_element_by_id('gmo_payment_card_name1').send_keys(FIRSTNAME)
driver.find_element_by_id('gmo_payment_card_name2').send_keys(LASTNAME)
```

またテスト時は
```python
# 購入確定ボタン（※購入してしまうのでテスト時はコメントアウトしておく!!!）
driver.find_element_by_id('order-button').click() 
```
を
```python
# 購入確定ボタン（※購入してしまうのでテスト時コメントアウトしておく!!!）
# driver.find_element_by_id('order-button').click() 
```
の状態にしておく（でないと予定にない購入をしてしまう）

# Evaluation
試しに下記商品を自動購入することで本ツールの事前評価を行なった

https://yogibo.jp/products/detail/slp

|サイズ選択|カラー選択|販売開始時刻(仮)|スクリプト実行時刻|
|---|---|---|---|
|L|Blue|2022年11月23日 19:00|2022年11月23日 18:30|


|評価項目|速度[s]|
|---|---|
|スクリプト実行→ログインPL完了|2.23|
|ログインPL完了→認証完了|0.34|
|販売開始時刻→購入完了|1.67|

# Conclusion

`販売開始時刻→購入完了`が**2.0**秒以下という結果から，速度的にはセール開始時刻とほぼ同時に購入完了可能と結論づけることができる．ただし，懸念していることが2点ある．それは，セール開始と同時に，現在https://yogibo.jp/midnight2022 でマッピングされている<INS>各対象商品ページURLがセール用に変更されてしまわないか</INS>と言う点．もし変更されてしまうと，それに対してスクリプトが適用できなくなる...😭．もう一つはロードバランサー等インフラ側でそもそもリクエストが弾かれてしまうまたは503などのタイムアウトがありうる点である．

まあ準備は完璧！あとは25日を待つのみ

以上


# Ref

https://qiita.com/DisneyAladdin/items/2b685c5c78a04f425978


# 追記
日付：2022/11/27
結果：購入できませんでした（しませんでした）
理由：狙ってたYogiboがまさかの10%オフ(`小計：17,622円`)により
```python
# きちんと半額になってるのを確認してから購入確定ボタンをプッシュ
if  driver.find_element_by_class_name('item-sum').text == "小計：8,900円":
    # 購入確定ボタン
    driver.find_element_by_id('order-button').click()
```
の購入確定ボタンクリックの行が実行されずにそこでボットが処理停止したから

まあ10％なら元々買う気なかったらいいか．

久しぶりに**Python**かくきっかけにはなりました．おしまい．