2015-03-09 6 views
11

Der folgende Code möchte, dass Phantom.js die Seite lädt, auf eine Schaltfläche klickt und 5 Sekunden wartet, bevor der HTML-Code der Seite zurückgegeben wird. setTimeout in Phantom.js

Problem: jedoch setTimeout() mit der Verzögerung von 5 Sekunden erstellen bewirkt, dass die page.evaluate Funktion null an die Callback-Funktion anstelle des HTML zurückzukehren.

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

      }) 
     }) 
    }) 
}) 

Ersetzen setTimeout() mit Meteor.setTimeout() führt zu einem weiteren Fehler:

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

Antwort

9

page.evaluate() die Sandbox-Seite Rahmen PhantomJS ist. Es hat keinen Zugriff auf Variablen, die auf der Außenseite definiert sind. Wenn Sie das Timeout benötigen, dann müssen Sie zwei Anrufe page.evaluate() tun, weil man nichts von einer asynchronen Funktion (explanation) zurückgeben kann:

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

Statt den zweiten page.evaluate() Aufruf verwenden, können Sie den Code verkürzen kann durch direkter Zugriff auf den Inhalt als definiert here:

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

Dies ist keine gute Lösung, aber funktioniert, wenn alles, was Sie tun möchten, ist Seite Änderungen auf der Schaltfläche klickt und Formular abschickt handhaben. Deklarieren Sie einfach die Funktionsvariablen außerhalb von page.open() und ordnen Sie ihnen später Seitenbewertungsfunktionen zu. onLoadFinished wird aufgerufen, nachdem die Seite mit den Änderungen aus dem Klick der Schaltfläche neu geladen wurde und Sie können sie dann erneut auswerten.

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

Es ist nicht schön, aber es funktioniert.