indexデータの取得

Option Explicit

Private Sub testParse()
    Dim url As String
    url = "http://www.morningstar.co.jp/FundData/common/chart/xml/all/MSCI011010.xml"
    
    Application.StatusBar = "Loading: " & url
    Dim xdoc As New DOMDocument60
    xdoc.async = False
    xdoc.Load url
    Application.StatusBar = ""
    
    If (xdoc.parseError.ErrorCode <> 0) Then 'ロード失敗
        Debug.Print xdoc.parseError.reason   'エラー内容を出力
        Exit Sub
    End If
    
    Dim code As IXMLDOMNode
    Set code = xdoc.SelectSingleNode("//morningstarXML/fund/@code")
    Debug.Print code.Text
    
    Dim name As IXMLDOMNode
    Set name = xdoc.SelectSingleNode("//morningstarXML/fund/@name")
    Debug.Print name.Text
    
    Dim period_end As IXMLDOMNode
    Set period_end = xdoc.SelectSingleNode("//morningstarXML/fund/@period_end")
    Debug.Print period_end.Text
    
    '//morningstarXML/fund/@name //morningstarXML/fund/@period_end
    
    Dim days As IXMLDOMNodeList
    Set days = xdoc.SelectNodes("//day")
    Debug.Print days.Length
        
    Dim day As IXMLDOMNode
    For Each day In days
        Dim attr As IXMLDOMNamedNodeMap
        Set attr = day.Attributes
        
        Dim year As String
        year = attr.getNamedItem("year").Text
        
        Dim month As String
        month = attr.getNamedItem("month").Text
        
        Dim value As String
        value = attr.getNamedItem("value").Text
    
        Dim price As String
        price = attr.getNamedItem("price").Text
    
        Dim volume As String
        volume = attr.getNamedItem("volume").Text
    
        Dim return_value As String
        return_value = attr.getNamedItem("return_value").Text
    
        Dim indication As String
        indication = attr.getNamedItem("indication").Text
    
        Dim work_end As String
        work_end = ""
        Dim node As IXMLDOMNode
        Set node = attr.getNamedItem("work_end")
        If Not node Is Nothing Then
            work_end = node.Text
        End If
    Next
End Sub

Private Sub testLoadIndex()
    Dim bk As Workbook
    Dim sht As Worksheet
    
    Set bk = ThisWorkbook
    
    LoadIndex "NYGL00100", bk.Sheets("Sheet48") 'bk.Sheets.Add
End Sub

Public Sub LoadAllIndex()
    Dim shts As Sheets
    Set shts = ThisWorkbook.Sheets
    
    Dim sht As Worksheet
    Set sht = shts("index_list")
    
    Dim area As Range
    Set area = sht.Range("B:B").SpecialCells(xlCellTypeConstants)
    
    Dim cur As Range
    For Each cur In area
        Debug.Print cur.value
        LoadIndex cur.value, shts.Add(after:=shts(shts.Count))
    Next
    
End Sub


