RustでZaimと東京ガスを連携させてみた【1】

最近、昨年の医療費控除を受けるために確定申告の準備などをしているのですが、家計簿ソフトで利用しているZaimにガスや水道代が勝手に計上されてくれると便利だな〜と思い、Zaim APIとSeleniumを使って実現してみようと思います。 Zaim Developers Center 東京ガスのCSVファイルをダウンロードする Rustのheadless-chromeを使って構築します。 とりあえずCargoパッケージ作ってライブラリを追加します。 cargo new sync-zaim cargo add headless-chrome 追加できました。 使い勝手を知るために、まずはGoogleにアクセスしてスクショをとってみます。 use headless_chrome::{protocol::cdp::Page::CaptureScreenshotFormatOption, Browser}; use std::fs; fn main() { let browser = Browser::default().unwrap(); let tab = browser.new_tab().unwrap(); tab.set_default_timeout(std::time::Duration::from_secs(200)); tab.navigate_to("https://www.google.com/").unwrap(); tab.wait_until_navigated().unwrap(); let jpeg_data = tab.capture_screenshot( CaptureScreenshotFormatOption::Jpeg, None, None, true, ).unwrap(); fs::write("screenshot.jpg", jpeg_data).unwrap(); } 実行すると、Googleの画面のスクショが保存されました。 この調子で東京ガスのCSVを取得できるようにしましょう。 コードはpythonですがここを参考にします。 Python(Selenium)で東京ガスのcsvをダウンロード|gadgetking 実行すると少しエラーは出ますが、ファイルダウンロードは可能になりました。 use headless_chrome::{protocol::cdp::Page::CaptureScreenshotFormatOption, Browser}; use headless_chrome::protocol::cdp::types::Method; use std::sync::Arc; use std::{env, thread, time, error::Error}; use std::{fs, path}; use serde::Serialize; const TOKYO_GAS_LOGIN_URL: &str = "https://members.tokyo-gas.co.jp/login.html"; const GAS_ID: &str = "東京ガスのログインID"; const GAS_PASSWD: &str = "東京ガスのログインパスワード"; // https://github.com/rust-headless-chrome/rust-headless-chrome/issues/187 #[derive(Serialize, Debug)] struct Command { behavior: &'static str, downloadPath: &'static str, } impl Method for Command { const NAME: &'static str = "Page.setDownloadBehavior"; type ReturnObject = serde_json::Value; } fn main() -> Result<(), Box<dyn Error>> { let browser = Browser::default().unwrap(); let tab = browser.new_tab().unwrap(); tab.set_default_timeout(std::time::Duration::from_secs(200)); // https://github.com/rust-headless-chrome/rust-headless-chrome/issues/280 tab.enable_request_interception(Arc::new(|transport, session_id, params| { println!("request!"); println!("transport: {:?}", transport); println!("session_id: {:?}", session_id); println!("params: {:?}", params); headless_chrome::browser::tab::RequestPausedDecision::Continue(None) }))?; tab.navigate_to(TOKYO_GAS_LOGIN_URL).unwrap(); tab.wait_until_navigated().unwrap(); let jpeg_data = tab.capture_screenshot( CaptureScreenshotFormatOption::Jpeg, None, None, true, ).unwrap(); fs::write("screenshot.jpg", jpeg_data).unwrap(); tab.wait_for_element("#loginId").unwrap().type_into(GAS_ID).unwrap(); tab.wait_for_element("#password").unwrap().type_into(GAS_PASSWD).unwrap(); tab.wait_for_element("#submit-btn").unwrap().click().unwrap(); // tab.wait_until_navigated().unwrap(); thread::sleep(time::Duration::from_secs(5)); let jpeg_data = tab.capture_screenshot( CaptureScreenshotFormatOption::Jpeg, None, None, true, ).unwrap(); fs::write("screenshot.jpg", jpeg_data).unwrap(); let path_string = env::current_dir()?.into_os_string().into_string().unwrap(); // println!("pwd: {}", path_string); let static_path_str: &'static str = Box::leak(path_string.into_boxed_str()); let command = Command { behavior: "allow", downloadPath: static_path_str, }; tab.call_method(command)?; // CSVファイルをダウンロード tab.navigate_to("https://members.tokyo-gas.co.jp/api/mieru/chargeAmountCsv.jsp?no=0&target=total") .unwrap(); thread::sleep(time::Duration::from_secs(30)); Ok(()) } 実行すると以下のようなエラーが出ます。 ...

September 10, 2023 · にあえん