PhantomJSで遊ぶ

Javascriptでいじれる、webkitベースのヘッドレスブラウザであるPhantomJSでスクレイピングを行う。

だいぶ前に、ファミマTなクレジットカードのログイン画面がクソ認証を導入したのでログインにマウス操作が必要になってしまった。どうせ見に行く情報も限られているし、この際スクレイピングしてしまおう。

クソな画像認証

 

 

まずはログイン画面の仕組みを探す。いじってみると、正解の記号(ここでは星)のみ掴めるようになっていて、他はただ置いてあるだけ。droppable関係を見てみれば良いのかな?とか思いながらソースコードやインスペクタとにらめっこしていると、「画像認証に必要なJS」とかいういかにもなコメントの下にjsファイルが3つくっついていた。

これを見てみる(HTMLにもJSにも丁寧にコメントが書いてあるので解釈が捗る)と、droppableの設定の下にdropした時の挙動が書いてあり、その辺の動作を真似てしまえば画像認証を突破できるのでは?という気持ちに。…突破も何も、ただソース読めば誰にだってわかる内容である、難読の工夫は見られないし、ご丁寧にコメントで可読性もバシバシ向上。

 

という訳でPhantomJSのお話。

てきとうなjsファイルを作成して、それをphantomjsに食わせると動く。

$ sudo pacman -S phantomjs
$ cat hello.js
console.log('hello phantom');
phantom.exit();
$ phantomjs hello.js
hello phantom

 

 

取り敢えずログイン画面を表示してスクショをとってみるのが以下。

var page = require('webpage').create();
page.open('https://wis.pocketcard.co.jp/netservice/login?type=ft', function(status) {
 console.log("Status: " + status);
 if(status === "success") {
  page.render('screen_shot.png');
 }
 phantom.exit();
});

 

実際にはページ遷移を挟むので参考URLを見ながらそれらしいものを書いたのが以下。今月の請求額をぴょろっと吐き出して終了する。

var page = require('webpage').create();

page.onInitialized = function() 
 page.evaluate(function() {
  document.addEventListener('DOMContentLoaded', function() {
   window.callPhantom('DOMContentLoaded');
  }, false);
 });
};

var funcs = function(funcs) {
 this.funcs = funcs;
 this.init();
};
funcs.prototype = {
 init: function() {
  var self = this;
  page.onCallback = function(data) {
   if(data === 'DOMContentLoaded') self.next();
  }
 },
 next: function() {
  var func = this.funcs.shift();
  if(func !== undefined) {
   func();
  } else {
   page.onCallback = function(){};
  }
 }
};

new funcs([
 function() {
  page.open('https://wis.pocketcard.co.jp/netservice/login?type=ft');
 },
 function() {
  page.evaluate(function() {
   $('#username').val('USERNAME');
   $('#password').val('PASSWORD');
   $('#imgCertf').val('1');
   $('#command').submit();
  });
 },
 function() {
  page.render('out.png');
  var seikyu = page.evaluate(function() {
   return $.trim($('div.table-cell-width501:first div span.bold').text());
  });
  console.log("seikyu:" + seikyu);
  phantom.exit();
 }
]).next();

 

 

 

ページ内でJSな処理を行うには、page.evaluate(function(){処理});みたいな感じでやれば良く、データを取り出すには、その処理からreturnしてあげれば良い。単純だ。

 

スクレイピングと言えば、コニカミノルタの強そうな複合機の消耗品残量を見れるWebページから情報を取り出してチャットツールに放り込むスクリプトを書いた事があるけど、その時はScrapyを使った。Python何もわからんところからスタートしてやっていくのとてもとてもしんどかったし、Scrapyなんだかんだ複雑だった…(しかもそのWebページはJavascriptで情報更新だったのでただ見に行くだけでは情報は得られず、ScrapinghubのSplashを使って情報を取得したのだった)

Javascriptは遊んだ経験が多少あったので、違和感なく簡単にコードが書けてよかった。

ただ、ES6の構文(アロー関数とか)を何も言わずに使うと処理も進まず思考停止みたいな感じになってしまってつらいことがあった。しょうがないのでES6の構文は使わずに上記コードを書いていたけど、github(ariya/phantomjs)では

Hi. Answering to your questions:

  1. When we solve all issues listed here #14458
  2. Yes. 2.5 will have full support for ES2015.

とある。pacmanで降ってきたのは2.1.1だったし、gihubの最新Releaseも2.1.3ということで、まだのようだ。

それからエラーを何も吐いてくれない感じだったので(ここでは閉じカッコの対応を間違えるというただのSyntax errorだったけど)参考URLの通り、nodeに読み込ませるとスッと教えてくれた。なんなんだろ。

 

 

参考URL:

PhantomJS でログインが必要なページでも自由自在にスクレイピング – 凹みTips

CasperJS/PhantomJSでシンタックスエラー行を取得する方法 – yohgaki’s blog

 

 

蛇足:

研究室では今までMajestouch NINJA(茶軸)を使っていたんだけど、追加で自分用にリアフォ(104UB-S)を買った。本当は少しお値段ケチって静音モデルでない方(104UB)が欲しかったんだけど、どこにも在庫が無くてネット販売でも取り寄せだったので、諦めて静音の方。

打ち心地は勿論最高に良いです。ずっと触っていたくなる…という訳でこのキーボードは研究が落ち着くまで研究室に置きっぱなしにしておく()

んで、このリアフォで書く最初の(趣味の)プログラムがこれ、というわけ。最近ずっと研究関係のものしか書いてなかったので時間があいてしまった。これからよろしくね♡