Money Forward Me のcsvダウンロードをしたかった
けど有料オプションしかない.
そして1年しか見れないから,過去ログを保存できない.
CSVで月ごとにダウンロードして,まとめたいと思ったので,自作することにした.
実装
早速実装に向けて取り掛かろうと思う
必要なもの
- モダンブラウザ
- Chrome (推奨)
- Firefox (推奨)
- Microsoft Edge (推奨)
- Safari (推奨)
- Opera Next
- Dolphin Browser
- Tampermonkey (拡張機能)
ここではTampermonkeyの説明はしません.
自作の拡張機能を簡単に使用する為の拡張機能です.
データのcsv化およびダウンロード
// セルの中の改行を消す為の正規表現
const removeN = /\n.*/g;
const removeComma = /,/g;
const regex = 'icon-check icon-large';
// 文字コードをBOM付きUTF-8に指定
var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
// データが入っている要素を取得
var table = document.getElementById('cf-detail-table');
const yearSlash = document.getElementsByTagName("h2")[1].innerText.slice(0,5); // <='2022/'
// ここに文字データとして値を格納していく
var data_csv="計算対象, 日付, 内容, 金額, 保有金融機関, 大項目, 中項目, メモ, 振替\n";
for(var i = 1; i < table.rows.length; i++){ // 行単位
// 振替チェック
var isTransfar
if(table.rows[i].cells[3].innerText.indexOf('\n') != -1) isTransfar = true;
else isTransfar = false;
for(var j = 0; j < table.rows[i].cells.length - 1; j++){ // 列単位
// data_cell の用意
var data_cell = table.rows[i].cells[j];
// data_cell の値を整形して data_csv に格納
if(j == 0) {
if((data_cell.innerHTML + '').indexOf(regex) != -1) data_csv += 'TRUE'; // data_cell のテキストがregexを含んでいたら(チェックボックスを入れるクラスがあれば)
else data_csv += 'FALSE';
} else if(j == 1) {
// 年表記を入れ,曜日表記を消す
var dataTemp = (yearSlash + data_cell.innerText.replace(removeN, "").replace(removeComma, ""));
data_csv += dataTemp.slice(0,10);
} else if(j == 8 && isTransfar) {
// 8列目かつ振替がON
data_csv += 'TRUE';
} else {
data_csv += data_cell.innerText.replace(removeN, "").replace(removeComma, "");
}
// 毎行の終わりに改行コードを追加
if(j == table.rows[i].cells.length - 2) data_csv += "\n";
// セル値の区切り文字として,を追加
else data_csv += ",";
}
}
// ダウンロードボタンの作成
// ダウンロードするファイル名の定義
var fileName = document.getElementsByTagName('h2')[1].innerText.slice(2,7).replace('/', '_');
fileName += '.csv'; // ex) 22_10.csv <=2022年10月分
// 挿入位置,ボタンなどの定義
var parentDiv = document.getElementsByClassName('pull-right mf-mb-medium')[0];
if(document.getElementById('download') != null) {
parentDiv.removeChild(download);
}
var childA = document.getElementsByClassName('btn cf-new-btn btn-warning')[0];
var blob = new Blob([ bom, data_csv ], { "type" : "text/csv" });
var newElement = document.createElement('a');
var newContent = document.createTextNode('Download');
newElement.appendChild(newContent);
newElement.setAttribute('id', 'download');
newElement.setAttribute('href', '#');
newElement.setAttribute('style', 'padding:5px 20px; width: 100px; height: 32px; line-height: 34px; margin-right: 10px;');
newElement.className = 'btn cf-new-btn btn-warning';
newElement.href = window.URL.createObjectURL(blob);
newElement.download = fileName;
// 作成したコードを挿入する
parentDiv.insertBefore(newElement, childA);
このコードでcsvにしてダウンロードするまでは出来た.
ダウンロードデータの更新
Money Forward Meでは月の移動をしたときにページの再読み込みをしない(部分的再読み込み)為,このままでは月の移動をした際に,欲しいデータがダウンロードできない.
したがって,上記コードを関数化(今回のコード上ではhandleDownload()
)し,適宜呼び出すことにする.
その為にはページ内の変更を監視し,その度に再呼び出しすればよい.
以下のように実装した.
var observer = new MutationObserver(function(){
// DOMの変化が起こった時の処理
const parentDiv = document.getElementsByClassName('pull-right mf-mb-medium')[0];
handleDownload();
if(document.getElementsByTagName('h2')[0].innerText.indexOf('Loading') != -1) {
if(document.getElementById('download') != null) {
parentDiv.removeChild(download);
}
}
}, false);
// 監視対象の要素オブジェクト
const elem = document.getElementsByClassName('date_range transaction-in-out-header')[0];
// 監視時のオプション
const config = {
attributes: true,
childList: true,
characterData: true,
subtree: true,
};
// 要素の変化監視をスタート
observer.observe(elem, config);
これで要素の変化を取得し,適宜handleDownload()
を実行することでデータの再読み込みを行っている.
なお,parentDiv.removeChild(download);
ではdownloadボタンの一次的な削除を行い,データのcsv化が終わるまではボタンを表示しないようにしている.
また,監視時のオプションconst config
において,subtree: true,
にしないと更新の読み込みが上手く行かないので注意が必要である.
最終的なコード
// ==UserScript==
// @name CSV Download
// @namespace https://github.com/Yuto-34
// @license Yuto-34
// @version 1.0.2
// @description Export csv file of displayed month.
// @author Yuto
// @match https://moneyforward.com/cf
// @icon https://www.google.com/s2/favicons?sz=64&domain=moneyforward.com
// @grant none
// ==/UserScript==
function handleDownload() {
// セルの中の改行を消す為の正規表現
const removeN = /\n.*/g;
const removeComma = /,/g;
const regex = 'icon-check icon-large';
// 文字コードをBOM付きUTF-8に指定
var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
// データが入っている要素を取得
var table = document.getElementById('cf-detail-table');
const yearSlash = document.getElementsByTagName("h2")[1].innerText.slice(0,5);
// ここに文字データとして値を格納していく
var data_csv="計算対象, 日付, 内容, 金額, 保有金融機関, 大項目, 中項目, メモ, 振替\n";
for(var i = 1; i < table.rows.length; i++){
var isTransfar
// 振替チェック
if(table.rows[i].cells[3].innerText.indexOf('\n') != -1) {
isTransfar = true;
} else {
isTransfar = false;
}
for(var j = 0; j < table.rows[i].cells.length - 1; j++){
// data_cell の用意
var data_cell = table.rows[i].cells[j];
// HTML中の表のセル値をdata_csvに格納
if(j == 0) {
if((data_cell.innerHTML + '').indexOf(regex) != -1) {
data_csv += 'TRUE';
} else {
data_csv += 'FALSE';
}
} else if(j == 1) {
var dataTemp = (yearSlash + data_cell.innerText.replace(removeN, "").replace(removeComma, ""));
data_csv += dataTemp.slice(0,10);
} else if(j == 8 && isTransfar) {
data_csv += 'TRUE';
} else {
data_csv += data_cell.innerText.replace(removeN, "").replace(removeComma, "");
}
// 行終わりに改行コードを追加
if(j == table.rows[i].cells.length - 2) data_csv += "\n";
// セル値の区切り文字として,を追加
else data_csv += ",";
}
}
// ダウンロードボタンの作成
var fileName = document.getElementsByTagName('h2')[1].innerText.slice(2,7).replace('/', '_');
fileName += '.csv';
var parentDiv = document.getElementsByClassName('pull-right mf-mb-medium')[0];
if(document.getElementById('download') != null) {
parentDiv.removeChild(download);
}
var childA = document.getElementsByClassName('btn cf-new-btn btn-warning')[0];
var blob = new Blob([ bom, data_csv ], { "type" : "text/csv" });
var newElement = document.createElement('a');
var newContent = document.createTextNode('Download');
newElement.appendChild(newContent);
newElement.setAttribute('id', 'download');
newElement.setAttribute('href', '#');
newElement.setAttribute('style', 'padding:5px 20px; width: 100px; height: 32px; line-height: 34px; margin-right: 10px;');
newElement.className = 'btn cf-new-btn btn-warning';
newElement.href = window.URL.createObjectURL(blob);
newElement.download = fileName;
parentDiv.insertBefore(newElement, childA);
// delete data_csv;//data_csvオブジェクトはもういらないので消去してメモリを開放
}
(function() {
'use strict';
// Your code here...
handleDownload();
var observer = new MutationObserver(function(){
// DOMの変化が起こった時の処理
const parentDiv = document.getElementsByClassName('pull-right mf-mb-medium')[0];
handleDownload();
if(document.getElementsByTagName('h2')[0].innerText.indexOf('Loading') != -1) {
if(document.getElementById('download') != null) {
parentDiv.removeChild(download);
}
}
}, false);
// 監視対象の要素オブジェクト
const elem = document.getElementsByClassName('date_range transaction-in-out-header')[0];
// 監視時のオプション
const config = {
attributes: true,
childList: true,
characterData: true,
subtree: true,
};
// 要素の変化監視をスタート
observer.observe(elem, config);
})();
公開済みURL
現在Greasy Forkにて公開中(誰でもインストールできる状態)にしております.
必要に応じてご使用ください.
月1程度でメンテナンスする予定です.
おまけ
batファイルで出力したcsvファイルを結合した
@echo off
powershell -c "$csvfiles=dir -file '*.csv';$csvfiles|%%{ipcsv $_ -encoding utf8}|epcsv 'comb.csv' -NoTypeInformation -encoding utf8"