ポイ活：年間4800ポイントを貰える住信SBIネット銀行の即時決済をSeleniumで自動化してみた

# 概要
銀行の残高や取引を活用した「銀行ポイ活」で効率よく貯められる銀行のひとつが住信SBIネット銀行。
この住信SBIネット銀行のVポイント支店と第一生命支店では即時決済を1回行うたびに20ポイント付与されるサービスをそれぞれ提供している。
即時決済の対象となる決済方法は複数あるが、筆者が利用しているSBI証券への入金も対象となっているため、毎月SBI証券へ入金を行っていた。

![スクリーンショット 2024-05-11 午後15.11.29 午後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789698/43a6ea23-9d17-1894-5b8e-bbcde5e5419f.png)
引用：https://tneobank.tsite.jp/merit/
参考：https://www.netbk.co.jp/contents/lineup/pointprogram-daiichilife/point/

# 自動化に至った経緯
上記支店での即時決済1回につき20ポイントが付与されるが、即時決済の判定は1日につき1回までという制約があるため、月の上限である200ポイントを貰うためには10日にわたってSBI証券への入金を行う必要がある。

手動での入金と実施回数の管理をするのが面倒だと感じていたため、ブラウザ操作の自動化で入金処理の自動化ができないか試してみた。

![スクリーンショット 2024-05-11 午後15.14.44 午後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789698/1c71cc45-24df-81fb-eb5c-3d496e3870da.png)
引用：https://tneobank.tsite.jp/merit/

# 環境
プログラムの実行環境を以下に記載する。
#### ハードウェア
M1 Mac mini（メモリ16GB、ストレージ256GB）
#### OS
macOS sonoma バージョン14.4.1
#### ブラウザ
Google Chrome バージョン124.0.6367.119
#### 言語
Python 3.12.2

# 使用技術
#### Selenium
Chromeの自動化ライブラリとしてSeleniumが提供されているため、Seleniumを用いて自動入金処理を実装する。Seleniumは、Java、Python、C＃などの言語をサポートしているが、簡単に実装できるPythonを選択。

#### LINE Notify API
処理を自動化するにあたって、プログラムが実行されたタイミングと終了したタイミングで通知を受け取れるようにLINE Notify APIを使用する。

#### cron
実装したプログラムを日次で自動実行するために、OSに標準搭載されているcronを使用する。

# ライブラリのインストール
プログラムの実行にあたって以下のライブラリが必要となるため、ターミナルで以下のコマンドを実行して必要なライブラリをインストールする。
※筆者はローカルを汚したくなかったため、venvで仮想環境を用意してライブラリのインストールを行ったが、仮想環境の構築方法については割愛する。仮想環境を用意しなくても処理は動作する。

```
pip3 install selenium
pip3 install chromedriver
pip3 install requests
```

# 用意したプログラム
自動入金処理とLINE通知処理を実現するために、以下のプログラムを用意した。
・auto_payment.py
・line_notify.py
・cron.py


### auto_payment.py
SBI証券への自動入金処理はauto_payment.pyで実装する。
注意点として、SBI証券および住信SBI証券の認証情報については、利用者によって異なるため、適宜置換すること。