Sub LoadIndex(indexCode As String, sht As Worksheet)
    Dim url As String
    url = "http://www.morningstar.co.jp/FundData/common/chart/xml/all/" & indexCode & ".xml"
    
    Application.StatusBar = "Loading: " & url
    Dim xdoc As New DOMDocument60
    xdoc.async = False
    xdoc.Load url
    Debug.Print url
    Application.StatusBar = ""
    
    If (xdoc.parseError.ErrorCode <> 0) Then 'ロード失敗
        MsgBox xdoc.parseError.reason   'エラー内容を出力
        Exit Sub
    End If
    
    Dim code As IXMLDOMNode
    Set code = xdoc.SelectSingleNode("//morningstarXML/fund/@code")
    Debug.Print code.Text
    
    Dim name As IXMLDOMNode
    Set name = xdoc.SelectSingleNode("//morningstarXML/fund/@name")
    Debug.Print name.Text
    Dim indexName As String
    indexName = name.Text
    
    Dim period_end As IXMLDOMNode
    Set period_end = xdoc.SelectSingleNode("//morningstarXML/fund/@period_end")
    Debug.Print period_end.Text
    
    Dim days As IXMLDOMNodeList
    Set days = xdoc.SelectNodes("//day")
    Debug.Print days.Length
    
        
    Application.ScreenUpdating = False
    Dim Calculation As XlCalculation
    Calculation = Application.Calculation
    Application.Calculation = xlCalculationManual
    
    
    sht.UsedRange.Delete xlToLeft
    
    
    Dim cur As Range
    
    Set cur = sht.Range("B2")
    
    cur.Offset(, 0).value = "Code"
    With cur.Offset(, 1).Resize(, 5)
        .HorizontalAlignment = xlLeft
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .MergeCells = True
    End With
    cur.Offset(, 1).value = code.Text
    
    Set cur = cur.Offset(1)
    cur.Offset(, 0).value = "Name"
    With cur.Offset(, 1).Resize(, 5)
        .HorizontalAlignment = xlLeft
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .MergeCells = True
    End With
    cur.Offset(, 1).value = name.Text
    
    Set cur = cur.Offset(1)
    cur.Offset(, 0).value = "Period End"
    With cur.Offset(, 1).Resize(, 5)
        .HorizontalAlignment = xlLeft
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .MergeCells = True
    End With
    cur.Offset(, 1).value = period_end.Text
    
    Set cur = cur.Offset(2)
    cur.Offset(, 0).value = "Date"
    cur.Offset(, 1).value = "Price"
    cur.Offset(, 2).value = "Volume"
    cur.Offset(, 3).value = "Return Value"
    cur.Offset(, 4).value = "Indication"
    cur.Offset(, 5).value = "Work End"
    
    Dim header As Range
    Set header = sht.Range(cur, cur.Offset(, 255).End(xlToLeft))
    
    
    Dim i As Long
    Dim l As Long
    l = days.Length - 1
    For i = 0 To l
        Application.StatusBar = indexName & "; " & i & " / " & l
        
        Dim day As IXMLDOMNode
        Set day = days(i)
        
        Dim attr As IXMLDOMNamedNodeMap
        Set attr = day.Attributes
        
        Dim year As String
        year = attr.getNamedItem("year").Text
        
        Dim month As String
        month = attr.getNamedItem("month").Text
        
        Dim value As String
        value = attr.getNamedItem("value").Text
    
    
        Dim price As String
        price = attr.getNamedItem("price").Text
    
        Dim volume As String
        volume = attr.getNamedItem("volume").Text
    
        Dim return_value As String
        return_value = attr.getNamedItem("return_value").Text
    
        Dim indication As String
        indication = attr.getNamedItem("indication").Text
    
        Dim work_end As String
        work_end = ""
        Dim node As IXMLDOMNode
        Set node = attr.getNamedItem("work_end")
        If Not node Is Nothing Then
            work_end = node.Text
        End If
        
        Set cur = cur.Offset(1)
        cur.Offset(, 0).value = year & "/" & month & "/" & value
        cur.Offset(, 1).value = price
        cur.Offset(, 2).value = volume
        cur.Offset(, 3).value = return_value
        cur.Offset(, 4).value = indication
        cur.Offset(, 5).value = work_end
    Next
    
    header.Font.Bold = True
    With header
        .HorizontalAlignment = xlCenter
        .VerticalAlignment = xlCenter
        .WrapText = False
        .Orientation = 0
        .AddIndent = False
        .IndentLevel = 0
        .ShrinkToFit = False
        .ReadingOrder = xlContext
        .MergeCells = False
    End With
    With header.Interior
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 16777164
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
    
    header.AutoFilter
    header.EntireColumn.AutoFit
    
    Application.StatusBar = False
    Application.ScreenUpdating = True
    Application.Calculation = Calculation

    Dim s As Worksheet
    On Error Resume Next
    Set s = sht.Parent.Sheets(Left(indexName, 31))
    On Error GoTo 0
    If s Is Nothing Then
        sht.name = Left(indexName, 31)
    Else
        If vbOK = MsgBox("シートが重複しています。削除しますか?", vbOKCancel) Then
            s.Delete
            sht.name = Left(indexName, 31)
        End If
    End If
    
End Sub

Private Sub showSheetNames()
    Dim sht As Worksheet
    For Each sht In ThisWorkbook.Sheets
        Debug.Print sht.name
    Next
    
End Sub

indexCheckの一覧を取得

var downloadAsTextFile = function(fileName, content) {
    var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
    var blob = new Blob([bom, content]);
    var url = window.URL || window.webkitURL;
    var blobURL = url.createObjectURL(blob);

    var a = document.createElement('a');
    a.download = fileName;
    a.href = blobURL;
    a.click();  
};
var downloadAsCSVFile = function(fileName, rows) {
  var content = "";
  for( var i in rows) {
    var ary = rows[i];
    for (var j = 0, m = ary.length; j < m; ++j) {
      content += '"' + ("" + ary[j]).replace('"', '""') + '"';
      if (j !== m) {
        content += ',';
      }
    }
    content += '\n';
  }
  downloadAsTextFile(fileName, content);
};


downloadAsCSVFile("index_list.csv", $("input[name='indexCheck']").toArray().map((e)=>[e.parentElement.innerText, e.value]));
http://www.morningstar.co.jp/FundData/common/chart/xml/all/<indexCheck>.xml

