2012-12-19 20 views
14

Può PhantomJS essere utilizzato in alternativa al BeautifulSoup?come raschiare legami con phantomjs

Sto cercando di cercare su Etsy e visitare tutti i collegamenti in termini. In Python, so come farlo (con BeautifulSoup) ma oggi voglio vedere se posso fare lo stesso con PhantomJS. Non sto andando molto lontano.

Questo script dovrebbe cercare "ciao kitty" su Etsy e restituire tutti i prodotti <a class="listing-thumb" href=...></a> e stamparli nella console. Idealmente vorrei visitarli più tardi e ottenere le informazioni di cui ho bisogno. In questo momento si blocca solo. Qualche idea?

var page = require('webpage').create(); 
var url = 'http://www.etsy.com/search?q=hello%20kitty'; 

page.open(url, function(status){ 
    // list all the a.href links in the hello kitty etsy page 
    var link = page.evaluate(function() { 
     return document.querySelectorAll('a.listing-thumb'); 
    }); 
    for(var i = 0; i < link.length; i++){ console.log(link[i].href); } 
    phantom.exit(); 
}); 

ho accarezzato con l'utilizzo di CasperJS, che possono essere meglio progettato per questo.

+1

Consiglio di controllare [cheerio] (https://github.com/MatthewMueller/cheerio). È perfettamente adatto al compito di raschiare le pagine Web e le sue API di attraversamento/manipolazione sono molto simili a quelle di jQuery. – davidchambers

risposta

4

L'unico problema con il codice è che non si capisce ambiti phantomjs. Hai degli ambiti fantasma e di pagina. Hai provato a restituire riferimenti agli oggetti DOM JavaScript (quelli non possono essere serializzati) dall'ambito della pagina (pagina.valuta le esecuzioni nello scope della pagina) allo scopo principale fantasma. Penso che non sia possibile. Ecco qui di seguito il codice che funziona:

var page = require('webpage').create(); 
var url = 'http://www.etsy.com/search?q=hello%20kitty'; 

// for debug (to see if page returns status code 200) 
page.onResourceReceived = function(response) { 
    if (response.url === url) { 
     console.log('Resorce: "' + response.url + '" status: ' + response.status); 

     if (response.status === 200) { 
      console.log(response.url); 
      for (var i = 0; i < response.headers.length; i++) { 
       console.log(response.headers[i].name + ': ' + response.headers[i].value); 
      } 
     } 
    } 
}; 

page.onLoadFinished = function(status){ 
    console.log('Status: ' + status); 

    console.log('Starting evaluate...'); 
    var links = page.evaluate(function() { 
     var nodes = [], 
      matches = document.querySelectorAll("a.listing-thumb"); 

      for(var i = 0; i < matches.length; ++i) { 
       nodes.push(matches[i].href); 
      } 

      return nodes; 
    }); 
    console.log('Done evaluate... count: ' + links.length); 

    if (links && links.length > 0) { 
     for(var i = 0; i < links.length; ++i) { 
      console.log('(' + i + ') ' + links[i]); 
     } 
    } else { 
     console.log("No match found!"); 
    } 

    phantom.exit(0); 
}; 

page.open(url); 
35

PhantomJS evaluate() non può serializzare e restituire oggetti complessi come HTMLElements o NodeLists, in modo da avere a loro mappare alle cose serializzabili prima:

var page = require('webpage').create(); 
var url = 'http://www.etsy.com/search?q=hello%20kitty'; 

page.open(url, function(status) { 
    // list all the a.href links in the hello kitty etsy page 
    var links = page.evaluate(function() { 
     return [].map.call(document.querySelectorAll('a.listing-thumb'), function(link) { 
      return link.getAttribute('href'); 
     }); 
    }); 
    console.log(links.join('\n')); 
    phantom.exit(); 
}); 

Nota: qui usiamo [].map.call() per trattare uno NodeList come standard Array.

+7

+1 Vorrei che ciò fosse esplicitamente indicato nei documenti. Come newby, mi ci sono volute ore per trovare questa risposta! :-( – Alberto

+2

Uhmmm ... Intendi la freccia "* return nativi types *" nel diagramma della documentazione di CasperJS o la nota nella documentazione di PhantomJS "* Gli argomenti e il valore restituito alla funzione di valutazione devono essere un semplice oggetto primitivo. [...] * "(nota: il link in CasperJS a [WebPage.evaluate] (https://github.com/ariya/phantomjs/wiki/API-Reference-WebPage#evaluatefunction-arg1-arg2--object) è sbagliato)? Sono sicuro che mi manca qualcosa, perché la sceneggiatura nel diagramma è facilmente supervisionata, e come novellino ho ingenuamente pensato di poter fare a meno del documento PhantomJS.Grazie comunque per questo fantastico strumento – Alberto

+0

Sì, quindi semplice, ma da nessuna parte nei documenti. Grazie mille. – HartleySan

1

Ecco un codice che ho recentemente scritto che raschia gli URL usando PhantomJs, se fornisci solo un URL che visualizzerà tutti gli URL nella pagina, se fornisci un argomento di class|id seguito da un "nome di classe/id" verrà mostra solo gli URL della classe/id.

////////////////////////////////////////////////////////// 
///// PhantomJS URL Scraper v.1.3 ///// 
// 
// Copyrighted by +A.M.Danischewski 2016+ (c) 
// This program may be reutilized without limits, provided this 
// notice remain intact. 
// 
// Usage: phantomjs phantom_urls.js <URL> [["class"|"id"] [<query id/class name>]] 
// 
// Argument 1: URL -- "https://www.youtube.com/watch?v=8TniRMwL2Vg" 
// Argument 2: "class" or "id" 
// Argument 3: If Argument 2 was provided, "class name" or "id name" 
// 
// By default this program will display ALL urls from a user supplied URL. 
// If a class name or id name is provided then only URL's from the class 
// or id are displayed. 
// 
/////////////////////////////////// 

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

if (system.args.length === 1) { 
    console.log(' Usage: phantomjs phantom_urls.js <URL> [["class"|"id"] [<query id/class name>]]'); 
    phantom.exit(); 
} 

address = system.args[1]; 
querytype= system.args[2]; 
queryclass = system.args[3]; 
page.open(address, function(status) { 
    if (status !== 'success') { 
    console.log('Error loading address: '+address); 
    } else { 
    //console.log('Success! In loading address: '+address); 
    } 
}); 

page.onConsoleMessage = function(msg) { 
    console.log(msg); 
} 

page.onLoadFinished = function(status) { 
    var dynclass="function() { window.class_urls = new Array(); window.class_urls_next=0; var listings = document.getElementsByClassName('"+queryclass+"'); for (var i=0; i < listings.length; i++) { var el = listings[i]; var ellnks=[].map.call(el.querySelectorAll('a'),function(link) {return link.getAttribute('href');}); var elhtml=el.innerHTML; window.class_urls.push(ellnks.join('\\n')); }; return window.class_urls;}"; 
    var dynid="function() { window.id_urls = new Array(); window.id_urls_next=0; var listings = document.getElementById('"+queryclass+"'); var ellnks=[].map.call(listings.querySelectorAll('a'),function(link) {return link.getAttribute('href');}); var elhtml=listings.innerHTML; window.id_urls.push(ellnks.join('\\n')); return window.id_urls;}"; 
    var allurls="function() { var links = page.evaluate(function() { return [].map.call(document.querySelectorAll('a'), function(link) { return link.getAttribute('href'); };); };); console.log(links.join('\\n')); }"; 
    var page_eval_function=""; 
    if (querytype === "class") { 
    console.log(page.evaluate(dynclass).toString().replace(/,/g, "\n")); 
    } else if (querytype === "id") { 
    console.log(page.evaluate(dynid).toString().replace(/,/g, "\n")); 
    } else { 
    var links = page.evaluate(function() { 
     return [].map.call(document.querySelectorAll('a'), function(link) { 
      return link.getAttribute('href'); 
     }); 
    });  
     console.log(links.join('\n')); 
    }    
    phantom.exit(); 
};