```python:main.py
# ======================================================
# ライブラリ
# ======================================================

# 待機時間
from time import sleep

# Selenium
from selenium import webdriver
from selenium.webdriver.common.by import By

# ======================================================
# 定額自動入金のメイン処理
# ======================================================

def auto_payment_main(neo_bank_select_btn):
    # ======================================================
    # Chrome起動設定
    # ======================================================

    # オプションの設定
    options = webdriver.ChromeOptions()

    # Chromeプロファイルの指定
    options.add_argument("--user-data-dir=/Users/<ユーザ名>/Library/Application Support/Google/Chrome")

    # Selenium実行後もChromeを開いたままにする
    options.add_experimental_option('detach', True)

    # ポップアップウィンドウの許可設定
    options.add_argument('--disable-popup-blocking')

    # ======================================================
    # SBI証券ログイン処理
    # ======================================================

    # Chromeブラウザを開く
    driver = webdriver.Chrome()

    # 指定のURLを開く
    url_top = 'https://site3.sbisec.co.jp/ETGate/'
    driver.get(url_top)

    sleep(2)

    # SBI証券のユーザ名入力
    sbi_user_id = '<SBI証券のユーザIDを直接入力してください>'
    sbi_login_user_id = driver.find_element(By.NAME, 'user_id')
    sbi_login_user_id.send_keys(sbi_user_id)

    # SBI証券のログインパスワード入力
    sbi_password = '<SBI証券のログインパスワードを直接入力してください>'
    sbi_login_user_password = driver.find_element(By.NAME, 'user_password')
    sbi_login_user_password.send_keys(sbi_password)

    # SBI証券のログインボタン押下
    sbi_login_btn = driver.find_element(By.NAME, 'ACT_login')
    sbi_login_btn.click()

    sleep(2)

    # ======================================================
    # 振込指示処理
    # ======================================================

    # SBI証券の入金ボタンを押下
    xpath_payment_btn = '//*[@id="link02M"]/ul/li[4]/a/img'
    sbi_payment = driver.find_element(By.XPATH, xpath_payment_btn)
    sbi_payment.click()

    sleep(2)

    # SBI証券の入金額を入力
    sbi_payment_amount = '10000'
    sbi_payment_amount_form = driver.find_element(By.NAME, 'FML_TRANSFER_AMOUNT')
    sbi_payment_amount_form.send_keys(sbi_payment_amount)

    # SBI証券の取引パスワード入力
    sbi_transaction_password = '<SBI証券の取引パスワード入力してください>'
    sbi_payment_transaction_password = driver.find_element(By.NAME, 'transefer_pass')
    sbi_payment_transaction_password.send_keys(sbi_transaction_password)

    # SBI証券の次へボタンを押下
    sbi_next_btn = '//*[@id="MAINAREA02_780"]/form[1]/div/div/div/div/div/div[2]/a/img'
    sbi_next = driver.find_element(By.XPATH, sbi_next_btn)
    sbi_next.click()

    sleep(2)

    # SBI証券の振込指示ボタンを押下
    sbi_transfer_instructions_btn = '//*[@id="MAINAREA02_780"]/div[4]/ul/li[1]/form/a/input'
    sbi_transfer_instructions = driver.find_element(By.XPATH, sbi_transfer_instructions_btn)
    sbi_transfer_instructions.click()

    sleep(2)

    # ======================================================
    # 住信SBIネット銀行ログイン処理
    # ======================================================

    # ウィンドウを切り替える
    handle_array = driver.window_handles
    driver.switch_to.window(handle_array[1])

    # 支店を押下
    neo_bank_select = driver.find_element(By.XPATH, neo_bank_select_btn)
    neo_bank_select.click()

    sleep(2)

    # 住信SBIネット銀行のユーザネームを入力
    neo_bank_user_id = '<住信SBIネット銀行のユーザネームを入力してください>'
    neo_bank_login_user_id = driver.find_element(By.NAME, 'userNameNewLogin')
    neo_bank_login_user_id.send_keys(neo_bank_user_id)

    # 住信SBIネット銀行のログインパスワード入力
    neo_bank_user_password = '<住信SBIネット銀行のログインパスワードを入力してください>'
    neo_bank_login_user_password = driver.find_element(By.ID, 'loginPwdSet')
    neo_bank_login_user_password.send_keys(neo_bank_user_password)

    # 住信SBIネット銀行のログインボタン押下
    neo_bank_login_btn = '/html/body/app/div/ng-component/div/main/ng-component/div/form/section/div/div/ul/li/nb-button-login'
    neo_bank_login = driver.find_element(By.XPATH, neo_bank_login_btn)
    neo_bank_login.click()

    sleep(2)

    # 住信SBIネット銀行の取引パスワード入力
    neo_bank_transaction_password = '<住信SBIネット銀行の取引パスワードを入力してください>'
    neo_bank_payment_transaction_password = driver.find_element(By.ID, 'toriPwd')
    neo_bank_payment_transaction_password.send_keys(neo_bank_transaction_password)

    # 確定するボタン押下
    neo_bank_submit_btn = '/html/body/app/div/ng-component/div/main/ng-component/section[2]/div/ul/li/nb-button/a'
    neo_bank_submit = driver.find_element(By.XPATH, neo_bank_submit_btn)
    neo_bank_submit.click()

    sleep(10)
    driver.quit()
```

### line_notify.py
LINEへの通知処理はline_notify.pyで実装する。
注意点として、LINE Notify APIのパーソナルアクセストークンは各自で取得すること。

```python:line_notify.py
# ======================================================
# ライブラリ
# ======================================================
import requests

# ======================================================
# LINE通知のメイン処理
# ======================================================
def line_notify(message):
    line_notify_token = '<LINE Notify APIのパーソナルアクセストークンを入力してください>'
    line_notify_api = 'https://notify-api.line.me/api/notify'
    payload = {'message': message}
    headers = {'Authorization': 'Bearer ' + line_notify_token}
    requests.post(line_notify_api, data=payload, headers=headers)
```

### cron.py
auto_payment.pyで実装した自動入金処理とline_notify.pyで実装した通知処理を実行するプログラムとしてcron.pyを実装する。
本プログラムを毎月の1日から10日の0:01に自動実行するよう、cronの設定を別途行う必要がある。

```python:cron.py
# ======================================================
# 関数のインポート
# ======================================================
from auto_payment import auto_payment
from line_notify import line_notify

# 処理の開始をNotifyに通知
line_notify('SBI証券への自動入金処理を開始します。')

# V NEOBANKからSBI証券への入金処理を実行
if __name__ == '__main__':
    try:
        # Vポイント支店からSBI証券へ1万円入金
        v_neo = auto_payment('//*[@id="CCC"]/label/div/div/div/ul/li/div/img')
    except Exception as e:
        line_notify(e)
    else:
        line_notify('Vポイント支店からSBI証券へ1万円入金しました。')

# 第一生命 NEOBANKからSBI証券への入金処理を実行
if __name__ == '__main__':
    try:
        # Vポイント第一生命支店からSBI証券へ1万円入金
        Dai_ichi_neo = auto_payment('//*[@id="DLB"]/label/div/div/div/ul/li/div/img')
    except Exception as e:
        line_notify(e)
    else:
        line_notify('第一生命支店からSBI証券へ1万円入金しました。')
```

# cron設定
ターミナルで以下のコマンドを実行して、cron.pyを毎月1日~10日の00:01に自動実行する。

```ターミナル:ターミナル
# rootユーザにスイッチ
sudo su -

# cronの編集画面を表示
crontab -e
```
上記コマンドを実行後、以下のコマンドを実行してcronの設定を行う。

```cron:cron
# cronの編集モードを有効化
i

# 以下をペーストしてcronの実行条件を入力
0 0 1-10 * * pmset repeat wake MTWRFSU 00:00:30
1 0 1-10 * * python3 <cron.pyのフルパスを記入してください>
5 0 1-10 * * pmset repeat sleep MTWRFSU 00:06:00

# 設定を反映
:wq

# cronの設定が保存されていることを確認
crontab -l
```

# 今後の展望
上記プログラムを実装したことで目的である入金処理の自動化に加えて、LINE Notifyによる通知の受け取りを達成した。
一方で、本プログラムは自動化処理の検証のため、セキュリティ面については考慮できていない。
プログラム内に認証情報を埋め込む設計としているため、情報漏洩した際のリスクを抱えている。
セキュリティの向上のために、認証情報の暗号化を検討する必要がある。