Подтвердить что ты не робот

Использование нескольких page.open в Single Script

Моя цель - выполнить 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

В результате я получаю только скриншот третьей страницы. Я не уверен, почему PhantomJS пропускает первый и второй блоки кода (очевидно из отсутствующих сообщений console.log(), которые должны были выводиться из 1-го и 2-го блоков) и выполняются только третий блок кода.

4b9b3361

Ответ 1

Проблема заключается в том, что второй 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();

Ответ 2

Я пробовал принятые варианты ответа, но он не работает (по крайней мере, не для v2.1.1).

Чтобы быть точным, принятый ответ работал некоторое время, но я все еще испытывал спорадические неудачные вызовы page.open(), около 90% времени на конкретных наборах данных.

Самый простой ответ, который я нашел, - создать экземпляр нового модуля страницы для каждого 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(){
        // ... 
        // ...
    })
}

Следующее из документации модуля api по методу close говорит:

close() {void}

Закройте страницу и освободите кучу памяти, связанную с ней. Не используйте экземпляр страницы после вызова этого.

Из-за некоторых технических ограничений объект веб-страницы может не быть полностью собранным мусором. Это часто встречается, когда один и тот же объект используется снова и снова. Вызов этой функции может остановить увеличение распределения кучи.

В основном после того, как я протестировал метод close(), я решил использовать один и тот же экземпляр веб-страницы для разных вызовов open(), что слишком ненадежно, и это нужно сказать.

Ответ 3

Использование очереди процессов, образец:

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();

Надеюсь, это полезно, привет.

Ответ 4

Вы можете использовать рекурсию:

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());