-
-
Save arcatdmz/8500521 to your computer and use it in GitHub Desktop.
Amazonの注文履歴をCSV形式にして出力するスクリプト
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Amazonの注文履歴をCSV形式にして出力するスクリプト | |
// | |
// 以下のスクリプトを参考に作成されました。 | |
// http://moroya.hatenablog.jp/entry/2013/06/03/225935 | |
// | |
// 使い方は以下のURLに書いてあります。 | |
// https://junkato.jp/ja/blog/2014/11/13/amazon-payment-history-as-csv/ | |
// | |
// CSVに成型しているのは14行目から定義されているformatEntryという関数なので、これを書き換えれば自由な書式で出力できます。 | |
(function(){ | |
// 各注文履歴をCSVフォーマットにして返す | |
let datePattern = new RegExp("(\\d{4})年(\\d{1,2})月(\\d{1,2})日"); | |
function formatEntry(entry) { | |
const match = entry.date.match(datePattern); | |
let year = match[1]; | |
let month = match[2]; if (month.length <= 1) month = "0" + month; | |
let day = match[3]; if (day.length <= 1) day = "0" + day; | |
let item = entry.item.split("\"").join("\"\""); | |
//for private use: | |
return year + month + day + ",,," + entry.price + ",\"Amazon: " + item + "\"\n"; | |
//for public use: | |
//return year + "-" + month + "-" + day + "," + entry.price + ",\"" + item + "\"\n"; | |
} | |
// 一つの注文に含まれる複数の物品名をつなぐ文字列 | |
let itemDelimiter = " / "; | |
let total = {}; | |
let year = '2014'; | |
let all = false; | |
function init(num) { | |
if(typeof num !== 'number') { | |
num = 0; | |
jQuery('<div/>').css({ | |
position: 'fixed', | |
left: 0, | |
top: 0, | |
width: '100%', | |
height: '100%', | |
zIndex: 1000, | |
backgroundColor: 'rgba(0,0,0,.7)', | |
color: '#fff', | |
fontSize: 30, | |
textAlign: 'center', | |
paddingTop: '15em' | |
}).attr('id', '___overlay').text('Amazonいくら使った?').appendTo('body'); | |
year = window.prompt('何年分の注文を集計しますか?\n - 半角数字4桁で入力してください\n - 全期間を集計する場合は「all」と入力します', year); | |
if(year === 'all') { | |
all = true; | |
year = jQuery('div.top-controls select option:last').val().match(/[0-9]/g).join(''); | |
} else if(!/^[0-9]{4}$/.test(year)) { | |
alert('正しい数値を入力してください'); | |
jQuery('#___overlay').remove(); | |
return false; | |
} | |
year = Number(year); | |
} | |
let progress = load(num); | |
jQuery('#___overlay').text(year+'年の集計中… / '+(num+1)+'ページ目'); | |
progress.then(function(results){ | |
if (typeof total[year] === 'undefined') { | |
total[year] = results; | |
} else { | |
total[year] = total[year].concat(results); | |
} | |
init(num+1); | |
}).catch(function(){ | |
if(all && new Date().getFullYear() > year) { | |
year++; | |
init(0); | |
} else { | |
let txt = 'あなたは\n'; | |
let _contents = ""; | |
let _total = 0; | |
jQuery.each(total, function(year, results){ | |
let yen = 0; | |
jQuery.each(results, function(){ | |
yen += this.price; | |
_contents += formatEntry(this); | |
}); | |
txt += year + '年 合計' + addFigure(yen) + '円分\n'; | |
_total += yen; | |
}); | |
if(all) txt += '総計' + addFigure(_total) + '円分\n'; | |
popup(_contents).alert(txt + 'の買い物をAmazonでしました!'); | |
let saveAsCsv = function() { | |
let blob = new Blob([_contents], {type: "text/plain; charset=utf-8"}); | |
saveAs(blob, `amazon-${year}.csv`); | |
}; | |
if(typeof saveAs !== 'function') { | |
let d=document; | |
let s=d.createElement('script'); | |
s.src='//cdn.rawgit.com/eligrey/FileSaver.js/b4a918669accb81f184c610d741a4a8e1306aa27/FileSaver.js'; | |
s.onload=saveAsCsv; | |
d.body.appendChild(s); | |
} else { | |
saveAsCsv(); | |
} | |
jQuery('#___overlay').remove(); | |
} | |
}); | |
} | |
function load(num) { | |
let df = jQuery.Deferred(); | |
let page = get(num); | |
page.then(function(data){ | |
let dom = jQuery.parseHTML(data); | |
let results = []; | |
jQuery(dom).find('div.order').each(function(){ | |
let box = jQuery(this); | |
let dateText = jQuery(box.find('div.order-info span.value')[0]).text().trim(); | |
let items = []; | |
box.find('div.a-row>a.a-link-normal').each(function(){ | |
items.push(jQuery(this).text().trim()); | |
}); | |
let item = items.join(itemDelimiter); | |
let priceText = jQuery(box.find('div.order-info span.value')[1]).text(); | |
let price = Number(priceText.match(/[0-9]/g).join('')); | |
// FIXME: need to follow href | |
// document.querySelector("div.order div.order-info a.a-link-normal") | |
// .getAttribute("href") | |
// and get the actual prices with | |
// document.querySelector("#od-subtotals div.a-row:last-child div.a-span-last span") | |
// .textContent.trim() | |
// which would look like "¥ 1,499" | |
console.log(item, price); | |
results.push({'date':dateText,'item':item,'price':price}); | |
}); | |
if(results.length <= 0) df.reject(); | |
else df.resolve(results); | |
}); | |
return df.promise(); | |
} | |
function get(num) { | |
let df = jQuery.Deferred(); | |
let generator=window.open('https://www.amazon.co.jp/gp/css/order-history?digitalOrders=1&unifiedOrders=1&orderFilter=year-'+year+'&startIndex='+num*10,'amazon-input','height=250,width=700'); | |
setTimeout(() => { | |
df.resolve(generator.document.body.innerHTML); | |
generator.close(); | |
}, 5000); | |
// jQuery.ajax({ | |
// url: 'https://www.amazon.co.jp/gp/css/order-history?digitalOrders=1&unifiedOrders=1&orderFilter=year-'+year+'&startIndex='+num*10, | |
// beforeSend: function (xhr){ | |
// xhr.setRequestHeader('X-Requested-With', {toString: function(){ return ''; }}); | |
// }, | |
// }) | |
// .then(function(data){ | |
// df.resolve(data); | |
// }) | |
// .catch(function(e){ | |
// console.error("faild to fetch page",e); | |
// }); | |
return df.promise(); | |
} | |
function addFigure(str) { | |
let num = new String(str).replace(/,/g, ""); | |
while(num != (num = num.replace(/^(-?\d+)(\d{3})/, "$1,$2"))); | |
return num; | |
} | |
function popup(content) { | |
let generator=window.open('','amazon-output','height=250,width=700'); | |
generator.document.body.innerHTML = `<html><head><title>Amazon to CSV</title></head><body><pre>${escapeHtml(content)}</pre></body></html>`; | |
return generator; | |
} | |
let entityMap = { | |
"&": "&", | |
"<": "<", | |
">": ">", | |
'"': '"', | |
"'": ''', | |
"/": '/' | |
}; | |
let entityPattern = new RegExp("[&<>\"'\/]", "g"); | |
function escapeHtml(string) { | |
return String(string).replace(entityPattern, function (s) { | |
return entityMap[s]; | |
}); | |
} | |
if(typeof window["jQuery"] !== 'function') { | |
let d=document; | |
let s=d.createElement('script'); | |
s.src='//code.jquery.com/jquery-3.6.0.min.js'; | |
s.onload=init; | |
d.body.appendChild(s); | |
} else { | |
init(); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment