Skip to content

Instantly share code, notes, and snippets.

@paulmist
Forked from benjie/HSBC.js
Created August 15, 2013 20:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulmist/6244473 to your computer and use it in GitHub Desktop.
Save paulmist/6244473 to your computer and use it in GitHub Desktop.
/******************************
HSBC Personal Statement to CSV
v0.5
Copyright: Benjie Gillam (2012)
License: WTFPL v2.0 ( http://en.wikipedia.org/wiki/WTFPL )
Instructions:
Add the following bookmarklet to your browser:
javascript:(function(s)%7Bvar%20e%3Ddocument.createElement('script')%3Be.src%3Ds%3Bdocument.head.appendChild(e)%3B%7D)('https%3A%2F%2Fraw.github.com%2Fgist%2F2157849%2FHSBC.js')
Open this bookmarklet when you are looking at a previous statement or recent
transactions in HSBC personal online banking and it will bring up a CSV copy of
your transactions from that statement/list that you can copy to your clipboard
and paste into a file (e.g. to import into Xero or similar services).
Changelog:
v0.5 - Add AJAX fetching of a full year of statements.
v0.4.1 - Add status updates for AJAX fetches.
v0.4 - Add AJAX fetching of extra information for abbreviated transactions
v0.3 - Fix dates for accounts with Overdraft Limits
v0.2 - Adds support for recent transactions, fixes balance=0
v0.1 - initial release
Compatibility: only tested in Chrome 17 under Mac OS X against my own accounts.
Use at your own risk. Educational purposes only. Etc etc.
******************************/
var textarea = null;
var superarray = [];
var additionalPending = 0;
var statementPending = 0;
var errorOccurred = false;
var ascending = true;
var div = document.createElement('div');
var h1 = document.createElement('h1');
h1.innerText = "Double-click to dismiss";
//div.insertBefore(h1,div.firstChild);
div.appendChild(h1);
div.appendChild(document.createTextNode("Date, Transaction Type, Payee, Amount, Balance (GBP)"));
textarea = document.createElement('textarea');
textarea.style.width="100%";
textarea.style.height = "80%";
textarea.value = "If this message remains, then an error has occurred";
div.appendChild(textarea);
div.style.position="fixed";
div.style.top = "100px";
div.style.bottom = "100px";
div.style.border = "10px solid black";
div.style.borderRadius = "25px";
div.style.padding = "20px";
div.style.left = "100px";
div.style.right = "100px";
div.style.zIndex = "100";
div.style.backgroundColor = "white";
div.addEventListener('dblclick',function(e) {
div.parentNode.removeChild(div);
},false);
document.body.appendChild(div);
function checkFinished() {
if (errorOccurred) {
div.style.backgroundColor = "red";
}
if (additionalPending+statementPending == 0) {
done();
} else {
textarea.value = additionalPending+" additional info fetches remaining; "+statementPending+" statements fetches remaining...";
}
};
function done() {
superarray.reverse();
var results = [];
for (var j = 0; j < superarray.length; j++) {
var array = superarray[j];
if (!ascending) {
array.reverse();
}
for (var i = 0; i<array.length; i++) {
results.push(array[i].join(","));
}
}
csv = results.join("\n");
textarea.value = csv;
textarea.focus();
textarea.select();
}
function parseStatement(statement,$,$rootEl,array) {
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var previous = true;
var tmp = $rootEl.find("div .hsbcTextRight").first()[0].innerText.split(" ");
if (tmp.length > 4) {
tmp = $rootEl.find("div .extPibRow .extTableColumn2").eq(2)[0].innerText.split(" ");
previous = false;
}
var statementDay = parseInt(tmp[0].replace(/^0/,""));
var statementMonth = months.indexOf(tmp[1]);
var statementYear = parseInt(tmp[2]);
if (statementMonth == 0 && ascending) {
statementYear--;
}
var table = $rootEl.find(".containerMain table").eq(1);
var trs = table.find('tbody tr');
var currentMonth = statementMonth;
var currentBalance = 0;
for (var i = 0, l = trs.length; i<l; i++) {
var tr = trs.eq(i);
if (previous && (i == 0 || i == l-1)) {
currentBalance = parseFloat(tr.find('td')[5].innerText);
// Skip "Balance brought/carried forward" lines
continue;
}
(function(){
var row = [];
var tds = tr.find('td');
var amount = 0;
for (var j = 0, m = tds.length; j<m; j++) {
if (j >= 6) {
continue;
}
var td = tds[j];
var skip = false;
str = td.innerText;
if (!previous) {
var tmp = str.split("\n");
for (var k = 0; k < tmp.length; k++) {
tmp[k] = $.trim(tmp[k]);
}
str = tmp.join("\n");
str = str.replace(/\n\n/g,"\n");
}
str = str.replace(/[\v\0\r\t]/g," ");
str = str.replace(/(^ | (?= )| $)/g,"");
str = str.replace(/"/g,"\\\"");
str = $.trim(str);
str = str.replace(/\n/g, " | ");
if (j == 0) {
tmp = str.split(" ");
var day = parseInt(tmp[0].replace(/^0/,""));
var month = months.indexOf(tmp[1]);
if (currentMonth == 11 && month == 0) {
statementYear++;
}
currentMonth = month;
strMonth = ""+(month+1);
if (strMonth.length < 2) {
strMonth = "0"+strMonth;
}
strDay = ""+day;
if (strDay.length < 2) {
strDay = "0"+strDay;
}
str = ""+statementYear+"-"+strMonth+"-"+strDay;
}
if (j < 3) {
str = "\""+str+"\"";
if (j == 2) {
// AJAX fetch more details.
var a = tds.eq(j).find("a");
if (a && a.length) {
var href = a.attr("href");
console.log("Additional: "+statement+" :: "+a[0].innerText);
additionalPending++;
$.ajax({
url:href
}).done(function(data){
var a = data.indexOf("<strong>Additional details:</strong>");
if (a != -1) {
var relevant = data.substr(a,1000);
relevant = relevant.replace(/^[\s\S]*?<p>([\s\S]*?)<\/p>[\s\S]*$/,"$1");
relevant = relevant.replace(/<br \/>/g,"");
relevant = $.trim(relevant);
var tmp = relevant.split("\n");
for (var i = 0; i < tmp.length; i++) {
tmp[i] = $.trim(tmp[i]);
}
relevant = tmp.join("\n");
relevant = relevant.replace(/\n\n/g,"\n");
relevant = relevant.replace(/\n/g," | ");
row[2] = "\""+relevant+"\"";
console.log(relevant);
}
additionalPending--;
checkFinished();
});
}
}
} else {
str = parseFloat(str);
if (isNaN(str)) {
str = 0.0;
}
if (j == 3) {
amount -= str;
skip = true;
} else if (j == 4) {
amount += str;
str = amount;
} else if (j == 5) {
if (previous) {
if (str == 0.0) {
currentBalance = str = parseInt((currentBalance + amount)*100)/100;
} else {
currentBalance = str;
}
}
}
}
if (!skip) {
row.push(str);
}
}
array.push(row);
})();
}
checkFinished();
}
(function($){
var trs = $(".hsbcMainContent table.hsbcRowSeparator tr");
if (!trs || trs.length < 2) {
var array = [];
superarray.push(array);
parseStatement("Current Page", $, $(document.body), array);
} else {
for (var i = 1; i<trs.length; i++) {
(function(){
var a = trs.eq(i).find("td a").eq(0);
var href = a.attr("href");
var array = [];
superarray.push(array);
if (href && href.length) {
// Fetch it
statement = a[0].innerText;
console.log("Statement: "+statement);
statementPending++;
$.ajax({
url:href
}).done(function(data){
data = data.replace(/^[\s\S]*<body[^>]*>([\s\S]*)<\/body[\s\S]*$/,"$1");
var div = document.createElement('div');
div.innerHTML = data;
var $div = $(div);
parseStatement(statement, $, $div, array);
statementPending--;
checkFinished();
});
} else {
console.error("One of the links doesn't work!!");
errorOccurred = true;
}
})();
}
}
})(jQuery);
checkFinished();
@shuckster
Copy link

Hello! Just in case someone finds it useful I wrote a bookmarklet to download HSBC statements in CSV and other formats: http://shuckster.github.io/OBIS/

It can download a whole year of statements at once, and can follow the memo-links inside the individual statements to get the description of each transaction, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment