Skjermskraping med Node.js

Du har kanskje brukt NodeJS som en webserver, men visste du at du også kan bruke den til nettskraping? I denne veiledningen vurderer vi hvordan du skal skrape statiske websider - og de irriterende med dynamisk innhold - ved hjelp av NodeJS og noen nyttige NPM-moduler.



Litt om nettskraping

Nettskraping har alltid hatt en negativ connotation i verden av webutvikling - og med god grunn. I moderne utvikling er APIer til stede for de fleste populære tjenester, og de bør brukes til å hente data i stedet for å skrape. Det iboende problemet med skraping er at det er avhengig av den visuelle strukturen til siden som blir skrapt. Når denne HTML endres - uansett hvor liten endringen kan være - kan den helt ødelegge koden din.

Til tross for disse feilene, er det viktig å lære litt om nettskraping og noen av verktøyene som er tilgjengelige for å hjelpe med denne oppgaven. Når et nettsted ikke avslører en API eller en syndikatmateriell (RSS / Atom, etc), er det eneste alternativet vi har igjen for å få det innholdet ... å skrape.

Merk: Hvis du ikke kan få informasjonen du trenger gjennom en API eller en feed, er det et godt tegn på at eieren ikke vil at denne informasjonen skal være tilgjengelig. Det er imidlertid unntak.


Hvorfor bruke NodeJS?

Skraper kan skrives på noe språk, egentlig. Grunnen til at jeg liker å bruke Node er på grunn av sin asynkrone natur, noe som betyr at koden min ikke er blokkert når som helst i prosessen. Jeg er ganske kjent med JavaScript, så det er en ekstra bonus. Endelig er det noen nye moduler som er skrevet for NodeJS som gjør det enkelt å skrape nettsteder på en pålitelig måte (vel, så pålitelig som skraping kan få!). La oss komme i gang!


Enkel skraping med YQL

La oss starte med det enkle brukstilfellet: statiske websider. Dette er de vanlige web-sidene dine. For disse, Yahoo! Query Language (YQL) bør gjøre jobben veldig bra. For de som ikke er kjent med YQL, er det en SQL-lignende syntaks som kan brukes til å jobbe med forskjellige APIer på en konsekvent måte.

YQL har noen flotte tabeller for å hjelpe utviklere til å få HTML av en side. De jeg vil fremheve er:

  • html
  • data.html.cssselect
  • htmlstring

La oss gå gjennom hver av dem, og se gjennom hvordan de implementeres i NodeJS.

html bord

De html Tabellen er den mest grunnleggende måten å skrape HTML fra en URL. En vanlig spørring med denne tabellen ser slik ut:

velg * fra html hvor url = "http://finance.yahoo.com/q?s=yhoo" og xpath = "// div [@ id =" yfi_headlines "] / div [2] / ul / li / a "

Denne spørringen består av to parametere: "url" og "xpath". Nettadressen er selvforklarende. XPath består av en XPath-streng som forteller YQL hvilken del av HTML-en skal returneres. Prøv denne spørringen her.

Ytterligere parametere som du kan bruke inkluderer nettleser (Boolean), charset (streng), og compat (String). Jeg har ikke hatt å bruke disse parameterne, men referer til dokumentasjonen hvis du har spesifikke behov.

Ikke komfortabel med XPath?

Dessverre er XPath ikke en veldig populær måte å krysse HTML-strukturen på. Det kan være komplisert å lese og skrive for nybegynnere.

La oss se på neste tabell, som gjør det samme, men lar deg bruke CSS i stedet

data.html.cssselect bord

De data.html.cssselect Tabellen er min foretrukne måte å skrape HTML på en side. Det fungerer på samme måte som html bord, men lar deg CSS istedenfor XPath. I praksis konverterer dette bordet CSS til XPath under hetten og kaller deretter html bord, så det er litt tregere. Forskjellen skal være ubetydelig for skrapebehov.

En vanlig spørring med denne tabellen ser ut som:

velg * fra data.html.cssselect hvor url = "www.yahoo.com" og css = "# news a"

Som du kan se, er det mye renere. Jeg anbefaler at du prøver denne metoden først når du prøver å skrape HTML ved hjelp av YQL. Prøv denne spørringen her.

htmlstring bord

De htmlstring Tabellen er nyttig i tilfeller der du prøver å skrape en stor del av formatert tekst fra en nettside.

Ved hjelp av dette tabellen kan du hente hele HTML-innholdet på den siden i en enkelt streng, i stedet for som JSON som er delt basert på DOM-strukturen.

