2015年2月19日木曜日

amazon の購入履歴csv 化

casperjs というのを使って作ってみた。
コードは荒いが必要な方がいるかもしれないので、公開します。

casperjs のセットアップ(こちらが良いかも)後、下のamazon_csv.js を保存して、コマンドラインから次のように実行します。

使い方)
casperjs amazon_csv.js メールアドレス パスワード 対象年

例)
casperjs amazon_csv.js hoge@fugapiyo.co.jp password001 2014年


ここからamazon_csv.js
var casper = require('casper').create({
  pageSettings: {
    userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4'
  },
  verbose: true,
  logLevel: 'debug'
});
var email = casper.cli.get(0);
var pass = casper.cli.get(1);
var targetYear = casper.cli.get(2);

var url = 'https://www.amazon.co.jp/gp/css/order-history';
var detailIdList = [], detailIdIndex = 0;
var capId = 1;
var csvDatas = [];

function cap(){
  casper.capture(capId++ + '.png');
}
function dump(o){
  require('utils').dump(o);
}

function setEidToDetail(){
  var linkCandidates = document.querySelectorAll(".order .a-link-normal");
  var map = Array.prototype.map;
  var filter = Array.prototype.filter;
  var id = 0;
  return map.call(filter.call(linkCandidates, function(e){
    return '注文の詳細' == e.innerHTML;
  }), function(e) {
    var newId = 'secret' + id++;
    e.setAttribute('id', newId)
    return newId;
  });
}

casper.start(url);

// ログイン
casper.then(function() {
  this.fill('form#ap_signin_form', {
    email: email, 
    password: pass
  }, true);
});

// 期間を選択するプルダウンをクリック
casper.thenClick('#a-autoid-1 .a-dropdown-prompt')

// 指定の年を選択
casper.then(function(){
  var id = this.evaluate(function(targetYear){
    var liList = document.getElementById('1_dropdown_combobox').getElementsByTagName('li');
    for (var i = 0; i < liList.length; i++){
      var li = liList[i];
      var a = li.childNodes[0];
      if (a.innerHTML.trim() == targetYear){
        return a.id;
      }
    }
  }, targetYear);
  if (! id){
    throw 'no such year ' + targetYear;
  }
  casper.thenClick('#' + id);
});

// 詳細画面へのリンク一覧を取得
function first(){
  debugger;
  detailIdIndex = 0
  loop.call(this);
}

// 詳細画面へ遷移し、csv 情報を取得
function loop(){
  detailIdList = this.evaluate(setEidToDetail);
  if (detailIdIndex < detailIdList.length){
    var eId = detailIdList[detailIdIndex++]
    this.thenClick('#' + eId);
    casper.then(function(){
      var datas = this.evaluate(function (){
        function scrapeDetailNormal(){
          // 注文日
          var date = document.getElementsByClassName('order-date-invoice-item')[0].childNodes[0].textContent.replace(/注文日/, '').trim();
          // 注文番号
          var order = document.getElementsByClassName('order-date-invoice-item')[1].childNodes[0].textContent.replace(/注文番号/, '').trim();
          var items = document.querySelectorAll('.a-fixed-left-grid');
          var result = [];
          for (var i = 0; i < items.length; i++){
            var item = items[i];
            item.getElementsByClassName('a-row').childNode
            // 商品名
            var name = item.getElementsByClassName('a-row')[0].getElementsByTagName('a')[0].innerHTML.trim()
            // 金額
            var price = item.getElementsByClassName('a-color-price')[0].innerHTML.trim()
            // 販売
            var shopEl = item.getElementsByClassName('a-color-secondary')[0]
            var shopAEl = shopEl.getElementsByTagName('a');
            if (0 < shopAEl.length){
              var shop = shopAEl[0].innerHTML;
            }else{
              var shop = shopEl.innerHTML;
            }
            shop = shop.replace(/販売:/, '').trim()
            result.push({
              date:date, order:order, name:name, price:price, shop:shop
            });
          }
          return result;
        }
        if (0 < document.getElementsByTagName('h1').length){
          return scrapeDetailNormal();
        }else{
          // TODO 電子書籍は難しい
          return [];
        }
      });
      csvDatas = csvDatas.concat(datas);
    });
    casper.then(function(){
      casper.back();
    });
    casper.then(function(){
      loop.call(this);
    });
  }else{
    last.call(this);
  }
}

// 次のページがあれば遷移してループを続ける
function last(){
  var nextId = this.evaluate(function(){
    var lastEls = document.getElementsByClassName('a-last')[0].getElementsByTagName('a');
    if (0 < lastEls.length){
      var a = lastEls[0];
      if (0 < a.className.indexOf('disabled')){
        return;
      }
      var id = 'go-to-next-page';
      a.setAttribute('id', id);
      return id;
    }
  });
  this.then(function(){
    debugger;
    if (nextId){
      this.thenClick('#' + nextId, function(){
        first.call(this);
      });
    }else{
      var csv = [];
      for (var i = 0; i < csvDatas.length; i++){
        var data = csvDatas[i];
        if (! data){
          continue;
        }
        var list = [];
        list.push(data.date);
        list.push(data.order);
        list.push(data.shop);
        list.push(data.name);
        list.push(data.price);
        for (var j = 0; j < list.length; j++){
          list[j] = '"' + list[j].replace(/"/g, '""') + '"';
        }
        var line = list.join(',');
        csv.push(line);
      }
      for (var i = 0; i < csv.length; i++){
        this.echo(csv[i]);
      }
    }    
  });
}

// ループ開始
casper.then(function() {
  first.call(this);
});

casper.run();

ここまで
※電子書籍の購入履歴は取得できません
※数量の取得もできません

本日 2014/2/19 時点では動くが、いつかデザインが変わったら動かなくなると思う。
お約束ではありますが、試される方は全て自己責任でお願いします。

0 件のコメント:

コメントを投稿