Domanda Promise framework per PhantomJS?


Sono nuovo di PhantomJS. Voglio caricare una pagina, raschiare i suoi collegamenti, quindi aprire ciascuno di quelli in sequenza, uno alla volta, forse anche con un ritardo tra ogni richiesta. Ho difficoltà a far sparare l'uno dopo l'altro, quindi ho pensato che forse avrei potuto usare le promesse per risolvere questo problema, ma non credo che le librerie Node funzionino con Phantom. Ogni esempio che ho visto finora apre una singola pagina e quindi si chiude.

Ecco cosa ho:

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

page.open('http://example.com/secretpage', function(status) {
    console.log(status);
    if(status !== 'success') {
        console.log('Unable to access network');
    } else {
        var links = page.evaluate(function() {
            var nodes = [];
            var matches = document.querySelectorAll('.profile > a');
            for(var i = 0; i < matches.length; ++i) {
                nodes.push(matches[i].href);
            }
            return nodes;
        });


        links.forEach(function(link) {
            console.log(link);
            page.open(link, function(status) { // <---- tries opening every page at once
                console.log(status);

                var name = page.evaluate(function() {
                    return document.getElementById('username').innerHTML;
                });

                console.log(name);
                page.render('profiles/'+name + '.png');
            });
        });
    }
//    phantom.exit();
});

C'è un modo per aprire ogni link in sequenza?


11
2018-02-25 04:34


origine


risposte:


Io uso per questo scenario tipico async.js e soprattutto la coda componente.

Ecco un'implementazione molto semplice

phantom.injectJs('async.js');

var q = async.queue(function (task, callback) {
    page.open(task.url, function(status) { // <---- tries opening every page at once
                if(status !== 'success') {
        console.log('Unable to open url > '+task.url);
    } else {
                console.log('opened '+task.url);
                //do whatever you want here ...
                    page.render(Date.now() + '.png');
                }           
                callback();
            });

}, 1);

// assign a callback
q.drain = function() {
    console.log('all urls have been processed');
    phantom.exit();
}

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

page.open('http://phantomjs.org/', function(status) {
    console.log(status);
    if(status !== 'success') {
        console.log('Unable to access network');
    } else {
        var links = page.evaluate(function() {
            var nodes = [];
            var matches = document.querySelectorAll('a');
            for(var i = 0; i < matches.length; ++i) {
                nodes.push(matches[i].href);
            }
            return nodes;
        });

        links.forEach(function(link) {
                q.push({url: link}, function (err) {
                    console.log('finished processing '+link);
                });
        });
    }   
});

Gli URL vengono aggiunti alla coda e verranno elaborati in parallelo (fino al limite di concorrenza, uno qui). Riuso la stessa istanza della pagina ma non è obbligatoria.

Come ho già fatto questo tipo di crawler in passato, lascia che ti dia altri due consigli:

  • Non caricare le immagini per accelerare il test
  • href è a volte relativo, quindi controlla prima se è un url valido

4
2018-02-25 07:31



[MODIFICARE]

È necessario accodare questo. Ho modificato il codice e aggiunto un semplice meccanismo di coda.

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

page.open('http://example.com/secretpage', function(status) {
    console.log(status);
    if (status !== 'success') {
        console.log('Unable to access network');
    } else {
        var links = page.evaluate(function() {
            var nodes = [];
            var matches = document.querySelectorAll('.profile > a');
            for (var i = 0; i < matches.length; ++i) {
                nodes.push(matches[i].href);
            }
            return nodes;
        });

        var pointer = 0,
            linksCount = links.length,
            q = function() {
                var link = links[pointer];
                console.log(link);

                page.open(link, function(status) { // <---- tries opening every page at once
                    console.log(status);

                    var name = page.evaluate(function() {
                        return document.getElementById('username').innerHTML;
                    });

                    console.log(name);
                    page.render('profiles/' + name + '.png');

                    // pointer increaments;
                    pointer++;
                    if (pointer == linksCount) {
                        // recursion exit
                        phantom.exit();
                    }
                    else {
                        // recursive cal;
                        q();
                    }
                });             

            };

        // start queue to load links one by one     
        q();
});

NOTA: foreach non attende il caricamento di ogni pagina e il caricamento della pagina è asincrono. Da qui il tuo problema.

Puoi leggere una risposta a una domanda simile su CasperJS (un wrapper su PhantomJS) con codice su come gestirlo da Come per il ciclo in casperjs


4
2018-02-26 07:50



Potresti usare Phantom-promessa  A PhantomJS bridge with a promise based api. o fantasma  PhantomJS integration module for NodeJS. Un'altra opzione per aprire ciascun collegamento in sequenza

  1. Cybermaxs risposta
  2. Usa esempio da aspettare come suggerito Cybermaxs on altra domanda SO

Fondamentalmente hai 3 opzioni, ma puoi dare un'occhiata Casperjs  Navigation scripting & testing for PhantomJS and SlimerJS


2
2017-09-21 14:06