PhantomJS とは
WebKit が積まれている JavaScript エンジン。
要するにブラウジングをコンソール内 (ヘッドレス) でエミュレートできる。
とりあえずできあがったもの
/)
///)
/,.=゙''"/
/ i f ,.r='"-‐'つ____ こまけぇこたぁいいんだよ!!
/ / _,.-‐'~/⌒ ⌒\
/ ,i ,二ニ⊃( ●). (●)\
/ ノ il゙フ::::::⌒(__人__)⌒::::: \
,イ「ト、 ,!,!| |r┬-| |
/ iトヾヽ_/ィ"\ `ー'´ /
https://github.com/kobake/amazon-shop-tool
※node.jsが必要。
$ git clone git@github.com:kobake/amazon-shop-tool.git $ cd amazon-shop-tool $ npm install $ chmod +x amazon-order-history $ ./amazon-order-history Email: (Input your email for amazon.co.jp) Password: (Input your password for amazon.co.jp)
取得できるもの
こういうのがずらずらと取得できる。
222-2222222-2222222,2010年12月18日,¥2100,注文が確定しました,YOURNAME,暗号解読〈上〉 111-1111111-1111111,2010年10月30日,¥4195,注文が確定しました,YOURNAME,人月の神話
複数商品を一度に注文したときの履歴が1行にまとまってしまうのがアレ。
今のところは困ってないけど後々分割したい。
プログラム解説(シェルスクリプト部分)
サインイン情報受け取り部
PhantomJS でも標準入力受け取りはできるっぽいけど、サンプルが少ないのでちょっとアレ。
シェルスクリプトで読ませるほうが参考にできるサンプル多いのでそっち採用。
積まれている量の多いナレッジを使うのがおそらくジャスティス。
# Email echo -n "Email: " read mail # Password read -sp "Password: " pass tty -s && echo
このへんを参考にしました。
ShellScript - シェルスクリプトでパスワード入力プロンプト - Qiita
PhantomJS 起動
パスの指定とか我流すぎる気がする。フツーの方法が知りたい。
dir=$(cd $(dirname $0);pwd) phantomjs="$dir/node_modules/phantomjs/bin/phantomjs" "$phantomjs" "$dir/js/index.js" "$mail" "$pass"
プログラム解説(js部分)
エンジン準備
webpage を create して、いろいろ下ごしらえをします。
var page = require('webpage').create(); // ↓これをやっておくと、 // ページ内スクリプトからのログが PhantomJS 上の標準出力に表示される。 page.onConsoleMessage = function (msg) { console.log(msg); };
サインイン
最初はサインインしていない状態なので、どのページ開いてもサインインフォームが出る。
で、そのフォーム内では
- #ap_email のところにメールアドレス
- #ap_password のところにパスワード
を入れて、
#signInSubmit-input のボタンをクリックすればサインイン処理となる。
page.evaluate でページ内スクリプト実行をエミュレートできる。
var url = 'https://www.amazon.co.jp/gp/css/order-history'; page.open(url, function () { … page.evaluate(function(mail, pass){ jQuery('#ap_email').val(mail); jQuery('#ap_password').val(pass); jQuery('#signInSubmit-input').click(); }, mail, pass); } );
そのまんまのコード。
ページ内の注文履歴をログ出力
まずは実際のブラウザ内でページ内の履歴を出力するスクリプトを実験しながら仕上げると良い。
で、page.evaluate の中身に、上記で仕上げたスクリプトをそのまま貼り付ければ良い。
page.evaluate(function(){ // この部分のスクリプトは // 実際にブラウザで確認したものを貼り付けると良い jQuery('.action-box').each(function(){ var text = ''; var order = jQuery(this); text = order.find('.order-level .info-data a').text().trim(); // 注文番号 text += "," + order.find('.order-level h2').text().trim(); // 日付 text += "," + order.find('.order-level .price').text().trim().replace(/[ ,]/g, ''); // 価格 text += "," + order.find('.order-level .top-text').text().trim(); // 状態 text += "," + order.find('.order-level .info-data.recipient').text().trim(); // 受取人 order.find('.ship-listing .shipment > li').each(function(){ var item = jQuery(this); text += "," + item.find('.item-title').text().trim(); // タイトル }); console.log(text); }); });
ページめくり
「次へ」という見た目のリンクを探してクリック。
…といきたいところだが、a タグを jQuery で click しても反応無いので、
ここは直接 location.href を書き換える形で。
page.evaluate(function(){ // 次ページ var next = jQuery('div.pagination-box a:contains("次へ")'); if(next.size() == 1){ location.href = next.attr('href'); } });
その他:逐次処理について
おそらく jQuery.Deferred あたりを使うのがスマートなのかもしれない。
が、個人的にはいろいろいじってみた感触としては、
あたりが学習コスト対効果が良かった。
というかもうちょっと直接的に言うと、汎用的な用途を狙うフレームワークの学習コストがそこそこある割に、それほど効果を感じなかったので、なんか自前でゴリゴリやるほうがマシという感じでした。
しかしチーム開発が必要なときには何かしらフレームワークを選定する必要があるであるのであろう。。。ふーむ。
おしまい
いろんな都合で JavaScript 触らざるを得ないことが最近多いけど、こいつとはいまだに仲良くなりきれていませぬ。