Ga naar hoofdinhoud

Webscraping met R en PhantomJS

Korte tutorial over het scrapen van JavaScript-gegenereerde data met R met behulp van PhantomJS.
· 5 min lezen

Verken met AI

Als je webscraping wilt doen, maak je normaal gesproken gebruik van Hadley Wickham’s rvest-pakket. Dit pakket biedt een gebruiksklare, eenvoudige oplossing om de html-code op te halen die een webpagina genereert. Maar wanneer de website of webpagina JavaScript gebruikt om de data te tonen waarin je geïnteresseerd bent, mist het rvest-pakket de benodigde functionaliteit. Een oplossing is het gebruik van PhantomJS.

(Wil je meer oefenen met het importeren van data in R? Probeer deze tutorial.)

Laad de benodigde pakketten

Als eerste stap moet je alle pakketten die we voor deze analyse nodig hebben in de workspace laden (als je deze pakketten nog niet lokaal hebt geïnstalleerd, gebruik dan install.packages() om ze beschikbaar te maken):

library(rvest)
library(stringr)
library(plyr)
library(dplyr)
library(ggvis)
library(knitr)
options(digits = 4)

JavaScript-gegenereerde data scrapen met R

De volgende stap is het verzamelen van de TechStars-data met PhantomJS. Bekijk het volgende basis-.js-bestand:

// scrape_techstars.js

var webPage = require('webpage');
var page = webPage.create();

var fs = require('fs');
var path = 'techstars.html'

page.open('http://www.techstars.com/companies/stats/', function (status) {
  var content = page.content;
  fs.write(path,content,'w')
  phantom.exit();
});

Het script rendert in feite de HTML-pagina nadat de onderliggende JavaScript-code zijn werk heeft gedaan, zodat je de HTML-pagina kunt ophalen met alle tabellen erin. Om voor de rest van deze analyse in R te blijven, raden we aan de functie system() te gebruiken om PhantomJS aan te roepen (je moet PhantomJS downloaden en installeren en in je werkmap zetten):

# Laat phantomJS techstars scrapen, uitvoer wordt weggeschreven naar techstars.html
system("./phantomjs scrape_techstars.js")

Na deze kleine omweg heb je eindelijk een HTML-bestand, techstars.html, op ons lokale systeem, dat gescrapet kan worden met rvest. Inspectie van de Techstars-webpagina laat zien dat de tabellen waarin we geïnteresseerd zijn zich bevinden in divs met de css-klasse batch:

batches <- html("techstars.html") %>%
  html_nodes(".batch")

class(batches)
[1] "XMLNodeSet"

Je hebt nu een lijst met XMLNodeSet-objecten: elk object bevat de data voor één TechStars-batch. Daarin vinden we informatie over de locatie van de batch, het jaar, het seizoen, maar ook over de bedrijven, hun huidige hoofdkantoor, hun huidige status en het totale bedrag aan opgehaald kapitaal. We gaan hieronder niet in op de details van de dataverzamelings- en opschoonstappen; je kunt de code zelf uitvoeren en bekijken wat deze doet. Je zult zien dat er wat maatwerkopschoning plaatsvindt om ervoor te zorgen dat elk beetje informatie netjes is opgemaakt:

batch_titles <- batches %>%
  html_nodes(".batch_class") %>%
  html_text()

batch_season <- str_extract(batch_titles, "(Fall|Spring|Winter|Summer)")
batch_year <- str_extract(batch_titles, "([[:digit:]]{4})")
# locatie-informatie is alles in de batchtitel dat geen jaar- of seizoensinfo is
batch_location <- sub("\\s+$", "",
                      sub("([[:digit:]]{4})", "",
                          sub("(Fall|Spring|Winter|Summer)","",batch_titles)))

# maak een data frame met batchinfo
batch_info <- data.frame(location = batch_location,
                         year = batch_year,
                         season = batch_season)

breakdown <- lapply(batches, function(x) {
  company_info <- x %>% html_nodes(".parent")
  companies_single_batch <- lapply(company_info, function(y){
    as.list(gsub("\\[\\+\\]\\[\\-\\]\\s", "", y %>%
       html_nodes("td") %>%
       html_text()))
  })
  df <- data.frame(matrix(unlist(companies_single_batch),
                   nrow=length(companies_single_batch),
                   byrow=T,
                   dimnames = list(NULL, c("company","funding","status","hq"))))
  return(df)
})

# Voeg batchinfo toe aan breakdown
batch_info_extended <- batch_info[rep(seq_len(nrow(batch_info)),
                                  sapply(breakdown, nrow)),]
breakdown_merged <- rbind.fill(breakdown)

# Voeg alle informatie samen
techstars <- tbl_df(cbind(breakdown_merged, batch_info_extended)) %>%
  mutate(funding = as.numeric(gsub(",","",gsub("\\$","",funding))))

Met een combinatie van core R-, rvest-, plyr- en dplyr-functies hebben we nu het techstars-dataframe: een dataset van alle TechStars-bedrijven, met alle publiek beschikbare informatie die netjes is opgemaakt:

techstars
## Source: local data frame [535 x 7]
##
##          company funding   status                hq location year season
## 1    Accountable  110000   Active    Fort Worth, TX   Austin 2013   Fall
## 2          Atlas 1180000   Active        Austin, TX   Austin 2013   Fall
## 3        Embrace  110000   Failed        Austin, TX   Austin 2013   Fall
## 4  Filament Labs 1490000   Active        Austin, TX   Austin 2013   Fall
## 5        Fosbury  300000   Active        Austin, TX   Austin 2013   Fall
## 6          Gone!  840000   Active San Francisco, CA   Austin 2013   Fall
## 7     MarketVibe  110000 Acquired        Austin, TX   Austin 2013   Fall
## 8           Plum 1630000   Active        Austin, TX   Austin 2013   Fall
## 9  ProtoExchange  110000   Active        Austin, TX   Austin 2013   Fall
## 10       Testlio 1020000   Active        Austin, TX   Austin 2013   Fall
## ..           ...     ...      ...               ...      ...  ...    ...
names(techstars)
## [1] "company"  "funding"  "status"   "hq"       "location" "year"
## [7] "season"

Conclusie

Met de bovenstaande combinatie van tools en code is het gelukt om data te scrapen van een website die een JavaScript-script gebruikt om zijn data te genereren. Zoals je ziet is dit een zeer gestructureerd proces dat eenvoudig uit te voeren is zodra de initiële code beschikbaar is. Wil je R leren en ben je op zoek naar data science- en R-tutorials en -cursussen? Kijk dan eens bij DataCamp

Onderwerpen

Gerelateerd