amazon_record2015.js amazon_record.ts
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, '');
127item.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})();