web-dev-qa-db-fra.com

Utilisation de plusieurs pages.open dans un seul script

Mon objectif est d'exécuter PhantomJS en utilisant:

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

Et puis à l'intérieur de script.js, Je prévois d'utiliser plusieurs page.open() pour capturer des captures d'écran de différentes pages telles que:

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

En exécutant exec, j'obtiens la sortie suivante à la page:

Array ( [0] => opening page 3 ) 0

En conséquence, je ne reçois que la capture d'écran de la 3e page. Je ne sais pas pourquoi PhantomJS saute les premier et deuxième blocs de code (évidents à partir des messages console.log() manquants qui étaient censés être sortis des 1er et 2e blocs) et exécutent uniquement le troisième bloc de code.

24
asprin

Le problème est que le second page.open est invoqué avant la fin du premier, ce qui peut provoquer plusieurs problèmes. Vous voulez une logique à peu près comme la suivante (en supposant que les noms de fichiers sont donnés comme arguments de ligne de commande):

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

C'est récursif. Cela garantit que le traitement de la fonction est passé à page.open se termine, avec un petit délai de grâce de 100 ms, avant de passer au fichier suivant.

Au fait, vous n'avez pas besoin de répéter

page = require('webpage').create();
44
user663031

J'ai essayé les suggestions de réponses acceptées, mais cela ne fonctionne pas (du moins pas pour la v2.1.1).

Pour être précis, la réponse acceptée a fonctionné une partie du temps, mais j'ai toujours rencontré des appels sporadiques échoués à page.open (), environ 90% du temps sur des ensembles de données spécifiques.

La réponse la plus simple que j'ai trouvée est d'instancier un nouveau module de page pour chaque 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(){
        // ... 
        // ...
    })
}

Le texte suivant de la documentation de l'API du module de page sur la méthode close indique :

close () {void}

Fermez la page et libérez le segment de mémoire qui lui est associé. N'utilisez pas l'instance de page après avoir appelé cela.

En raison de certaines limitations techniques, l'objet de page Web peut ne pas être complètement récupéré. Cela se produit souvent lorsque le même objet est utilisé à plusieurs reprises. L'appel de cette fonction peut arrêter l'allocation de tas croissante.

Fondamentalement, après avoir testé la méthode close (), j'ai décidé d'utiliser la même instance de page Web pour différents appels open () est trop peu fiable et il fallait le dire.

8
believesInSanta

Vous pouvez utiliser la récursivité:

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

À l'aide de processus en file d'attente, exemple:

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

J'espère que cela est utile, salut.

1
froilanq