For eksempel, en vanlig JSON-respons som skraper en tag ser slik ut:

"resultater": "" "" href ":" ... "," mål ":" _blank "," innhold ":" Apple Chief Executive Cook å klatre på et nytt trinn "

Se hvordan attributter defineres som egenskaper? I stedet er svaret fra htmlstring bordet ville se slik ut:

"resultater": "resultat": "Apple Chief Executive Cook å klatre på et nytt stadium

Så hvorfor ville du bruke dette? Vel, fra min erfaring, kommer dette til stor nytte når du prøver å skrape en stor mengde formatert tekst. For eksempel, vurder følgende utdrag:

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Proin nek diam magna. Sett ikke lorem en nisi porttitor pharetra et non arcu.

Ved å bruke htmlstring tabell, kan du få denne HTML som en streng, og bruk regex for å fjerne HTML-kodene, som etterlater deg med bare teksten. Dette er en enklere oppgave enn iterating gjennom JSON som har blitt delt inn i egenskaper og barnobjekter basert på DOM-strukturen på siden.


Bruke YQL med NodeJS

Nå som vi vet litt om noen av tabellene som er tilgjengelige for oss i YQL, la oss implementere en webskraper ved hjelp av YQL og NodeJS. Heldigvis er dette veldig enkelt, takket være node-YQL modul av Derek Gathright.

Vi kan installere modulen ved hjelp av NPM:

npm installere yql

Modulen er ekstremt enkel, bestående av bare én metode: YQL.exec () metode. Det er definert som følgende:

funksjon exec (streng spørring [, funksjon tilbakeringing] [, objekt params] [, objekt httpOptions])

Vi kan bruke det ved å kreve det og ringe YQL.exec (). For eksempel, la oss si at vi ønsker å skrape overskriftene fra alle innleggene på Nettuts hovedside:

