web-dev-qa-db-ja.com

単一のスクリプトで複数のpage.openを使用する

私の目標は、以下を使用してPhantomJSを実行することです。

_// adding $op and $er for debugging purposes
exec('phantomjs script.js', $op, $er);
print_r($op);
echo $er;
_

そして、_script.js_内で、複数のpage.open()を使用して、次のような異なるページのスクリーンショットをキャプチャする予定です。

_var url = 'some dynamic url goes here';
page = require('webpage').create();
page.open(url, function (status) {
    console.log('opening page 1');  
    page.render('./slide1.png');            
});

page = require('webpage').create();
page.open(url, function (status) {
    console.log('opening page 2');  
    page.render('./slide2.png');        
});

page = require('webpage').create();
page.open(url, function (status) {
    console.log('opening page 3');  
    page.render('./slide3.png');        
    phantom.exit(); //<-- Exiting phantomJS only after opening all 3 pages
});
_

execを実行すると、ページに次の出力が表示されます。

_Array ( [0] => opening page 3 ) 0
_

その結果、3ページ目のスクリーンショットしか取得できません。 PhantomJSがコードの最初と2番目のブロックをスキップして(1番目と2番目のブロックから出力されるはずだったconsole.log()メッセージが欠落していることから明らか)、3番目のコードブロックのみを実行する理由はわかりません。

24
asprin

問題は、最初の__が終了する前に2番目の_page.open_が呼び出されるため、複数の問題が発生する可能性があることです。おおよそ次のようなロジックが必要です(ファイル名がコマンドライン引数として指定されている場合):

_function handle_page(file){
    page.open(file,function(){
        ...
        page.evaluate(function(){
            ...do stuff...
        });
        page.render(...);
        setTimeout(next_page,100);
    });
}
function next_page(){
    var file=args.shift();
    if(!file){phantom.exit(0);}
    handle_page(file);
}
next_page();
_

そう、それは再帰的です。これにより、_page.open_に渡された関数の処理は、次のファイルに進む前に、わずか100ミリ秒の猶予期間で終了します。

ところで、繰り返し続ける必要はありません

_page = require('webpage').create();
_
44
user663031

受け入れられた回答の提案を試しましたが、機能しません(少なくともv2.1.1では機能しません)。

正確にするために、受け入れられた答えは時々機能しましたが、特定のデータセットで約90%の時間、散発的な失敗したpage.open()呼び出しがまだ発生しました。

私が見つけた最も簡単な答えは、各URLに新しいページモジュールをインスタンス化することです。

// first page
var urlA = "http://first/url"
var pageA = require('webpage').create()

pageA.open(urlA, function(status){
    if (status){
        setTimeout(openPageB, 100) // open second page call
    } else{
        phantom.exit(1)
    }
})

// second page
var urlB = "http://second/url"
var pageB = require('webpage').create()

function openPageB(){
    pageB.open(urlB, function(){
        // ... 
        // ...
    })
}

closeメソッドに関するページモジュールAPIドキュメントからの次のような

close(){void}

ページを閉じて、それに関連付けられているメモリヒープを解放します。これを呼び出した後、ページインスタンスを使用しないでください。

技術的な制限のため、Webページオブジェクトは完全にガベージコレクションされない場合があります。これは、同じオブジェクトが何度も何度も使用される場合によく発生します。この関数を呼び出すと、増加するヒープ割り当てが停止する場合があります。

基本的に、close()メソッドをテストした後、異なるopen()呼び出しに同じWebページインスタンスを使用することはあまりにも信頼性が低く、言う必要があると判断しました。

8
believesInSanta

再帰を使用できます:

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

// the urls to navigate to
var urls = [
    'http://phantomjs.org/',
    'https://Twitter.com/sidanmor',
    'https://github.com/sidanmor'
];

var i = 0;

// the recursion function
var genericCallback = function () {
    return function (status) {
        console.log("URL: " + urls[i]);
        console.log("Status: " + status);
        // exit if there was a problem with the navigation
        if (!status || status === 'fail') phantom.exit();

        i++;

        if (status === "success") {

            //-- YOUR STUFF HERE ---------------------- 
            // do your stuff here... I'm taking a picture of the page
            page.render('example' + i + '.png');
            //-----------------------------------------

            if (i < urls.length) {
                // navigate to the next url and the callback is this function (recursion)
                page.open(urls[i], genericCallback());
            } else {
                // try navigate to the next url (it is undefined because it is the last element) so the callback is exit
                page.open(urls[i], function () {
                    phantom.exit();
                });
            }
        }
    };
};

// start from the first url
page.open(urls[i], genericCallback());
2
sidanmor

キュープロセスの使用、サンプル:

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

// Queue Class Helper
var Queue = function() {
    this._tasks = [];
};
Queue.prototype.add = function(fn, scope) {
    this._tasks.Push({fn: fn,scope: scope});
    return this;
};
Queue.prototype.process = function() {
    var proxy, self = this;
    task = this._tasks.shift();
    if(!task) {return;}
    proxy = {end: function() {self.process();}};
    task.fn.call(task.scope, proxy);
    return this;        
};
Queue.prototype.clear = function() {
    this._tasks = []; return this;
};

// Init pages .....  
var q = new Queue();       

q.add(function(proxy) {
  page.open(url1, function() {
    // page.evaluate
    proxy.end();
  });            
});

q.add(function(proxy) {
  page.open(url2, function() {
    // page.evaluate
    proxy.end();
  });            
});


q.add(function(proxy) {
  page.open(urln, function() {
    // page.evaluate
    proxy.end();
  });            
});

// .....

q.add(function(proxy) {
  phantom.exit()
  proxy.end();
});

q.process();

よろしくお願いします。

1
froilanq