2015-03-09 17 views
11

Il codice seguente richiede a Phantom.js di caricare la pagina, fare clic su un pulsante e attendere 5 secondi prima di restituire il codice HTML della pagina.setTimeout in Phantom.js

Problema: Tuttavia usando setTimeout() per creare il ritardo di 5 secondi, si aumenta la funzione page.evaluate per tornare null alla funzione di callback al posto del codice HTML.

myUrl = 'http://www.google.com' 

var phantom = Meteor.npmRequire('phantom') 
phantom.create = Meteor.wrapAsync(phantom.create) 
phantom.create(function(ph) { 

    ph.createPage = Meteor.wrapAsync(ph.createPage) 
    ph.createPage(function(page) { 

     page.open = Meteor.wrapAsync(page.open) 
     page.open(listingUrl, function(status) { 
      console.log('Page loaded') 

      page.evaluate = Meteor.wrapAsync(page.evaluate) 
      page.evaluate(function() { 

       // Find the button 
       var element = document.querySelector('.search-btn'); 

       // create a mouse click event 
       var event = document.createEvent('MouseEvents'); 
       event.initMouseEvent('click', true, true, window, 1, 0, 0); 

       // send click to element 
       element.dispatchEvent(event); 

       // Give page time to process Click event 
       setTimeout(function() { 
        // Return HTML code 
        return document.documentElement.outerHTML 
       }, 5000) 

      }, function(html) { 

       // html is `null` 
       doSomething() 

      }) 
     }) 
    }) 
}) 

Sostituzione setTimeout() con Meteor.setTimeout() provoca un altro errore:

phantom stdout: ReferenceError: Can't find variable: Meteor 

risposta

9

page.evaluate() è il contesto di pagina sandbox di PhantomJS. Non ha accesso a variabili definite all'esterno. Se avete bisogno il timeout, allora avete bisogno di fare due chiamate a page.evaluate(), perché non si può tornare qualsiasi cosa, da una funzione asincrona (explanation):

page.evaluate(function() { 
    ... 
    element.dispatchEvent(event); 
}, function() { 
    setTimeout(function() { 
     page.evaluate(function() {  
      return document.documentElement.outerHTML 
     }, function(html) { 
      doSomething() 
     }) 
    }, 5000) 
}) 

Invece di usare la seconda page.evaluate() chiamata, si può accorciare il codice accedendo direttamente il contenuto come definito here:

setTimeout(function() { 
    page.get("content", function(content) { 
     doSomething() 
    }) 
}, 5000) 
0

Questa non è una grande soluzione, ma funziona se tutto quello che vogliamo fare è gestire i cambiamenti pagina su pulsanti e sottomette forma. Basta dichiarare le variabili di funzione all'esterno di page.open() e quindi assegnare loro le funzioni di valutazione della pagina più avanti all'interno. onLoadFinished verrà chiamato dopo che la pagina si è ricaricata con le modifiche dal clic del pulsante e quindi è possibile valutarla nuovamente.

var loadInProgress = false, 
jurl = 'http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js', 
page = require('webpage').create(); 

// declare variables outside page.open and assign them later inside 
var evalPageFunc; 

// assign callbacks which will be called by phantom 
page.onLoadStarted = function() { 
    loadInProgress = true; 
    console.log('load started'); 
}; 
page.onLoadFinished = function() { 
    loadInProgress = false; 
    console.log('load finished'); 
    if (evalPageFunc) { 
     // since the page has loaded we can safely evaluate it 
     var mydata = evalPageFunc(); 
     console.log(mydata); 
     if (!mydata.havemore) { 
     phantom.exit(); 
     // or next url 
     } 
    } 
}; 

page.open(url, function(status) { 
    page.includeJs(jurl, function(){ 

    // define your page evaluating functions 
    evalPageFunc = function(){ 
     return page.evaluate(function() { 
     var datafromhtml = {}, havemoretoclick = true; 
     // get your data and perform clicks if you want to 
     // datafromhtml.somedata = $('stealme').text(); 
     // $("clickme").click(); 
     return { 
      havemore: havemoretoclick, 
      data: datafromhtml 
     }; 
     }); 
    } 
    var k = evalPageFunc(); 
    }); 
}); 

Non è bello ma funziona.