で,あたりが取り放題?

JB

使っていないiPhone5があったので,なんとなくJBしてみたが,なかなかすんなりいかないね。。。

http://tools4hack.santalab.me/howto-ios10-ios1033-jailbreak-h3lix-32bit-device.html

はじめパスワード思い出すまでハマって,やっと入力できたと思ったら,

Sign in with the app-specific password you generated. If you forgot the app-specific password or need to create a new one, go to appleid.apple.com

なんか出た。app-specificのことを知らず,下記のサイトにたどり着く。

https://i-bitzedge.com/jailbreak-news/cydia-impactor-error-list#Sign_in_with_the_app-specific_password8230

plist.hpp:201
not PLIST_STRING
<dict>
	<key>CFBundleIdentifier</key>
	<string>org.tihmstar.h3lix</string>
</dict>

とエラー。またエラーかと思っていたら,調べてみるとPhoneをチェックせいと。

https://twitter.com/wgarlin3/status/945456552437264384

なんか入っている。開こうとしたら信頼されていないとか言われ,
どこに設定があるねんと思っていたら,下記にあると。

https://qiita.com/ktysne/items/8e3afc029754dd50f758


無事できたみたい。

積立設定取得

var Queue = function() {
  this.promise = Promise.resolve(true);
  this.addAsync = (action) => this.promise = this.promise.then(() => new Promise((resolve) => action(resolve)));
  this.addSync  = (action) => this.addAsync((resolve) => { action(); resolve(); });
  this.delay    = (delay)  => this.addAsync((resolve) => setTimeout(resolve, delay));
};
var downloadAsTextFile = function(fileName, content) {
    var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
    var blob = new Blob([bom, content]);
    var url = window.URL || window.webkitURL;
    var blobURL = url.createObjectURL(blob);

    var a = document.createElement('a');
    a.download = fileName;
    a.href = blobURL;
    a.click();  
};
var downloadAsCSVFile = function(fileName, rows) {
  var content = "";
  for( var i in rows) {
    var ary = rows[i];
    for (var j = 0, m = ary.length; j < m; ++j) {
      content += '"' + ("" + ary[j]).replace('"', '""') + '"';
      if (j !== m) {
        content += ',';
      }
    }
    content += '\n';
  }
  downloadAsTextFile(fileName, content);
};

var q = new Queue();
var rows = [["ID", "ファンド名", "コース", "預り区分", "積立金額", "申込設定日", "信託報酬", "信託財産留保額"]];
$("tr.md-l-tr-03 td.vaM > a").filter((i, e) => e.href.match(/param6=(\w+)/i)).each((i,e) => {
  var td = $("td", $(e).parent().parent());
  var name = $("a", td[0]).text();
  var course = $(td[1]).text().trim();
  var kind = $(td[2]).text().trim();
  var amount = $(td[3]).html().replace(/\s/g, "").split("<br>");
  
  q.addAsync((resolve) => {
    //console.log("open", name);
    var w = window.open(e.href, "dummy", 'width=100,height=100');
    $(w).load(function() {
      //console.log("loaded", name);
      var t = $(w.document).find("#CONTENTSAREA01");
      rows.push([
        w.location.href.match(/param6=(\w+)/i)[1],
        name,
        course,
        kind,
        amount[0].match(/[\d,]+/)[0],
        amount[1].match(/\((.*)\)/)[1],
        t.find("p:contains('信託報酬')").closest("tr").next("tr").text().trim().match(/([\d\.]+)[%%]/)[1]+"%",
        t.find("p:contains('信託財産留保額')").closest("tr").next("tr").text().trim(),
      ]);
      //console.log("close", name);
      w.close();
      resolve();
    })
  });
})
q.addSync(() => downloadAsCSVFile("hoge.csv", rows));

カテゴリIDのダウンロード

