2012-12-19 4 views
14

Kann PhantomJS eine Alternative zu BeautifulSoup verwendet werden?wie kratzen links mit phantomjs

Ich versuche auf Etsy zu suchen und alle Links in Term zu besuchen. In Python weiß ich, wie man das macht (mit BeautifulSoup), aber heute möchte ich sehen, ob ich das gleiche mit PhantomJS machen kann. Ich komme nicht sehr weit.

Dieses Skript sollte „Hallo Kitty“ auf Etsy suchen und zurück alle Produkte <a class="listing-thumb" href=...></a> und sie in der Konsole aus. Idealerweise würde ich sie später besuchen und die Informationen bekommen, die ich brauche. Im Moment erstarrt es gerade. Irgendwelche Ideen?

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

Ich habe mit der Verwendung von CasperJS, gespielt, die besser dafür ausgelegt sein kann.

+1

Ich empfehle Auschecken [cheerio] (https://github.com/MatthewMueller/cheerio). Es ist perfekt für die Aufgabe des Scraping von Webseiten geeignet, und seine Traversierungs-/Manipulations-APIs sind denen von jQuery sehr ähnlich. – davidchambers

Antwort

4

Das einzige Problem mit Ihrem Code ist, dass Sie phantomjs Bereiche nicht verstehen. Sie haben Phantom- und Seitenbereiche. Sie haben versucht, JavaScript-DOM-Objektreferenzen (die nicht serialisiert werden können) vom Seitenbereich (page.evaluate wird im Seitenbereich ausgeführt) in den Phantom-Hauptbereich zurückzugeben. Ich denke, das ist nicht möglich. Hier folgt Code, das funktioniert:

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() nicht serialisiert werden kann und das Rück komplexe Objekte wie HTMLElements oder Nodelisten, so dass Sie sie zu serializable Dinge vor zur Karte haben:

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

Hinweis: hier benutzen wir [].map.call() um einen NodeList als Standard Array zu behandeln.

+7

+1 wünschen diese wurden in der Dokumentation explizit angegeben. Es hat Stunden gedauert, bis ich diese Antwort gefunden habe! :-( – Alberto

+2

Uhmmm ... Wollen Sie das bedeuten „* Rückkehr native Typen *“ Pfeil in CasperJS Dokumentation Diagramm oder die Notiz in der PhantomJS Dokumentation „* Die Argumente und der Rückgabewert die Funktion auswerten ein einfaches primitives Objekt sein muss. [...] * "(Anmerkung: der Link in CasperJS zu [WebPage.evaluate] (https://github.com/ariya/phantomjs/wiki/API-Reference-WebPage#evaluatefunction-arg1-arg2--object) ist falsch)? ich bin sicher, dass ich etwas, weil das Skript in dem Diagramm leicht über bin fehle, und als Neuling dachte ich naiv, dass ich ohne die doc PhantomJS tun kann. Danke trotzdem für dieses fantastische Werkzeug. – Alberto

+0

Ja, so Einfach, aber nirgendwo in der Dokumentation. Vielen Dank. – HartleySan

1

Hier ist ein Code, den ich kürzlich geschrieben habe, der URLs mit PhantomJs scrapt. Wenn Sie nur eine URL angeben, werden alle URLs auf der Seite angezeigt, wenn Sie ein Argument von class|id gefolgt von einem "Klassen-/ID-Namen" angeben zeigen die URLs der Klasse/id nur.

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