Skip to content

Instantly share code, notes, and snippets.

@arcatdmz
Forked from moroya/aitter.js
Last active November 25, 2024 06:19
Show Gist options
  • Save arcatdmz/8500521 to your computer and use it in GitHub Desktop.
Save arcatdmz/8500521 to your computer and use it in GitHub Desktop.
Amazonの注文履歴をCSV形式にして出力するスクリプト
// 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 = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#47;'
};
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