どこに定義があるかと思ったら,変数に読み込んで持っているとは

  var downloadAsTextFile = function(fileName, content) {
      var a = document.createElement('a');
      a.download = fileName;
      a.href = (window.URL || window.webkitURL).createObjectURL(new Blob([new Uint8Array([0xEF, 0xBB, 0xBF]), content]));
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
  };

  var downloadAsCSVFile = function(fileName, rows) {
    var content = "";
    for(var i in rows) {
      for (var j = 0, m = rows[i].length; j < m; ++j) content += '"' + ("" + rows[i][j]).replace(/"/g, '""') + '"' + (j !== m ? ',' : '');
      content += '\n';
    }
    downloadAsTextFile(fileName, content);
  };

  
  downloadAsCSVFile("minus_large_category_list.csv",  category_list_minus.find(".l_c_name").toArray().map((e) => [e.id, e.innerHTML]));
  downloadAsCSVFile("minus_middle_category_list.csv", category_list_minus.find(".m_c_name").toArray().map((e) => [e.parentNode.parentNode.id, e.id, e.innerHTML]));

  downloadAsCSVFile("plus_large_category_list.csv",  category_list_plus.find(".l_c_name").toArray().map((e) => [e.id, e.innerHTML]));
  downloadAsCSVFile("plus_middle_category_list.csv", category_list_plus.find(".m_c_name").toArray().map((e) => [e.parentNode.parentNode.id, e.id, e.innerHTML]));

家計簿を一括ダウンロード,その2

FireFoxのコンソールで実行すると,ページをパースして,管理IDごとCSVに保存。

MoneyforwardのWeb上でカテゴリを変更するのが面倒だから,
Excelで編集して反映したいけど,CSVのアップロードではできない。
なので,一括変更のScriptを作成中。
とりあえず,管理IDがダウンロードできたから,
次はアップデートの方かな。


var downloadAsTextFile = function(fileName, content) {
    var a = document.createElement('a');
    a.download = fileName;
    a.href = (window.URL || window.webkitURL).createObjectURL(new Blob([new Uint8Array([0xEF, 0xBB, 0xBF]), content]));
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
};

var downloadAsCSVFile = function(fileName, rows) {
  var content = "";
  for(var i in rows) {
    for (var j = 0, m = rows[i].length; j < m; ++j) content += '"' + ("" + rows[i][j]).replace(/"/g, '""') + '"' + (j !== m ? ',' : '');
    content += '\n';
  }
  downloadAsTextFile(fileName, content);
};

var parseListBody = function(text, rows) {
  var dom = eval("$" + text.match(/list_body.*?(\(.*)/)[1]); 
  
  $(dom.filter("tr").get().reverse()).each((i, tr) => {
    /*
    $("td", tr).each((j, td) => {
      rows.push(["tbl", i, j, td.className, $(td).text().trim(), $(td).prop('outerHTML').replace(/[\n\r]/g,"").trim()]);
    });
    */
    
    //var form        = $("form", tr).serializeArray();
    var id            = $("input[name*='\[id\]']", tr).val();
    var lctg_id       = $("input[name*='\[large_category_id\]']", tr).val();
    var mctg_id       = $("input[name*='\[middle_category_id\]']", tr).val();
    var hash          = $("input[name*='\[sub_account_id_hash\]']", tr).val();
    var income        = $("input[name*='\[is_income\]']", tr).val();
    var target        = $("input[name*='\[is_target\]']", tr).val()||"";
    var td_date       = $("td.date", tr); 
    var date_id       = td_date.attr("data-table-sortable-value");
    var date_str      = td_date.text().trim();
    var content       = $("td.content div span", tr).text().trim();
    var amount        = $("td.amount span.offset, td.amount div.noform", tr).text().trim();
    var amount_memo   = $("td.amount div.offset", tr).text().trim();
    var pull_right    = $("td.amount div.pull-right", tr).text().trim();
    var note          = $("td.note, td.sub_account_id_hash span", tr).text().trim();
    var explain       = $("td.calc[title]", tr).attr("title");
    var transfer_from = $($("td.calc div.transfer_account_box", tr).parent().prop('outerHTML')).children().empty().parent().text().trim();
    var transfer_to   = $("td.calc div.transfer_account_box", tr).text().trim();
    var lctg          = $("td.lctg", tr).text().trim();
    var mctg          = $("td.mctg", tr).text().trim();
    var memo          = $("td.memo", tr).text().trim();
    
    rows.push([id, income, target, date_id.substr(0, 10), date_id, date_str, content, amount, amount_memo, pull_right, note, transfer_from, transfer_to, explain, lctg_id, lctg, mctg_id, mctg, memo, hash]);
    //rows.push([]);
  });
};

var Queue = function() {
  this.promise = Promise.resolve(true);
  this.addAsync = (action) => this.promise = this.promise.then(() => new Promise((resolve) => action(resolve)));
  this.addSync  = (action) => this.addAsync((resolve) => { action(); resolve(); });
  this.delay    = (delay)  => this.addAsync((resolve) => setTimeout(resolve, delay));
};

var getList = (q, rows, year, month) => {
  q.addAsync((resolve) => {
    $.ajax({
      dataType: "text",
      type:     "POST", 
      url:      "https://moneyforward.com/cf/fetch",
      data: {
        account_id_hash: "",
        from:            year + "/" + month + "/1",
        service_id:      ""
      },
      headers: {
        "X-CSRF-Token": $("meta[name=csrf-token]").attr("content")
      }
    }).done((text) => { console.log(year + "/" + month + "/1"); parseListBody(text, rows); resolve(); });
  });
};


var q = new Queue();
var rows = [["id", "income", "target", "date", "date_id", "date_str", "content", "amount", "amount_memo", "pull_right", "note", "transfer_from", "transfer_to", "explain", "lctg_id", "lctg", "mctg_id", "mctg", "memo", "hash"]];

for(var y = 2010; y <= 2018; ++y) {
  for(var m = 1; m <= 12; ++m) {
    getList(q, rows, y, m);
  }
}

q.addSync(() => downloadAsCSVFile("hoge.csv", rows));
q.addSync(() => alert("完了"));

  (()=>{
    var Queue = function() {
      this.promise = Promise.resolve(true);
      this.addAsync = (action) => this.promise = this.promise.then(() => new Promise((resolve) => action(resolve)));
      this.addSync  = (action) => this.addAsync((resolve) => { action(); resolve(); });
      this.delay    = (delay)  => this.addAsync((resolve) => setTimeout(resolve, delay));
    };

    var q = new Queue();
    var jqxhr, jqxhr2;
    var sub_win_option = "top=0,left=0,width=500,height=500,scrollbars=1,location=0,menubar=0,toolbar=0,status=1,directories=0,resizable=1";
    var sv_win = window.open(window.location, "show_view_window", sub_win_option);
    $(sv_win.document).ready(() => {
      var w   = $(window);
      var st  = w.scrollTop(), sb = st + w.height();
      
      var sub;
      q.delay(2000);
      q.addSync(() => { sub = $(sv_win.document.body); sub.empty(); });
      
      $('a._work, a.gtm-thumbnail-link').each((i,v) => {
        var a = $(v);
        var t = a.offset().top;
        if(st < t && t < sb)
        {
          var href = a.attr('href');
          var is_manga = $('.page-count, ._1VJYUl1', a ).length > 0;
          var fetch_manga = (resolve) => {
            var url = href.replace(/mode=medium/, "mode=manga");
            console.log("fetch_manga", url);
            if(jqxhr2)
            {
              jqxhr2.abort();
            }
            jqxhr2 = $.ajax({ url: url }).done( (data) => {
              $('section.manga img', data).each((i, e) => {
                $('<img/>').attr("src", $(e, data).attr('data-src')).css({ "max-width": "-moz-available" }).appendTo(sub);
              });
              resolve();
            }).fail((jqXHR, textStatus, errorThrown) => {
              console.log('fail', jqXHR.status);
              resolve();
            });
          };
          
          var fetch_image = (resolve) => {
            console.log("fetch_image", href);
            if(jqxhr)
            {
              jqxhr.abort();
            }
            
            jqxhr = $.ajax({ url: href }).done( (data) => {
              var img = $("img.original-image", data);
              if(img.length > 0) {
                $('<img/>').attr("src", img.attr('data-src')).css({ "max-width": "-moz-available" }).appendTo(sub);
                resolve();
              } else {
                fetch_manga(resolve);
              }
            });
          };
          
          q.addAsync((resolve) => {
            //console.log(is_manga, href);
            if( is_manga )
            {
              fetch_manga(resolve);
            } else {
              fetch_image(resolve);
            }
          });
        }
      });
    });
  })();

非同期処理をPromise でシーケンシャルに処理

まあ,車輪の再開発。ただ,Promiseとか難しいこと考えずに,再利用できないとね。。。
rejectは使わないだろうから省略。
resolve もなくしたいけど,非同期ができないからね。。。

var Queue = function() {
  this.promise = Promise.resolve(true);
  this.addAsync = (action) => this.promise = this.promise.then(() => new Promise((resolve) => action(resolve)));
  this.addSync  = (action) => this.addAsync((resolve) => { action(); resolve(); });
  this.delay    = (delay)  => this.addAsync((resolve) => setTimeout(resolve, delay));
};

var list = [
  { id: 1, name: 'taro' },
  { id: 2, name: 'jiro' },
  { id: 3, name: 'saburo' }
];

var q = new Queue();
list.forEach((args) => {
  q.addAsync((resolve) => {
    console.log(args);
    setTimeout(resolve, 1000);
  });
});

list.forEach((args) => {
  q.delay(1000);
  q.addSync(() => console.log(args));
});

参考:
http://kitak.hatenablog.jp/entry/2014/12/01/234759
https://qiita.com/AkihiroTakamura/items/1fdf6dfcb5cb8d5cfc4c