カード連携の不安よさらば!pythonで楽天カードの明細を家計簿へ登録する Part.1

こんにちは。ツバサです。家計簿のカード連携を自力でやってみようの第1回です。

今回は楽天カードのenaviへログインし、利用明細(csv)をダウンロードするまでを実装します。このプロジェクトの全体像についてはPart0に書いています。ぜひこちらもご覧ください↓

現在の動作環境はMac OS 11.2 python3.8です。

Mac OS のアップデートしないといけないですね・・・

フロー

明細をDLするフローはざっくりこんな感じです。

  1. 昨日(前回)DLした明細をyesterday.csvにリネーム
  2. enaviへログイン
  3. 1枚目のカードの明細をDL
  4. 2枚目のカードの明細をDL
  5. 3と4でDLした明細を合体

注意点

注意点は3つあります。

1つ目は、利用金額が確定しているかどうかです。

確定していると「次月」のボタンが押せるようになります。

最新月のデータが今月利用分のデータなので、「次月」ボタンが押せる時は押さないといけないです。

2つ目は、カードの選択についてです。

このセレクトボックスをクリックする時は、都度セレクトボックスのxpathを取得してください。

そうしないと、selenium.common.exceptions.StaleElementReferenceExceptionの例外が出ます。

詳しくはこちら↓

また、このセレクトボックスのxpathがたまに変わる?みたいで急にクリックできん!ってエラーが出て止まることがあります。その時は都度xpathを修正しています。

3つ目はDLボタンが広告で隠れてしまい、seleniumでクリックできないことです。

そのため、DLの処理だけはrequestsを使っています。

ここに関しては、参考にさせていただいた記事があります。それをこの記事の最後に紹介します。

プログラム

完成したプログラムを記載します。114行目のmain関数から処理が始まります。

import os
import time
from selenium import webdriver
from selenium.webdriver.support.select import Select        #セレクトボックス
from webdriver_manager.chrome import ChromeDriverManager    #webdriver自動更新用
import requests
import csv
import keyring  

# ① 昨日のデータの名前変更
def rename_csv_file(old_filename, new_filename):
    try:
        os.rename(old_filename, new_filename)
        print(f"File '{old_filename}' renamed to '{new_filename}' successfully.")
    except OSError as e:
        print(f"Error: {e}")

def get_today_csv():
    # ② enaviへログインする
    # ユーザ情報
    user = "****"
    pw = keyring.get_password('rakuten', user)
    #DL先のディレクトリを指定しておく
    script_path = os.path.abspath(__file__)
    download_dir = os.path.dirname(script_path)

    driver = webdriver.Chrome(ChromeDriverManager().install())      #webdriver自動更新して実行

    #chrome
    #楽天のログインページ
    driver.get('https://www.rakuten-card.co.jp/e-navi/members/statement/index.xhtml?l-id=enavi_all_glonavi_statement')
    #ページが開くまで待機
    time.sleep(5)

    #ID入力フォームを取得
    _user = driver.find_element_by_name('u')
    #pw入力フォームを取得する
    _pw = driver.find_element_by_name('p')

    _user.send_keys(user)
    _pw.send_keys(pw)

    #ログインボタン押下
    driver.find_element_by_id('loginButton').click()

    time.sleep(3)
    # ③ 1枚目のカードを選択
    card = driver.find_element_by_xpath('/html/body/div[2]/div/div[2]/div[4]/div[2]/form/div[2]/select')
    select = Select(card)
    select.select_by_index(0)	# 1枚目のカードを選択
    
    # 次月ボタンが押せるなら押す
    nextMonthButton = driver.find_elements_by_class_name('stmt-head-calendar__next')
    if nextMonthButton:
        nextMonthButton[0].click()

    # csvをダウンロードする    
    href = 'https://www.rakuten-card.co.jp/e-navi/members/statement/index.xhtml?downloadAsCsv=1'
    download_path1 = os.path.join(download_dir, 'card1.csv')
    c = {}
    for cookie in driver.get_cookies():
        c[cookie['name']] = cookie['value']

    # requestsを利用してデータのダウンロード
    r = requests.get(href, cookies=c)
    with open(download_path1, mode='wb') as f:
        f.write(r.content)

    # ④ 2枚目のカードを選択
    card = driver.find_element_by_xpath('/html/body/div[2]/div/div[2]/div[4]/div[2]/form/div[2]/select')
    select = Select(card)
    select.select_by_index(1)	# 2枚目のカードを選択
    # 次月ボタンがあったら押す
    nextMonthButton = driver.find_elements_by_class_name('stmt-head-calendar__next')
    if nextMonthButton:
        nextMonthButton[0].click()

    # # Chromeからクッキーデータを得る
    # csvをダウンロードする 
    download_path2 = os.path.join(download_dir, 'card2.csv')
    c = {}
    for cookie in driver.get_cookies():
        c[cookie['name']] = cookie['value']
    # requestsを利用してデータのダウンロード
    r = requests.get(href, cookies=c)
    with open('card2.csv', mode='wb') as f:
        f.write(r.content)

    #5sec待機
    time.sleep(5)
    driver.quit()

    # ⑤ 2つのcsvを合体
    data = []
    with open(download_path1, mode='r') as file:
        reader = csv.reader(file)

        for row in reader:
            data.append(row)

    with open(download_path2, mode='r') as file:
        reader = csv.reader(file)
        next(reader)  # 最初の行をスキップする

        for row in reader:
            # 行の処理
            data.append(row)

    today_path = './today.csv'
    with open(today_path, mode='w',newline='') as file:
        writer = csv.writer(file)
        writer.writerows(data)

def main():
    old_file = "today.csv"
    new_file = "yesterday.csv"
    # ① 昨日取得した明細をリネーム
    rename_csv_file(old_file, new_file)

    get_today_csv()

main()

2枚カードがあるのでダウンロードを2回実施します。

for文で書いた方がプログラムの行数は短くなりますが、所詮2回です。今回は愚直にダウンロードしていきます。

また、ログイン部分はkeyringというモジュールを使ってMacBookのキーチェーンに保存してあるパスワードを読み出します。

キーチェーンには「rakuten」という名前でパスワードを保存しています。この名前とユーザIDを指定することでパスワードの読み出しができます(プログラムの22行目)。

キーチェーンの画面はこんな感じです↓

まとめ

keyringを使うことで、パスワードの手入力不要、かつソースコードに埋め込み不要にできたことがGOODでした。

できればユーザIDも埋め込みたくないんですが、keyringはパスワードの取得しか出来ないみたいです・・・

次回は、本記事のプログラムで作成したyesterday.csvとtoday.csvの差分を出して、家計簿へ新たに登録が必要なデータだけを抽出します。

参考にしたサイト

明細をDLするところで、こちらの記事を参考にさせていただきました。

コメント

タイトルとURLをコピーしました