Web-skraping på klientsiden med JavaScript ved hjelp av jQuery og Regex

Da jeg bygde mitt første open source-prosjekt, codeBadges, trodde jeg det ville være enkelt å få brukerprofildata fra alle de viktigste nettstedene for kodelæring.

Jeg var kjent med API-samtaler og fikk forespørsler. Jeg trodde jeg bare kunne bruke jQuery til å hente dataene fra de forskjellige API-ene og bruke den.

var name = 'codemzy'; $.get('//api.github.com/users/' + name, function(response) { var followers = response.followers;});

Vel, det var enkelt. Men det viser seg at ikke alle nettsteder har en offentlig API som du bare kan hente dataene du vil ha fra.

Men bare fordi det ikke er noe offentlig API, betyr ikke det at du trenger å gi opp! Du kan bruke nettskraping til å hente dataene, med bare litt ekstra arbeid .

La oss se hvordan vi kan bruke nettskraping på klientsiden med JavaScript.

For et eksempel vil jeg hente brukerinformasjonen min fra min offentlige freeCodeCamp-profil. Men du kan bruke disse trinnene på en hvilken som helst offentlig HTML-side.

Det første trinnet i å skrape dataene er å hente helsiden til helsidesiden ved hjelp av en jQuery- .getforespørsel.

var name = "codemzy";$.get('//www.freecodecamp.com/' + name, function(response) { console.log(response);});

Fantastisk, hele kildekoden for siden bare logget på konsollen.

Merk: Hvis du får en feil på dette stadiet i tråd med No ‘Access-Control-Allow-Origin’ header is present on the requested resourceikke bekymre deg. Rull ned til Ikke la CORS stoppe deg- delen av dette innlegget.

Det var lett. Ved hjelp av JavaScript og jQuery ber koden ovenfor om en side fra www.freecodecamp.org, slik en nettleser ville gjort. Og freeCodeCamp svarer med siden. I stedet for en nettleser som kjører koden for å vise siden, får vi HTML-koden.

Og det er hva nettskraping er, ekstraherer data fra nettsteder.

Ok, svaret er ikke akkurat så pent som dataene vi får tilbake fra en API.

Men ... vi har dataene der inne et sted.

Når vi har kildekoden, er informasjonen vi trenger der inne, vi må bare hente dataene vi trenger!

Vi kan søke gjennom svaret for å finne elementene vi trenger.

La oss si at vi vil vite hvor mange utfordringer brukeren har gjennomført, fra brukerprofilresponsen vi fikk tilbake.

I skrivende stund er en fullført utfordring for en bobil organisert i tabeller på brukerprofilen. Så for å få det totale antallet utfordringer fullført, kan vi telle antall rader.

En måte er å pakke hele responsen i et jQuery-objekt, slik at vi kan bruke jQuery-metoder som .find()å få data.

// number of challenges completedvar challenges = $(response).find('tbody tr').length;

Dette fungerer bra - vi får riktig resultat. Men det er ikke en god måte å få det resultatet vi er ute etter. Å omsette svaret til et jQuery-objekt laster faktisk hele siden, inkludert alle de eksterne skriptene, skriftene og stilarkene fra den siden ... Uh oh!

Vi trenger noen få biter av data. Vi trenger virkelig ikke siden, og absolutt ikke alle eksterne ressurser som følger med den.

Vi kunne fjerne skriptkodene og kjøre resten av svaret gjennom jQuery. For å gjøre dette kan vi bruke Regex til å lete etter skriptmønstre i teksten og fjerne dem.

Eller enda bedre, hvorfor ikke bruke Regex til å finne det vi leter etter i utgangspunktet?

// number of challenges completedvar challenges = response.replace(/[\s|\S]*?/g).match(//g).length;

Og det fungerer! Ved å bruke Regex-koden ovenfor fjerner vi tabellhodelinjene (som ikke inneholdt noen utfordringer), og matcher deretter alle tabellrader for å telle antall fullførte utfordringer.

Det er enda enklere hvis dataene du ønsker bare er der i svaret i ren tekst. I skrivende stund var brukerpoengene i html-stil

[ 1498 ]

bare venter på å bli skrapet.

var points = response.match(/

\[ ([\d]*?) \]/)[1];

I det ovennevnte Regex-mønsteret matcher vi h1-elementet vi leter etter, inkludert det [ ]som omgir poengene, og grupperer et hvilket som helst tall inne med. ([\d]*?).Vi får en matrise tilbake, det første [0]elementet er hele kampen og det andre [1]er vår gruppekamp (våre poeng ).

Regex er nyttig for å matche alle slags mønstre i strenger, og det er flott å søke gjennom svaret vårt for å få de dataene vi trenger.

Du kan bruke den samme tretrinnsprosessen for å skrape profildata fra en rekke nettsteder:

  1. Bruk JavaScript på klientsiden
  2. Bruk jQuery til å skrape dataene
  3. Bruk Regex til å filtrere dataene for relevant informasjon

Inntil jeg traff et problem, CORS.

Ikke la CORS stoppe deg!

CORS eller Cross-Origin Resource Sharing, kan være et reelt problem med nettskraping på klientsiden.

Av sikkerhetsmessige årsaker begrenser nettlesere HTTP-forespørsler som er opprinnelig fra skript. Og fordi vi bruker Javascript på klientsiden på frontenden for nettskraping, kan CORS-feil oppstå.

Her er et eksempel på å prøve å skrape profildata fra CodeWars ...

var name = "codemzy";$.get('//www.codewars.com/users/' + name, function(response) { console.log(response);});

I skrivende stund gir kjøring av ovennevnte kode deg en CORS-relatert feil.

Hvis det ikke er noen Access-Control-Allow-Originoverskrift fra stedet du skraper, kan du støte på problemer.

Den dårlige nyheten er at du må kjøre slike forespørsler på serversiden for å komme deg rundt dette problemet.

Whaaaaaaat, dette skal være nettskraping på klientsiden ?!

Den gode nyheten er at takket være mange andre fantastiske utviklere som har fått de samme problemene, trenger du ikke å røre bakenden selv.

Fortsatt å holde oss innenfor frontend-skriptet vårt, kan vi bruke verktøy på tvers av domener som Any Origin, Whatever Origin, All Origins, crossorigin og sannsynligvis mye mer. Jeg har funnet ut at du ofte må teste noen av disse for å finne den som vil fungere på nettstedet du prøver å skrape.

Tilbake til vårt CodeWars-eksempel, kan vi sende forespørselen vår via et tverrdomeneverktøy for å omgå CORS-problemet.

var name = "codemzy";var url = "//anyorigin.com/go?url=" + encodeURIComponent("//www.codewars.com/users/") + name + "&callback=?";$.get(url, function(response) { console.log(response);});

Og akkurat som magi, har vi vårt svar.