var YQL = krever ("yql"); ny YQL.exec ('velg * fra data.html.cssselect hvor url = "http://net.tutsplus.com/" og css = ". post_title a"', funksjon (svar) // svar består av JSON at du kan analysere);

Den gode tingen om YQL er dens evne til å teste dine spørsmål og avgjøre hva JSON du kommer tilbake i sanntid. Gå til konsollen for å prøve denne spørringen, eller klikk her for å se den rå JSON.

De params og httpOptions objekter er valgfrie. Parametre kan inneholde egenskaper som env (om du bruker et bestemt miljø for tabellene) og format (xml eller json). Alle eiendommer gikk inn i params er URI-kodet og vedlagt søkestrengen. De httpOptions objektet sendes inn i overskriften til forespørselen. Her kan du angi om du vil aktivere SSL, for eksempel.

JavaScript-filen, navngitt yqlServer.js, inneholder den minimale koden som kreves for å skrape ved hjelp av YQL. Du kan kjøre den ved å utstede følgende kommando i terminalen din:

node yqlServer.js

Unntak og andre bemerkelsesverdige verktøy

YQL er mitt foretrukne valg for å skrape innhold fra statiske websider, fordi det er lett å lese og enkelt å bruke. Imidlertid vil YQL mislykkes hvis den aktuelle nettsiden har en robots.txt fil som nekter et svar på det. I dette tilfellet kan du se på noen av verktøyene nevnt nedenfor, eller bruk PhantomJS, som vi vil dekke i følgende seksjon.

Node.io er et nyttig Node-verktøy som er spesielt utviklet for dataskraping. Du kan opprette jobber som tar innspill, behandler det og returnerer noe utdata. Node.io er godt overvåket på Github, og har noen nyttige eksempler for å komme i gang.

JSDOM er et veldig populært prosjekt som implementerer W3C DOM i JavaScript. Når det leveres HTML, kan det konstruere et DOM som du kan samhandle med. Se dokumentasjonen for å se hvordan du kan bruke JSDOM og et hvilket som helst JS-bibliotek (for eksempel jQuery) til å skrape data fra nettsider.


Skrapingssider med dynamisk innhold

Så langt har vi sett på noen verktøy som kan hjelpe oss med å skrape nettsider med statisk innhold. Med YQL er det relativt enkelt. Dessverre presenteres vi ofte med sider som har innhold som lastes dynamisk med JavaScript. I disse tilfellene er siden ofte tom i utgangspunktet, og deretter legges innholdet til etterpå. Hvordan kan vi håndtere dette problemet?

Et eksempel

La meg gi et eksempel på hva jeg mener; Jeg har lastet opp en enkel HTML-fil til min egen nettside, som legger til noe innhold, via JavaScript, to sekunder etter document.ready () funksjon kalles. Du kan sjekke ut siden her. Slik ser kilden ut:

   Test side med innhold vedlagt etter sidebelastning   Innhold på denne siden legges til DOM etter at siden er lastet inn. 

Nå, la oss prøve å skrape teksten inne i

bruker YQL.

var YQL = krever ("yql"); ny YQL.exec ('velg * fra data.html.cssselect hvor url = "http://tilomitra.com/repository/screenscrape/ajax.html" og css = "# content"', funksjon (svar) // Dette vil returnere udefinert! Skrapingen var mislykket! Console.log (response.results););

Du vil legge merke til at YQL returnerer udefinert fordi når siden er lastet,

er tom. Innholdet har ikke blitt lagt til ennå. Du kan prøve spørringen ut selv her.

La oss se på hvordan vi kan komme seg rundt dette problemet!

Skriv inn PhantomJS

PhantomJS kan laste inn nettsider og etterligne en webkitbasert nettleser uten GUI.

Min foretrukne metode for å skrape informasjon fra disse nettstedene er å bruke PhantomJS. PhantomJS beskriver seg som en "headless Webkit med en JavaScript API. I forenklet sett betyr dette at PhantomJS kan laste inn nettsider og etterligne en webkitbasert nettleser uten GUI. Som utvikler kan vi ringe på spesifikke metoder som PhantomJS gir til Kjør kode på siden. Siden den oppfører seg som en nettleser, går skript på nettsiden som de ville i en vanlig nettleser.

For å få data fra vår side, skal vi bruke PhantomJS-Node, et flott lite open-source-prosjekt som overfører PhantomJS med NodeJS. Under hetten løper denne modulen PhantomJS som en barneprosess.

Installere PhantomJS

Før du kan installere PhantomJS-Node NPM-modulen, må du installere PhantomJS. Installasjon og bygging av PhantomJS kan være litt vanskelig, skjønt.

Først går du over til PhantomJS.org og laster ned den riktige versjonen for operativsystemet. I mitt tilfelle var det Mac OSX.

Etter å ha lastet ned, pakker du den til et sted som /Applikasjoner/. Deretter vil du legge den til i din STI:

sudo ln -s /Applications/phantomjs-1.5.0/bin/phantomjs / usr / local / bin /

Erstatte 1.5.0 med din nedlastede versjon av PhantomJS. Vær oppmerksom på at ikke alle systemer vil ha / Usr / local / bin /. Noen systemer vil ha: / Usr / bin /, / Bin /, eller usr / X11 / bin i stedet.

For Windows-brukere, sjekk kort opplæringen her. Du vet at du er opprettet når du åpner Terminal og skriver phantomjs, og du får ingen feil.

Hvis du er ubehagelig, redigerer du STI, Legg merke til hvor du unzipped PhantomJS, og jeg vil vise en annen måte å sette opp i neste avsnitt, selv om jeg anbefaler at du redigerer din STI.

Installere PhantomJS-Node

Det er mye enklere å sette opp PhantomJS-Node. Forutsatt at du har NodeJS installert, kan du installere via npm:

npm installere fantom

Hvis du ikke redigerte din STI i forrige trinn når du installerer PhantomJS, kan du gå inn i fantom / katalog trukket ned av npm og redigere denne linjen inn phantom.js.

ps = child.spawn ('phantomjs', args.concat ([__ dirname + '/shim.js', port]));

Endre banen til:

ps = child.spawn ('/ path / to / phantomjs-1.5.0 / bin / phantomjs', args.concat ([__ dirname + '/shim.js', port]));

Når det er gjort, kan du teste det ved å kjøre denne koden:

 var fantom = krever ("fantom"); phantom.create (funksjon (ph) return ph.createPage (funksjon (side) return page.open ("http://www.google.com", funksjon (status) console.log ("opened google?" , status), tilbake side.evaluere ((funksjon () return document.title;), funksjon (resultat) console.log ('Sidetittel er' + resultat); returner ph.exit ();); );;);

Kjører dette på kommandolinjen bør hente opp følgende:

åpnet google? suksess side tittelen er google

Hvis du har dette, er du klar og klar til å gå. Hvis ikke, legg inn en kommentar, og jeg vil prøve å hjelpe deg!

Bruke PhantomJS-Node

For å gjøre det enklere for deg, har jeg tatt med en JS-fil, kalt phantomServer.js i nedlastingen som bruker noen av PhantomJS 'API for å laste inn en nettside. Den venter i 5 sekunder før du utfører JavaScript som skraver siden. Du kan kjøre den ved å navigere til katalogen og utstede følgende kommando i terminalen din:

 node phantomServer.js

Jeg gir en oversikt over hvordan det fungerer her. For det første krever vi PhantomJS:

 var fantom = krever ("fantom");

Deretter implementerer vi noen metoder fra API. Nemlig oppretter vi en sidetilfelle og deretter kaller åpen() metode:

 phantom.create (funksjon (ph) return ph.createPage (funksjon (side) // Herfra kan vi bruke PhantomJS 'API-metoder returnere side.open ("http://tilomitra.com/repository/screenscrape /ajax.html ", funksjon (status) // Siden er nå åpen console.log (" åpnet nettsted? ", status);););;

Når siden er åpen, kan vi injisere noe JavaScript inn på siden. La oss injisere jQuery via page.injectJs () metode:

 phantom.create (funksjon (ph) return ph.createPage (funksjon (side) return page.open ("http://tilomitra.com/repository/screenscrape/ajax.html", funksjon (status) console.log ("åpnet nettsted?", status); page.injectJs ('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', funksjon () // jQuery Lastet // Vi kan bruke ting som $ ("body"). Html () her.););););

jQuery er nå lastet, men vi vet ikke om det dynamiske innholdet på siden har lastet inn ennå. For å regne for dette legger jeg vanligvis min skrapekode inne i en setTimeout () funksjon som utføres etter et bestemt tidsintervall. Hvis du vil ha en mer dynamisk løsning, lar PhantomJS API deg høre og etterligne bestemte hendelser. La oss gå med det enkle tilfellet:

 setTimeout (funksjon () return page.evaluate (function () // Få det du vil ha fra siden ved hjelp av jQuery. // En god måte er å fylle et objekt med alle jQuery-kommandoene du trenger, og returner objektet . var h2Arr = [], // array som inneholder all html for h2-elementer pArr = []; // array som inneholder all HTML for p-elementer // Populere de to arrays $ ('h2'). hver (funksjon h2Arr.push ($ (dette) .html ());); $ ('p'). hver (funksjon () pArr.push ($ (dette) .html ());); // Return denne dataregistreringen h2: h2Arr, p: pArr, funksjon (resultat) console.log (result); // Logg ut dataene ph.exit (););, 5000);

Setter alt sammen, vår phantomServer.js filen ser slik ut:

 var fantom = krever ("fantom"); phantom.create (funksjon (ph) return ph.createPage (funksjon (side) return page.open ("http://tilomitra.com/repository/screenscrape/ajax.html", funksjon (status) console.log ("åpnet nettsted?", status); page.injectJs ('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', funksjon () // jQuery Lastet / / Vent litt for AJAX-innhold å laste inn på siden. Her venter vi 5 sekunder. SetTimeout (funksjon () return page.evaluate (function () // Få det du vil ha fra siden ved hjelp av jQuery En god måte er å fylle et objekt med alle jQuery-kommandoene du trenger og returnere objektet. Var h2Arr = [], pArr = []; $ ('h2'). Hver (funksjon () h2Arr.push ($ (dette) .html ());); $ ('p'). hver (funksjon () pArr.push ($ (dette) .html ());); return h2: h2Arr, p: pArr;, funksjon (resultat) console.log (resultat); ph.exit ();;, 5000);););;;);

Denne gjennomføringen er litt rå og uorganisert, men det gjør poenget. Ved hjelp av PhantomJS kan vi skrape en side som har dynamisk innhold! Konsollen din skal sende ut følgende:

 → node phantomServer.js åpnet nettsted? suksess h2: ['Artikkel 1', 'Artikkel 2', 'Artikkel 3'], p: ['Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 'Ut sed nulla turpis, in faucibus ante. Vivamus ut malesuada est. Begynn deg med et eget navn, og ta en titt på det. ',' Curabitur euismod hendrerit quam ut euismod. Ut leo sem, viverra nek gravida nec, tristique nec arcu. ' ]

Konklusjon

I denne opplæringen har vi gjennomgått to forskjellige måter for å utføre nettskraping. Hvis vi skraper fra en statisk nettside, kan vi dra nytte av YQL, som er lett å sette opp og bruke. På den annen side, for dynamiske områder, kan vi utnytte PhantomJS. Det er litt vanskeligere å sette opp, men gir flere muligheter. Husk: Du kan også bruke PhantomJS for statiske nettsteder!

Hvis du har noen spørsmål om dette emnet, kan du spørre nedenfor og jeg vil gjøre mitt beste for å hjelpe deg.