2012-03-23 15 views
48

Sto usando PhantomJS page.evaluate() per fare un po 'di scraping. Il mio problema è che il codice che ho passato alla pagina del webkit è in modalità sandbox e quindi non ha accesso alle variabili del mio script fantasma principale. Questo rende difficile rendere generico il codice di scraping.Passare argomenti con page.valutare

page.open(url, function() { 
    var foo = 42; 

    page.evaluate(function() { 
    // this code has no access to foo 
    console.log(foo); 
    }); 
} 

Come posso inserire argomenti nella pagina?

+2

Credo che si può trovare la risposta più sufficienti: http://stackoverflow.com/questions/28524130/phantomjs -does-not-take-arguments-to-function-passa-alla-pagina-valuta – user2977636

risposta

62

Ho avuto esattamente questo problema. Può essere fatto con un po 'di inganno, perché anche page.evaluate può accettare una stringa.

Ci sono diversi modi per farlo, ma io uso un wrapper chiamato evaluate, che accetta parametri aggiuntivi da passare alla funzione che deve essere valutata sul lato webkit. Si potrebbe utilizzare in questo modo:

page.open(url, function() { 
    var foo = 42; 

    evaluate(page, function(foo) { 
    // this code has now has access to foo 
    console.log(foo); 
    }, foo); 
}); 

E qui è la funzione evaluate():

/* 
* This function wraps WebPage.evaluate, and offers the possibility to pass 
* parameters into the webpage function. The PhantomJS issue is here: 
* 
* http://code.google.com/p/phantomjs/issues/detail?id=132 
* 
* This is from comment #43. 
*/ 
function evaluate(page, func) { 
    var args = [].slice.call(arguments, 2); 
    var fn = "function() { return (" + func.toString() + ").apply(this, " + JSON.stringify(args) + ");}"; 
    return page.evaluate(fn); 
} 
+0

Brillante! Questo codice sorgente ha chiaramente spiegato come funziona. – Light

+1

Per una spiegazione approfondita del motivo per cui ciò accade, controlla questa domanda: http://stackoverflow.com/questions/12222856/passing-arguments-to-anonymous-function-inside-page-includejs-and-page- valutazione –

+0

La tua soluzione sembra essere buona, ma cosa succede se voglio passare la richiamata, è possibile? Due giorni sono andati alla spazzatura.Ci sono argomenti come oggetti globali di callback JS? – Velaro

2

Altra possibilità: passare le variabili con l'url. Ad esempio, per passare oggetto x

// turn your object "x" into a JSON string 
var x_json = JSON.stringify(x); 

// add to existing url 
// you might want to check for existing "?" and add with "&" 
url += '?' + encodeURIComponent(x_json); 

page.open(url, function(status){ 
    page.evaluate(function(){ 
     // retrieve your var from document URL - if added with "&" this needs to change 
     var x_json = decodeURIComponent(window.location.search.substring(1)); 
     // evil or not - eval is handy here 
     var x = eval('(' + x_json + ')');  
    )} 
}); 
+0

Non funzionerebbe in PhantomJS, perché 'page.evaluate()' è in modalità sandbox. –

60

Il cambiamento è stato spinto e ora lo si può utilizzare come

page.open(url, function() { 
    var foo = 42; 

    page.evaluate(function(foo) { 
    // this code has now has access to foo 
    console.log(foo); 
    }, foo); 
} 

I dettagli spinta sono qui: https://github.com/ariya/phantomjs/commit/81794f9096

+0

questo è il modo migliore per farlo nel caso in cui l'oggetto che passi non sia complesso, come una funzione, ecc. – Nik

+5

Puoi fornire un esempio di passaggio di più argomenti? – cwd

+0

Per coloro che desiderano passare più argomenti, hai provato a utilizzare un array come argomento singolo? – bla

0

Questo funziona per me:

page.evaluate("function() {document.body.innerHTML = '" + size + uid + "'}"); 

Mezzi per mettere tutto come una stringa. In ogni caso diventa una stringa. Controlla la fonte della biblioteca.

2

V'è la soluzione che funziona con PhantomJS 0.9.2 e 0.2.0:

page.evaluate(
    function (aa, bb) { document.title = aa + "/" + bb;}, //the function 
    function (result) {}, // a callback when it's done 
    "aaa", //attr 1 
    "bbb"); //attr 2 
+1

Questo probabilmente non è direttamente per PhantomJS, ma un ponte tra node.js e PhantomJS. Tali bridge usano una sintassi leggermente diversa. –

+1

Questa risposta è il ticket se si sta tentando di passare un argomento nel metodo di valutazione sandbox nel [ponte phantomjs-nodejs] (https://github.com/sgentle/phantomjs-node). La documentazione è ancora fresca. Ho quasi rinunciato fino a quando ho trovato questa risposta. Grazie! – DMfll

0

non puoi semplicemente associare i args alla funzione ??

page.evaluate.bind(args)(callbackFn) 
0

Mentre è possibile passare argomenti in evaluate(function, arg1, arg2, ...), questo è spesso un po 'macchinoso. Soprattutto nei casi in cui si passa in più variabili, o peggio, funzioni.

Per aggirare questo ostacolo, è possibile utilizzare injectJs(filename).

page.open(url, function() { 
    if (webpage.injectJs('my_extra_functionality.js')) { 
     page.evaluate(function() { 
      // this code has access to foo and also myFunction(); 
      console.log(foo); 
      console.log(myFunction()); 
     }); 
    } 
    else { 
     console.log("Failed to inject JS"); 
    } 
} 

Dove my_extra_functionality.js è un file locale nella stessa directory:

var foo = 42; 
var myFunction = function(){ 
    return "Hello world!"; 
} 
Problemi correlati