「マネーフォワード」は、無料のオンライン家計簿、資産管理ツール。
銀行、クレジットカード、証券会社、年金、ポイント、マイルなどの情報を一度、登録するだけで、残高や支出の情報を自動で取り込み分析できる。
知人に教えてもらって手当たり次第に口座登録したら50万円程度のドルの埋蔵金が見つかった。完全に忘れてたわ。
素晴らしいツールだが、マネーフォワードの機能制限や有料プランへの誘導が強まり、利用者からは改悪との声が上がっている。
- 2015年11月以前:一括更新が終了、連携口座上限を10件に変更
- 2020年7月7日:資産推移、資産内訳が終了
- 2022年12月7日:連携口座上限4件に変更
その反動で提携版「マネーフォワード for ◯◯銀行」に移行する人が増えている。
提携版は今後も連携口座10件のまま維持される可能性が高いらしい。次のような利点がある。
- ① 連携口座数が10個(マネーフォワード無料版は4個)
- ② 無料版で円グラフ表示がある(マネーフォワードはプレミアム会員のみ)
- ③ 無料版でも「入出金・残高情報の一括管理」が可能
- ④ 無料版でもホーム画面で金融機関の残高が確認できる
同じような仕組みを自作しようと思ってた僕には素晴らしいWebサイト。
だけど各金融機関情報の更新が面倒で手動でポチポチ押す必要がある(プレミアム会員は一括ボタンがある)。
寝ている間に勝手にマネーフォワードの更新を実行してくれて、朝見たときには更新済み!
という状況にしたいので自動で金融機関情報を更新するスクリプトを作る。
利用提携サービスは住信SBIネット銀行版。
登録している金融機関情報を自動更新(Seleniumなし版)
さくらインターネット上ではChromedriverが動作しなかった。
だからSeleniumを使わず、まずは代替ライブラリのRequestsとBeautifulSoupを使用して実現を試したい。
- inputタグのnameとvalueの情報を集める
- action=””に記載してあるURLにPOSTする
という手順が必要。例えばログインするには
1 2 3 4 5 6 7 |
<form class="form-horizontal mf-mb40" id="new_sign_in_session_service" action="/session" accept-charset="UTF-8" method="post"> <input type="hidden" name="authenticity_token" value="トークン" autocomplete="off"> <input placeholder="メールアドレスを入力して下さい" class="js-focus-form" type="email" name="sign_in_session_service[email]" id="sign_in_session_service_email"> <input placeholder="パスワード(8?100文字)" type="password" name="sign_in_session_service[password]" id="sign_in_session_service_password"> <input class="checkbox" id="show-ps" name="new1" type="checkbox" value="1"> <input type="submit" name="commit" value="ログイン" id="login-btn-sumit" data-disable-with="ログイン"> </form> |
name | value |
---|---|
authenticity_token | トークン(毎回異なる) |
sign_in_session_service[email] | 自分のE-mail |
sign_in_session_service[password] | 自分のパスワード |
new1 | 1 |
commit | ログイン |
を渡す必要がある。そしてトークンはサイトにアクセスするたびに変化し、都度取得が必要。
コードは次のとおり。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
import requests from bs4 import BeautifulSoup import logging import time import sys args = sys.argv _format = "%(asctime)s %(levelname)s %(name)s :%(message)s" logging.basicConfig(filename="moneyforward.log", level=logging.DEBUG, format=_format) # セッションインスタンス作成 session = requests.Session() g_url = "https://ssnb.x.moneyforward.com" def login(email, password): login_url = g_url + "/users/sign_in" res = session.get(login_url) # BeautifulSoupオブジェクト作成(token取得の為) soup = BeautifulSoup(res.text, 'html.parser') token = soup.find(attrs={'name':'authenticity_token'}).get('value') cookie = res.cookies login_payload = { "authenticity_token": token, "sign_in_session_service[email]": email, "sign_in_session_service[password]": password, "new1": "1", "commit": "ログイン" } login_url = g_url + "/session" response = session.post(login_url, data=login_payload, cookies=cookie) response.raise_for_status() logging.debug("Login successful") return cookie def open_accounts_page(): accounts_url = g_url + "/accounts" logging.debug("Opening accounts page") response = session.get(accounts_url) response.raise_for_status() logging.debug("Accounts page opened") return response.text def click_reloads(html_content, cookie): soup = BeautifulSoup(html_content, "html.parser") reload_buttons = soup.find_all("input", {"data-disable-with": "更新"}) logging.debug(f"button: {len(reload_buttons)}") for button in reload_buttons: reload_url = button.parent.get("action") logging.debug(f"Reloading URL: {reload_url}") upload_payload = { "commit": "更新" } response = session.post(g_url + reload_url, data=upload_payload, cookies=cookie) response.raise_for_status() time.sleep(0.5) time.sleep(5) if __name__ == '__main__': try: cookie = login(args[1], args[2]) accounts_page_html = open_accounts_page() click_reloads(accounts_page_html, cookie) logging.info("DONE") except Exception as e: logging.error(e) |
ステータスコードも200が返って正常実行されているように見えるけど更新されていない。
更新ボタンはAjax処理だから正しく動かないのかな……
登録している金融機関情報を自動更新(Seleniumあり版)
こっちはググれば幾らでも出てくる。
記事が古くSelinum 4未対応なので修正を加える。
ChatGPT様の御神託によってww
© 泡沫に神は微睡む/安田 のら/KADOKAWA
ほんとに ありがてぇ……ありがたいこった!
これで一発で動くが少しアレンジする。
Selinum 4.6以降なので自動でchrome webdriverバージョン更新する
ChromeDriver自動更新といえば「webdriver-managerをインストール」を紹介する記事が多いが、Selenium 4.6以上であれば不要。
Selenium 4.6以前(従来)
「webdriver-manager」をインストール。
1 2 |
pip install webdriver-manager pip install selenium |
次のように書く。
1 2 3 4 |
from webdriver_manager.chrome import ChromeDriverManager # Chromeドライバーのパスを取得しChromeドライバーを初期化 driver = webdriver.Chrome(ChromeDriverManager().install()) |
Selenium 4.6以降
Selenium4.6以降 は「selenium manager」がデフォルトでついているため webdriver_manager を使わなくて良い。
seleniumのアップグレードとwebdriver-managerをアンインストール
1 2 |
pip install --upgrade selenium pip uninstall webdriver-manager |
コードはこれだけで良い。
1 |
driver = webdriver.Chrome() |
headlessモードではuser_agent を追加
ヘッドレスにすると次のエラーが出て Web ページ上の要素を見つけることができなかった。
ERROR root :Message: no such element: Unable to locate element: {“method”:”css selector”,”selector”:”[id=”sign_in_session_service_email”]”}
(Session info: chrome-headless-shell=121.0.6167.140); For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
スクリーンショットを取ると「Forbidden」と出ており、user_agent を設定すると正しく動作した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options import logging import time import sys import os # ログのフォーマット設定 if (os.path.isfile("moneyforward_reload_accounts.py.log")): os.remove("moneyforward_reload_accounts.py.log") _format = "%(asctime)s %(levelname)s %(name)s :%(message)s" logging.basicConfig(filename="moneyforward_reload_accounts.py.log", level=logging.DEBUG, format=_format) # グローバルなドライバー変数を初期化 driver = None def login(email, password): """指定されたメールアドレスとパスワードでログインする""" driver.get("https://ssnb.x.moneyforward.com/users/sign_in") driver.implicitly_wait(3) # driver.get_screenshot_as_file("screenshot.png") driver.find_element(By.ID, "sign_in_session_service_email").send_keys(email) driver.find_element(By.ID, "sign_in_session_service_password").send_keys(password) driver.find_element(By.ID, "login-btn-sumit").click() driver.implicitly_wait(3) def open_accounts_page(): """口座ページを開く""" driver.get("https://ssnb.x.moneyforward.com/accounts") def click_reloads(): """更新ボタンをクリックする""" elms = driver.find_elements(By.XPATH, "//input[@data-disable-with='更新']") for elm in elms: elm.click() driver.implicitly_wait(0.5) driver.implicitly_wait(5) if __name__ == '__main__': try: # ヘッドレスモードでブラウザを起動 options = webdriver.ChromeOptions() user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.50 Safari/537.36' options.add_argument(f'user-agent={user_agent}') options.add_argument('--headless') # Chromeドライバーを初期化 driver = webdriver.Chrome(options=options) # コマンドライン引数からメールアドレスとパスワードを取得してログイン login(sys.argv[1], sys.argv[2]) # 口座ページを開く open_accounts_page() # 更新ボタンをクリック click_reloads() # 処理が完了したことをログに記録 logging.info("DONE") except Exception as e: # エラーが発生した場合はログにエラーを記録 logging.error(e) finally: # ドライバーを終了 if driver: driver.quit() |
おわりに
やりたいことが一時間程度で終わった。
最近はChatGPT様の御神託を賜わることで実装は楽だけどエンジニアスキルが無いと完全に目的達成することは難しい。
効率化目的で使うには素晴らしいチートツール。
そして、これで口座情報の日々トラッキングが可能となった。