142 votes

phantomjs n'attend pas le chargement complet de la page

J'utilise PhantomJS v1.4.1 pour charger certaines pages web. Je n'ai pas accès à leur serveur, je reçois juste des liens pointant vers eux. J'utilise une version obsolète de Phantom car j'ai besoin de supporter Adobe Flash sur ces pages web.

Le problème est que de nombreux sites web chargent leur contenu mineur de manière asynchrone et c'est pourquoi le rappel onLoadFinished de Phantom (analogue à onLoad en HTML) se déclenche trop tôt alors que tout n'est pas encore chargé. Quelqu'un peut-il me suggérer comment attendre le chargement complet d'une page web pour faire, par exemple, une capture d'écran avec tout le contenu dynamique comme les publicités ?

13voto

user1278316 Points 61

Dans mon programme, j'utilise une certaine logique pour juger s'il est en charge : en observant les requêtes réseau, s'il n'y a pas eu de nouvelle requête au cours des 200 dernières ms, je considère qu'il est en charge.

A utiliser après onLoadFinish().

function onLoadComplete(page, callback){
    var waiting = [];  // request id
    var interval = 200;  //ms time waiting new request
    var timer = setTimeout( timeout, interval);
    var max_retry = 3;  //
    var counter_retry = 0;

    function timeout(){
        if(waiting.length && counter_retry < max_retry){
            timer = setTimeout( timeout, interval);
            counter_retry++;
            return;
        }else{
            try{
                callback(null, page);
            }catch(e){}
        }
    }

    //for debug, log time cost
    var tlogger = {};

    bindEvent(page, 'request', function(req){
        waiting.push(req.id);
    });

    bindEvent(page, 'receive', function (res) {
        var cT = res.contentType;
        if(!cT){
            console.log('[contentType] ', cT, ' [url] ', res.url);
        }
        if(!cT) return remove(res.id);
        if(cT.indexOf('application') * cT.indexOf('text') != 0) return remove(res.id);

        if (res.stage === 'start') {
            console.log('!!received start: ', res.id);
            //console.log( JSON.stringify(res) );
            tlogger[res.id] = new Date();
        }else if (res.stage === 'end') {
            console.log('!!received end: ', res.id, (new Date() - tlogger[res.id]) );
            //console.log( JSON.stringify(res) );
            remove(res.id);

            clearTimeout(timer);
            timer = setTimeout(timeout, interval);
        }

    });

    bindEvent(page, 'error', function(err){
        remove(err.id);
        if(waiting.length === 0){
            counter_retry = 0;
        }
    });

    function remove(id){
        var i = waiting.indexOf( id );
        if(i < 0){
            return;
        }else{
            waiting.splice(i,1);
        }
    }

    function bindEvent(page, evt, cb){
        switch(evt){
            case 'request':
                page.onResourceRequested = cb;
                break;
            case 'receive':
                page.onResourceReceived = cb;
                break;
            case 'error':
                page.onResourceError = cb;
                break;
            case 'timeout':
                page.onResourceTimeout = cb;
                break;
        }
    }
}

11voto

Brankodd Points 116

J'ai trouvé cette approche utile dans certains cas :

page.onConsoleMessage(function(msg) {
  // do something e.g. page.render
});

Ensuite, si vous possédez la page, mettez un script à l'intérieur :

<script>
  window.onload = function(){
    console.log('page loaded');
  }
</script>

5voto

manutero Points 45

J'ai trouvé cette solution utile dans une application NodeJS. Je ne l'utilise que dans les cas désespérés car elle lance un timeout afin d'attendre le chargement complet de la page.

Le deuxième argument est la fonction de rappel qui sera appelée lorsque la réponse sera prête.

phantom = require('phantom');

var fullLoad = function(anUrl, callbackDone) {
    phantom.create(function (ph) {
        ph.createPage(function (page) {
            page.open(anUrl, function (status) {
                if (status !== 'success') {
                    console.error("pahtom: error opening " + anUrl, status);
                    ph.exit();
                } else {
                    // timeOut
                    global.setTimeout(function () {
                        page.evaluate(function () {
                            return document.documentElement.innerHTML;
                        }, function (result) {
                            ph.exit(); // EXTREMLY IMPORTANT
                            callbackDone(result); // callback
                        });
                    }, 5000);
                }
            });
        });
    });
}

var callback = function(htmlBody) {
    // do smth with the htmlBody
}

fullLoad('your/url/', callback);

3voto

Dayong Points 5064

Il s'agit d'une mise en œuvre de la réponse de Supr. Elle utilise également setTimeout au lieu de setInterval comme l'a suggéré Mateusz Charytoniuk.

Phantomjs sortira dans 1000ms s'il n'y a pas de requête ou de réponse.

// load the module
var webpage = require('webpage');
// get timestamp
function getTimestamp(){
    // or use Date.now()
    return new Date().getTime();
}

var lastTimestamp = getTimestamp();

var page = webpage.create();
page.onResourceRequested = function(request) {
    // update the timestamp when there is a request
    lastTimestamp = getTimestamp();
};
page.onResourceReceived = function(response) {
    // update the timestamp when there is a response
    lastTimestamp = getTimestamp();
};

page.open(html, function(status) {
    if (status !== 'success') {
        // exit if it fails to load the page
        phantom.exit(1);
    }
    else{
        // do something here
    }
});

function checkReadyState() {
    setTimeout(function () {
        var curentTimestamp = getTimestamp();
        if(curentTimestamp-lastTimestamp>1000){
            // exit if there isn't request or response in 1000ms
            phantom.exit();
        }
        else{
            checkReadyState();
        }
    }, 100);
}

checkReadyState();

3voto

Rocco Musolino Points 187

Voici le code que j'utilise :

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

page.open('http://....', function(){
      console.log(page.content);
      var k = 0;

      var loop = setInterval(function(){
          var qrcode = page.evaluate(function(s) {
             return document.querySelector(s).src;
          }, '.qrcode img');

          k++;
          if (qrcode){
             console.log('dataURI:', qrcode);
             clearInterval(loop);
             phantom.exit();
          }

          if (k === 50) phantom.exit(); // 10 sec timeout
      }, 200);
  });

En fait, vous êtes censé savoir que la page est entièrement téléchargée lorsqu'un élément donné apparaît dans le DOM. Le script va donc attendre que cela se produise.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X