TAMINIF’s blog

京都在住のWebエンジニア。iOSアプリもやってます。勉強会にも参加してます。

PhantomJSのクローリングでwindow.openに対応する

WEBサイトのクローリングを行う一つに、PhantomJSがあります。
通常のブラウザとは違ってタブやウィンドウというものはなく、一つの画面を遷移して使用するのですが、リンク押下時にJSのwindow.openで遷移するタイプのものですと、そのまま次に進むことが出来ません。
調べてみるとstackoverflowで回答してくれている方がおられましたので、使用した方法を紹介します。


PhantomJSのOnPageCreated

PhantomJSのversion1.7以降では、上記の関数が標準で用意されています。リファレンスに記載されている通り、functionを格納することでwindow.open時にcallbackとして格納した関数が呼ばれます。

ただ、こちらの方法ですとwindow.open時に毎回呼ばれてしまいますので、個別の処理が書きにくいことや、page変数を使用してたどっていくことが出来ません。
stackoverflowでは他に二つの方法が記載されていましたので、そのうちの一つを採用しました。

pagesプロパティ

PhantomJSのcreate()で生成したオブジェクト(page)には、"pages"というプロパティがあり、そこに子のpageオブジェクトが格納されます。window.openで開いたページのロードが完了したタイミングで、pageオブジェクトを入れ替えてやれば、以降の処理も通常の遷移と同じ記述で進めることが出来ます。

ソース解説

自分が記述したソースを使用して説明します。


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

// クロールしたいページのURL
var pageUrl = 'http://www.***********.**.**';

page.open(pageUrl, function (status) {
  if (status == 'success') {
    windowAction(
      // タイムアウト時の処理を第一引数に渡す
      function test(){
        return page.pages && page.pages[0];
      },
      // 成功時の処理を第二引数に渡す
      function ready(){
        page.pages[0].onLoadFinished = function(){
          // ロード完了後次の処理へ
          loginAction(page.pages[0]);
        };
      }
    );
    page.evaluate(function(){
      document.querySelector('form').submit();// ここでwindow.openが実行される
    });
  } else {
    phantom.exit(1);
  }
});

// window.openの処理
function windowAction(testFx, onReady, timeOutMillis) {
  var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, // タイムアウト設定。デフォルト3秒
  start = new Date().getTime(),
  condition = false,
  //0.05秒ごとにチェックするインターバル処理
  interval = setInterval(function() {
    if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
      // タイムアウト時の判定処理
      condition = (typeof(testFx) === 'string' ? eval(testFx) : testFx()); // 第一引数の関数を実行して格納
    } else {
      if (!condition) {
        // タイムアウト判定時PhantomJSを終了
        phantom.exit(1);
      } else {
        // 処理完了後、第二引数の関数を呼び出しインターバル解除
        typeof(onReady) === 'string' ? eval(onReady) : onReady(); // 引数に文字を与えた場合はその関数を呼び出す
        clearInterval(interval); // インターバル解除
      }
    }
  }, 50); //< repeat check every 50ms
};

// window.openで開いたページの処理
function loginAction(page) {
  page.evaluate(function(params){
    // 次の処理へ
  });
};

 


まとめ

aタグのonClickにjavascript:window.openが設定されている時などに使用してみてください。
わからないことを調べるときに欲しい情報が英語のことが多くあるので、英語の文法記法やプログラムで使用する言い回しを覚えたい。
 
 
参考サイト