1 | // Amazonの注文履歴をTSV形式で出力するスクリプト |
1 | // Amazonの注文履歴をTSV形式で出力するスクリプト |
2 | // |
2 | // |
3 | // 2015-01-01 時点での DOM 構造に対応, GoogleCrome, Opera でテスト済。 |
3 | // 2015-01-01 時点での DOM 構造に対応, GoogleCrome, Opera でテスト済。 |
4 | // formatEntry関数を書き換えれば自由な書式で出力できます。 |
4 | // formatEntry関数を書き換えれば自由な書式で出力できます。 |
5 | // |
5 | // |
6 | // 参考: |
6 | // 参考: |
7 | // - Amazonの注文履歴をCSV形式にして出力するスクリプト |
7 | // - Amazonの注文履歴をCSV形式にして出力するスクリプト |
8 | // https://gist.github.com/arcatdmz/8500521 |
8 | // https://gist.github.com/arcatdmz/8500521 |
9 | // - Amazon で使った金額の合計を出す奴 (2014 年バージョン) |
9 | // - Amazon で使った金額の合計を出す奴 (2014 年バージョン) |
10 | // https://gist.github.com/polamjag/866a8af775c44b3c1a6d |
10 | // https://gist.github.com/polamjag/866a8af775c44b3c1a6d |
11 | |
11 | |
12 | (function(){ |
12 | (function () { |
13 | |
13 | |
14 | // 各注文履歴をTSVフォーマットにして返す |
14 | // 各注文履歴をTSVフォーマットにして返す |
15 | var datePattern = new RegExp("(\\d{4})年(\\d{1,2})月(\\d{1,2})日"); |
15 | var datePattern = new RegExp("(\\d{4})年(\\d{1,2})月(\\d{1,2})日"); |
16 | function formatEntry(entry) { |
16 | function formatEntry(entry) { |
17 | console.log(entry); |
17 | console.log(entry); |
18 | entry.date.match(datePattern); |
18 | entry.date.match(datePattern); |
19 | var year = RegExp.$1; |
19 | var year = RegExp.$1; |
20 | var month = RegExp.$2; if (month.length <= 1) month = "0" + month; |
20 | var month = RegExp.$2; if (month.length <= 1) month = "0" + month; |
21 | var day = RegExp.$3; if (day.length <= 1) day = "0" + day; |
21 | var day = RegExp.$3; if (day.length <= 1) day = "0" + day; |
22 | var date = "" + year + "/" + month + "/" + day; |
22 | var date = "" + year + "/" + month + "/" + day; |
23 | var arr = [date, entry.name, entry.author, entry.url]; |
23 | var arr = [date, entry.name, entry.author, entry.url]; |
24 | return arr.join('\t') + "\n"; |
24 | return arr.join('\t') + "\n"; |
25 | } |
25 | } |
26 | |
26 | |
27 | function popup(content) { |
27 | function popup(content) { |
28 | var generator=window.open('','name','height=250,width=700'); |
28 | var generator = window.open('', 'name', 'height=250,width=700'); |
29 | generator.document.write('<html><head><title>Amazon to TSV</title>'); |
29 | generator.document.write('<html><head><title>Amazon to TSV</title>'); |
30 | generator.document.write('</head><body>'); |
30 | generator.document.write('</head><body>'); |
31 | generator.document.write('<pre>'); |
31 | generator.document.write('<pre>'); |
32 | generator.document.write(content); |
32 | generator.document.write(content); |
33 | generator.document.write('</pre>'); |
33 | generator.document.write('</pre>'); |
34 | generator.document.write('</body></html>'); |
34 | generator.document.write('</body></html>'); |
35 | generator.document.close(); |
35 | generator.document.close(); |
36 | return generator; |
36 | return generator; |
37 | } |
37 | } |
38 | |
38 | |
39 | var itemDelimiter = " / "; |
39 | var itemDelimiter = " / "; |
40 | var total = {}; |
40 | var total = {}; |
41 | var year = '2014'; |
41 | var year = '2014'; |
| |
42 | var nyear: number = 2014; |
42 | var all = false; |
43 | var all = false; |
43 | |
44 | |
44 | function init(num) { |
45 | function init(num : number) { |
45 | if(typeof num !== 'number') { |
46 | if (typeof num !== 'number') { |
46 | var num = 0; |
47 | var num = 0; |
47 | $('<div/>').css({ |
48 | $('<div/>').css({ |
48 | position: 'fixed', |
49 | position: 'fixed', |
49 | left: 0, |
50 | left: 0, |
50 | top: 0, |
51 | top: 0, |
51 | width: '100%', |
52 | width: '100%', |
52 | height: '100%', |
53 | height: '100%', |
53 | zIndex: 1000, |
54 | zIndex: 1000, |
54 | backgroundColor: 'rgba(0,0,0,.7)', |
55 | backgroundColor: 'rgba(0,0,0,.7)', |
55 | color: '#fff', |
56 | color: '#fff', |
56 | fontSize: 30, |
57 | fontSize: 30, |
57 | textAlign: 'center', |
58 | textAlign: 'center', |
58 | paddingTop: '15em' |
59 | paddingTop: '15em' |
59 | }).attr('id', '___overlay').text('Amazonいくら使った?').appendTo('body'); |
60 | }).attr('id', '___overlay').text('Amazonいくら使った?').appendTo('body'); |
60 | year = window.prompt('何年分の注文を集計しますか?\n - 半角数字4桁で入力してください\n - 全期間を集計する場合は「all」と入力します', year); |
61 | year = window.prompt('何年分の注文を集計しますか?\n - 半角数字4桁で入力してください\n - 全期間を集計する場合は「all」と入力します', year); |
61 | if(year === 'all') { |
62 | if (year === 'all') { |
62 | all = true; |
63 | all = true; |
63 | year = jQuery('div.top-controls select option:last').val().match(/[0-9]/g).join(''); |
64 | year = jQuery('div.top-controls select option:last').val().match(/[0-9]/g).join(''); |
64 | } else if(!/^[0-9]{4}$/.test(year)) { |
65 | } else if (!/^[0-9]{4}$/.test(year)) { |
65 | alert('正しい数値を入力してください'); |
66 | alert('正しい数値を入力してください'); |
66 | $('#___overlay').remove(); |
67 | $('#___overlay').remove(); |
67 | return false; |
68 | return false; |
68 | } |
69 | } |
69 | year = Number(year); |
70 | nyear = Number(year); |
70 | } |
71 | } |
71 | // 第二引数を true にすると各商品とかエラーを逐一表示する |
72 | // 第二引数を true にすると各商品とかエラーを逐一表示する |
72 | var progress = load(num, false); |
73 | var progress = load(num, false); |
73 | $('#___overlay').text(year+'年の集計中… / '+(num+1)+'ページ目'); |
74 | $('#___overlay').text(nyear + '年の集計中… / ' + (num + 1) + 'ページ目'); |
74 | progress.done(function(results){ |
75 | progress.done(function (results) { |
75 | if (typeof total[year] === 'undefined') { |
76 | if (typeof total[nyear] === 'undefined') { |
76 | total[year] = results; |
77 | total[nyear] = results; |
77 | } else { |
78 | } else { |
78 | total[year] = total[year].concat(results); |
79 | total[nyear] = total[nyear].concat(results); |
79 | } |
80 | } |
80 | init(num+1); |
81 | init(num + 1); |
81 | }).fail(function(){ |
82 | }).fail(function () { |
82 | if(all && new Date().getFullYear() > year) { |
83 | if (all && new Date().getFullYear() > nyear) { |
83 | year++; |
84 | nyear++; |
84 | init(0); |
85 | init(0); |
85 | } else { |
86 | } else { |
86 | var _total = 0; |
87 | var _total = 0; |
87 | var _content = ""; |
88 | var _content = ""; |
88 | jQuery.each(total, function(year, results){ |
89 | jQuery.each(total, function (nyear, results) { |
89 | var yen = 0; |
90 | var yen = 0; |
90 | jQuery.each(results, function(){ |
91 | jQuery.each(results, function () { |
91 | yen += this.price; |
92 | yen += this.price; |
92 | $.each(this.items, function(i, item){ |
93 | $.each(this.items, function (i, item) { |
93 | _content += formatEntry(item); |
94 | _content += formatEntry(item); |
94 | }); |
95 | }); |
95 | }); |
96 | }); |
96 | _total += yen; |
97 | _total += yen; |
97 | }); |
98 | }); |
98 | // result |
99 | // result |
99 | $('#___overlay').remove(); |
100 | $('#___overlay').remove(); |
100 | alert('合計 ' + _total + ' 円'); |
101 | alert('合計 ' + _total + ' 円'); |
101 | popup(_content); |
102 | popup(_content); |
102 | console.log('合計 ' + _total + ' 円'); |
103 | console.log('合計 ' + _total + ' 円'); |
103 | } |
104 | } |
104 | }); |
105 | }); |
105 | } |
106 | } |
106 | |
107 | |
107 | function load(num, verbose) { |
108 | function load(num: number, verbose: boolean) { |
108 | var df = jQuery.Deferred(); |
109 | var df = jQuery.Deferred(); |
109 | var page = get(num, verbose); |
110 | var page = get(num, verbose); |
110 | page.done(function(data){ |
111 | page.done(function (data : string) { |
111 | var dom = jQuery.parseHTML(data); |
112 | var dom = jQuery.parseHTML(data); |
112 | var results = []; |
113 | var results = []; |
113 | |
114 | |
114 | jQuery(dom).find('div.order').each(function(){ |
115 | jQuery(dom).find('div.order').each(function () { |
115 | var box = jQuery(this); |
116 | var box = jQuery(this); |
116 | var dateText = jQuery(box.find('div.order-info span.value')[0]).text().trim(); |
117 | var dateText = jQuery(box.find('div.order-info span.value')[0]).text().trim(); |
117 | |
118 | |
118 | var items = []; |
119 | var items = []; |
| |
120 | var item = {}; |
119 | var pubarr = box.find("div.a-row > span.a-size-small"); |
121 | var pubarr = box.find("div.a-row > span.a-size-small"); |
120 | box.find("div.a-row > a.a-link-normal").each(function(j) { |
122 | box.find("div.a-row > a.a-link-normal").each(function (j) { |
121 | var item = {}; |
123 | item = {}; |
122 | item.name = $(this).text().trim(); |
124 | item['name'] = $(this).text().trim(); |
123 | item.path = $(this).attr('href').trim(); |
125 | item['path'] = $(this).attr('href').trim(); |
124 | item.url = 'https://www.amazon.co.jp' + item.path; |
126 | item['url'] = 'https://www.amazon.co.jp' + item['path']; |
125 | item.date = dateText; |
127 | item['date'] = dateText; |
126 | item.author = $(pubarr[j*2]).text().trim().replace(/(\n)/g, ''); |
128 | item['author'] = $(pubarr[j * 2]).text().trim().replace(/(\n)/g, ''); |
127 | item.price = $(this).parent().parent().parent().parent().parent().parent().parent().parent().parent().parent().parent().find(".order-info").find(".a-span2").find(".a-size-base").find(".value").text().trim(); |
129 | item['price'] = $(this).parent().parent().parent().parent().parent().parent().parent().parent().parent().parent().parent().find(".order-info").find(".a-span2").find(".a-size-base").find(".value").text().trim(); |
128 | items.push(item); |
130 | items.push(item); |
129 | }); |
131 | }); |
130 | |
132 | |
131 | var priceText = jQuery(box.find('div.order-info span.value')[1]).text(); |
133 | var priceText = jQuery(box.find('div.order-info span.value')[1]).text(); |
132 | var price = Number(priceText.match(/[0-9]/g).join('')); |
134 | var price = Number(priceText.match(/[0-9]/g).join('')); |
133 | |
135 | |
134 | if (verbose) console.log(item, price); |
136 | if (verbose) console.log(item, price); |
135 | results.push({'date':dateText,'items':items,'price':price}); |
137 | results.push({ 'date': dateText, 'items': items, 'price': price }); |
136 | }); |
138 | }); |
137 | |
139 | |
138 | if(results.length <= 0) df.reject(); |
140 | if (results.length <= 0) df.reject(); |
139 | else df.resolve(results); |
141 | else df.resolve(results); |
140 | }); |
142 | }); |
141 | return df.promise(); |
143 | return df.promise(); |
142 | } |
144 | } |
143 | |
145 | |
144 | function get(num) { |
146 | function get(num: number, verbose: boolean) { |
145 | var df = jQuery.Deferred(); |
147 | var df = jQuery.Deferred(); |
146 | jQuery.ajax({ |
148 | jQuery.ajax({ |
147 | url: 'https://www.amazon.co.jp/gp/css/order-history?digitalOrders=1&unifiedOrders=1&orderFilter=year-'+year+'&startIndex='+num*10, |
149 | url: 'https://www.amazon.co.jp/gp/css/order-history?digitalOrders=1&unifiedOrders=1&orderFilter=year-' + year + '&startIndex=' + num * 10, |
148 | beforeSend: function (xhr){ |
150 | beforeSend: function (xhr) { |
149 | xhr.setRequestHeader('X-Requested-With', {toString: function(){ return ''; }}); |
151 | //xhr.setRequestHeader('X-Requested-With', { toString: function () { return ''; } }); |
| |
152 | function toString(): string{ return ''; } |
| |
153 | xhr.setRequestHeader('X-Requested-With', 'toString' ); |
| |
154 | //xhr.setRequestHeader('X-Requested-With', '');//NG |
150 | }, |
155 | }, |
151 | }) |
156 | success: function (data) { |
152 | .success(function(data){ |
| |
153 | df.resolve(data); |
157 | df.resolve(data); |
| |
158 | } |
154 | }) |
159 | }) |
155 | .fail(function(jqXHR, msg){ |
160 | .fail(function (jqXHR, msg) { |
156 | if (verbose) console.log("fail", msg); |
161 | if (verbose) console.log("fail", msg); |
157 | }); |
162 | }); |
158 | return df.promise(); |
163 | return df.promise(); |
159 | } |
164 | } |
160 | |
165 | |
161 | if(typeof jQuery !== 'function') { |
166 | if (typeof jQuery !== 'function') { |
162 | var d=document; |
167 | var d = document; |
163 | var s=d.createElement('script'); |
168 | var s = d.createElement('script'); |
164 | s.src='//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'; |
169 | s.src = '//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'; |
165 | s.onload=init; |
170 | s.onload = e => { init(null); } |
166 | d.body.appendChild(s); |
171 | d.body.appendChild(s); |
167 | } else { |
172 | } else { |
168 | init(); |
173 | init(null); |
169 | } |
174 | } |
170 | })(); |
175 | })(); |