Potresti aver usato NodeJS come web server, ma lo sapevi che puoi anche usarlo per il web scraping? In questo tutorial, esamineremo come raschiare le pagine Web statiche e quelle fastidiose con contenuto dinamico, con l'aiuto di NodeJS e alcuni utili moduli NPM.
Il web scraping ha sempre avuto una connotazione negativa nel mondo dello sviluppo web - e per una buona ragione. Nello sviluppo moderno, le API sono presenti per i servizi più popolari e dovrebbero essere utilizzate per recuperare i dati anziché lo scraping. Il problema inerente allo scraping è che si basa sulla struttura visiva della pagina che viene raschiata. Ogni volta che l'HTML cambia, non importa quanto piccola possa essere la modifica, può completamente violare il codice.
Nonostante questi difetti, è importante imparare un po 'sul web scraping e alcuni degli strumenti disponibili per aiutare con questo compito. Quando un sito non rivela alcuna API o alcun feed di syndication (RSS / Atom, ecc.), L'unica opzione che ci rimane per ottenere quel contenuto ... è raschiare.
Nota: se non è possibile ottenere le informazioni richieste tramite un'API o un feed, è un buon segno che il proprietario non desidera che tali informazioni siano accessibili. Tuttavia, ci sono delle eccezioni.
I raschietti possono essere scritti in qualsiasi lingua, davvero. Il motivo per cui mi piace usare Node è dovuto alla sua natura asincrona, il che significa che il mio codice non è bloccato in nessun punto del processo. Sono abbastanza familiare con JavaScript, quindi questo è un ulteriore vantaggio. Infine, ci sono alcuni nuovi moduli che sono stati scritti per NodeJS che rendono facile scansionare i siti web in maniera affidabile (beh, affidabile come lo scraping può arrivare!). Iniziamo!
Iniziamo con il caso d'uso semplice: pagine web statiche. Queste sono le tue pagine web standard di routine. Per questi, Yahoo! Query Language (YQL) dovrebbe fare il lavoro molto bene. Per chi non ha familiarità con YQL, è una sintassi simile a SQL che può essere utilizzata per lavorare con diverse API in modo coerente.
YQL ha alcune ottime tabelle per aiutare gli sviluppatori a scaricare HTML da una pagina. Quelli che voglio evidenziare sono:
Esaminiamo ciascuno di essi e esaminiamo come implementarli in NodeJS.
Il html table è il modo più semplice per rimuovere HTML da un URL. Una query regolare che utilizza questa tabella ha il seguente aspetto:
seleziona * da html dove url = "http://finance.yahoo.com/q?s=yhoo" e xpath = "// div [@ id =" yfi_headlines "] / div [2] / ul / li / a "
Questa query consiste di due parametri: "url" e "xpath". L'URL è auto-esplicativo. L'XPath consiste in una stringa XPath che indica a YQL quale parte dell'HTML deve essere restituita. Prova questa query qui.
Ulteriori parametri che è possibile utilizzare includono del browser
(Booleano), charset
(stringa), e compat
(stringa). Non ho dovuto utilizzare questi parametri, ma fare riferimento alla documentazione se si hanno esigenze specifiche.
Sfortunatamente, XPath non è un modo molto diffuso per attraversare la struttura ad albero HTML. Può essere complicato leggere e scrivere per i principianti.
Diamo un'occhiata alla prossima tabella, che fa la stessa cosa ma ti permette invece di usare CSS
Il data.html.cssselect table è il mio modo preferito di rimuovere HTML da una pagina. Funziona allo stesso modo del html tabella ma ti consente di usare CSS anziché XPath. In pratica, questa tabella converte il CSS in XPath sotto il cofano e poi chiama il html tavolo, quindi è un po 'più lento. La differenza dovrebbe essere trascurabile per i bisogni di raschiatura.
Una query regolare che utilizza questa tabella è simile a:
seleziona * da data.html.cssselect dove url = "www.yahoo.com" e css = "# news a"
Come puoi vedere, è molto più pulito. Ti consiglio di provare questo metodo prima quando stai cercando di raschiare l'HTML usando YQL. Prova questa query qui.
Il htmlstring tabella è utile per i casi in cui si sta tentando di raschiare una grossa fetta di testo formattato da una pagina web.
L'utilizzo di questa tabella consente di recuperare l'intero contenuto HTML di quella pagina in una singola stringa, anziché come JSON suddiviso in base alla struttura DOM.
Ad esempio, una normale risposta JSON che cancella un il tag assomiglia a questo:
"risultati": "a": "href": "...", "target": "_blank", "content": "L'amministratore delegato di Apple Cook si arrampica su un nuovo palco"
Vedi come gli attributi sono definiti come proprietà? Invece, la risposta dal htmlstring tabella sarebbe simile a questo:
"risultati": "risultato": "L'amministratore delegato Apple si arrampica su un nuovo stadio
Quindi, perché dovresti usare questo? Bene, dalla mia esperienza, questo è di grande utilità quando stai cercando di racimolare una grande quantità di testo formattato. Ad esempio, considera il seguente frammento:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Proin nec diam magna. Sed non lorem a nisi porttitor pharetra et non arcu.
Usando il htmlstring tabella, è possibile ottenere questo codice HTML come stringa e utilizzare espressioni regolari per rimuovere i tag HTML, lasciando solo il testo. Questo è un compito più semplice di iterare con JSON che è stato suddiviso in proprietà e oggetti figlio basati sulla struttura DOM della pagina.
Ora che conosciamo un po 'di alcuni dei tavoli a nostra disposizione in YQL, implementiamo un web raschietto usando YQL e NodeJS. Fortunatamente, questo è davvero semplice, grazie a node-YQL modulo di Derek Gathright.
Possiamo installare il modulo usando npm
:
npm install yql
Il modulo è estremamente semplice, costituito da un solo metodo: il YQL.exec ()
metodo. È definito come il seguente:
funzione exec (stringa query [, funzione callback] [, oggetto params] [, oggetto httpOptions])
Possiamo usarlo richiedendolo e chiamando YQL.exec ()
. Ad esempio, supponiamo di voler analizzare i titoli di tutti i post nella pagina principale di Nettuts:
var YQL = require ("yql"); nuovo YQL.exec ('seleziona * da data.html.cssselect dove url = "http://net.tutsplus.com/" e css = ". post_title a" ", funzione (risposta) // la risposta è composta da JSON che puoi analizzare);
La cosa grandiosa di YQL è la sua capacità di testare le tue domande e determinare quale JSON stai recuperando in tempo reale. Vai alla console per provare questa query, oppure fai clic qui per vedere il JSON non elaborato.
Il params
e httpOptions
gli oggetti sono opzionali. I parametri possono contenere proprietà come ENV
(se si sta utilizzando un ambiente specifico per le tabelle) e formato
(xml o json). Tutte le proprietà passate params
sono codificati con URI e aggiunti alla stringa di query. Il httpOptions
l'oggetto è passato nell'intestazione della richiesta. Qui, è possibile specificare se si desidera abilitare SSL, ad esempio.
Il file JavaScript, chiamato yqlServer.js
, contiene il codice minimo richiesto per raschiare usando YQL. Puoi eseguirlo inviando il seguente comando nel tuo terminale:
nodo yqlServer.js
YQL è la mia scelta preferita per raschiare il contenuto da pagine web statiche, perché è facile da leggere e facile da usare. Tuttavia, YQL fallirà se la pagina web in questione ha un robots.txt
file che nega una risposta ad esso. In questo caso, puoi guardare alcune delle utility menzionate qui sotto, o usare PhantomJS, che tratteremo nella sezione seguente.
Node.io è un'utilità di nodo utile che è specificamente progettata per lo scraping dei dati. È possibile creare lavori che prendono input, elaborarli e restituire un output. Node.io è ben guardato su Github e contiene alcuni utili esempi per iniziare.
JSDOM è un progetto molto popolare che implementa il DOM W3C in JavaScript. Quando viene fornito HTML, può costruire un DOM con cui è possibile interagire. Guarda la documentazione per vedere come puoi usare JSDOM e qualsiasi libreria JS (come jQuery) insieme per raschiare dati da pagine web.
Finora, abbiamo esaminato alcuni strumenti che possono aiutarci a raschiare le pagine Web con contenuti statici. Con YQL, è relativamente facile. Sfortunatamente, spesso vengono presentate pagine con contenuti caricati dinamicamente con JavaScript. In questi casi, la pagina è spesso vuota inizialmente, quindi il contenuto viene aggiunto in seguito. Come possiamo affrontare questo problema?
Permettetemi di fornire un esempio di ciò che intendo; Ho caricato un semplice file HTML sul mio sito web, che aggiunge alcuni contenuti, tramite JavaScript, due secondi dopo document.ready ()
la funzione è chiamata. Puoi controllare la pagina qui. Ecco come appare la fonte:
Prova la pagina con il contenuto aggiunto dopo il caricamento della pagina Il contenuto di questa pagina viene aggiunto al DOM dopo che la pagina è stata caricata.
Ora proviamo a raschiare il testo all'interno del Noterai che YQL ritorna Diamo un'occhiata a come possiamo aggirare questo problema! PhantomJS può caricare pagine Web e imitare un browser basato su Webkit senza la GUI. Il mio metodo preferito per raschiare informazioni da questi siti è usare PhantomJS. PhantomJS si descrive come un "Webkit senza testa con un'API JavaScript" In termini semplicistici, ciò significa che PhantomJS può caricare pagine Web e imitare un browser basato su Webkit senza la GUI. Come sviluppatore, possiamo richiamare metodi specifici che PhantomJS fornisce a Esegui codice sulla pagina Poiché si comporta come un browser, gli script sulla pagina web funzionano come in un normale browser. Per ottenere i dati dalla nostra pagina, useremo PhantomJS-Node, un piccolo grande progetto open source che collega PhantomJS con NodeJS. Sotto il cofano, questo modulo esegue PhantomJS come processo figlio. Prima di poter installare il modulo NPM PhantomJS-Node, è necessario installare PhantomJS. Installare e costruire PhantomJS può essere un po 'complicato, però. Per prima cosa, vai su PhantomJS.org e scarica la versione appropriata per il tuo sistema operativo. Nel mio caso, era Mac OSX. Dopo averlo scaricato, decomprimilo in un posto come Sostituire Per gli utenti Windows, controlla qui il breve tutorial. Saprai di essere pronto quando apri il tuo terminale e scrivi Se sei a disagio nel modificare il tuo La configurazione di PhantomJS-Node è molto più semplice. Se hai installato NodeJS, puoi installare tramite npm: Se non hai modificato il tuo Cambia il percorso per: Una volta fatto, puoi testarlo eseguendo questo codice: L'esecuzione di questo sulla riga di comando dovrebbe visualizzare quanto segue: Se hai capito, sei pronto e pronto. In caso contrario, pubblica un commento e cercherò di aiutarti! Per facilitarti, ho incluso un file JS, chiamato Darò una panoramica di come funziona qui. Per prima cosa, richiediamo PhantomJS: Successivamente, implementiamo alcuni metodi dall'API. Vale a dire, creiamo un'istanza di pagina e poi chiamiamo il Una volta aperta la pagina, possiamo inserire alcuni JavaScript nella pagina. Iniettiamo jQuery tramite il jQuery è ora caricato, ma non sappiamo se il contenuto dinamico della pagina è già stato caricato. Per tener conto di ciò, di solito inserisco il mio codice di scraping all'interno di a Mettendo tutto insieme, il nostro Questa implementazione è un po 'rozza e disorganizzata, ma rende il punto. Usando PhantomJS, siamo in grado di grattare una pagina con contenuti dinamici! La tua console dovrebbe produrre quanto segue: In questo tutorial, abbiamo esaminato due diversi modi per eseguire lo scraping web. Se raschiamo da una pagina web statica, possiamo approfittare di YQL, che è facile da configurare e utilizzare. D'altra parte, per i siti dinamici, possiamo sfruttare PhantomJS. È un po 'più difficile da configurare, ma offre più funzionalità. Ricorda: puoi usare PhantomJS anche per siti statici! Se avete domande su questo argomento, sentitevi liberi di chiedere sotto e farò del mio meglio per aiutarvi.var YQL = require ("yql"); nuovo YQL.exec ('select * from data.html.cssselect where url = "http://tilomitra.com/repository/screenscrape/ajax.html" e css = "# content" ", function (response) // Ciò restituirà undefined! Lo scraping non ha avuto esito positivo! Console.log (response.results););
non definito
perché, quando la pagina è caricata, il Inserisci PhantomJS
Installazione di PhantomJS
/ Applicazioni /
. Successivamente, vuoi aggiungerlo al tuo SENTIERO
:sudo ln -s /Applications/phantomjs-1.5.0/bin/phantomjs / usr / local / bin /
1.5.0
con la versione scaricata di PhantomJS. Tieni presente che non tutti i sistemi avranno / Usr / local / bin /
. Alcuni sistemi avranno: / Usr / bin /
, /bidone/
, o usr / X11 / bin
anziché.phantomjs
, e non ottieni errori.SENTIERO
, prendi nota di dove hai decompresso PhantomJS e mostrerò un altro modo di configurarlo nella prossima sezione, anche se ti consiglio di modificare il tuo SENTIERO
.Installazione del nodo PhantomJS
npm installa phantom
SENTIERO
nel passaggio precedente durante l'installazione di PhantomJS, è possibile accedere a fantasma/
directory tirata giù da npm e modificare questa riga in phantom.js
. ps = child.spawn ('phantomjs', args.concat ([__ dirname + '/shim.js', port]));
ps = child.spawn ('/ percorso / a / phantomjs-1.5.0 / bin / phantomjs', args.concat ([__ dirname + '/shim.js', port]));
var phantom = require ('phantom'); phantom.create (function (ph) return ph.createPage (function (page) return page.open ("http://www.google.com", function (status) console.log ("ha aperto google?" , status); return page.evaluate ((function () return document.title;), function (result) console.log ('Il titolo della pagina è' + result); return ph.exit ();); );););
ha aperto google? il titolo della pagina di successo è Google
Utilizzando il nodo PhantomJS
phantomServer.js
nel download che utilizza alcune API di PhantomJS per caricare una pagina Web. Aspetta 5 secondi prima di eseguire JavaScript che graffia la pagina. Puoi eseguirlo navigando nella directory ed eseguendo il seguente comando nel tuo terminale: nodo phantomServer.js
var phantom = require ('phantom');
Aperto()
metodo: phantom.create (function (ph) return ph.createPage (function (page) // Da qui in avanti, possiamo usare i metodi API di PhantomJS 'return page.open ("http://tilomitra.com/repository/screenscrape /ajax.html ", function (status) // La pagina è ora aperta console.log (" sito aperto? ", stato););););
page.injectJs ()
metodo: phantom.create (function (ph) return ph.createPage (function (page) return page.open ("http://tilomitra.com/repository/screenscrape/ajax.html", function (status) console.log ("sito aperto?", stato); page.injectJs ('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', function () // jQuery Loaded // Possiamo usare cose come $ ("body"). Html () qui.););););
setTimeout ()
funzione che viene eseguita dopo un certo intervallo di tempo. Se si desidera una soluzione più dinamica, l'API PhantomJS consente di ascoltare ed emulare determinati eventi. Andiamo con il caso semplice: setTimeout (function () return page.evaluate (function () // Ottieni quello che vuoi dalla pagina usando jQuery. // Un buon metodo è popolare un oggetto con tutti i comandi jQuery di cui hai bisogno e quindi restituire l'oggetto . var h2Arr = [], // array che contiene tutti gli elementi html per h2 pArr = []; // array che contiene tutto l'html per gli elementi p // Compila i due array $ ('h2'). each (function () h2Arr.push ($ (this) .html ());); $ ('p'). each (function () pArr.push ($ (this) .html ());); // Restituisce questo ritorno di dati h2: h2Arr, p: pArr, function (result) console.log (risultato); // Disconnettiti i dati. Ph.exit (););, 5000);
phantomServer.js
il file assomiglia a questo: var phantom = require ('phantom'); phantom.create (function (ph) return ph.createPage (function (page) return page.open ("http://tilomitra.com/repository/screenscrape/ajax.html", function (status) console.log ("sito aperto?", stato); page.injectJs ('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', function () // jQuery Loaded // Aspetta un po 'per caricare il contenuto AJAX sulla pagina, qui aspettiamo 5 secondi setTimeout (function () return page.evaluate (function () // Ottieni quello che vuoi dalla pagina usando jQuery Un buon modo è popolare un oggetto con tutti i comandi jQuery di cui hai bisogno e quindi restituire l'oggetto var h2Arr = [], pArr = []; $ ('h2'). Each (function () h2Arr.push ($ (this) .html ());); $ ('p'). each (function () pArr.push ($ (this) .html ());); return h2: h2Arr, p: pArr;, function (result) console.log (risultato); ph.exit (););, 5000);););););
→ node phantomServer.js ha aperto il sito? successo h2: ['Articolo 1', 'Articolo 2', 'Articolo 3'], p: ['Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 'Ut sed nulla turpis, in faucibus ante. Vivamus ut malesuada est. Curabitur vel enim eget purus pharetra temp id in tellus. ',' Curabitur euismod hendrerit quam ut euismod. Ut leo sem, viverra nec gravida nec, tristique nec arcu. " ]
Conclusione