Zum Hauptinhalt springen

Web Scraping mit R und PhantomJS

Kurzes Tutorial zum Scrapen von JavaScript-generierten Daten mit R mithilfe von PhantomJS.
· 5 Min. lesen

Entdecke KI

Wenn du Web Scraping betreiben willst, greifst du normalerweise zu Hadley Wickhams rvest-Paket. Dieses Paket bietet eine unkomplizierte Out-of-the-Box-Lösung, um den HTML-Code abzurufen, der eine Webseite erzeugt. Nutzt die Site oder Seite jedoch JavaScript, um die für dich relevanten Daten anzuzeigen, fehlen rvest die nötigen Fähigkeiten. Eine Lösung ist der Einsatz von PhantomJS.

(Du möchtest mehr Daten nach R importieren? Probiere dieses Tutorial.)

Die benötigten Pakete laden

Als ersten Schritt lädst du alle für diese Analyse benötigten Pakete in den Workspace (falls du sie lokal noch nicht installiert hast, nutze install.packages(), um sie verfügbar zu machen):

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

JavaScript-generierte Daten mit R scrapen

Als nächstes sammeln wir die TechStars-Daten mit PhantomJS. Schau dir die folgende einfache .js-Datei an:

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

Das Skript rendert die HTML-Seite, nachdem der zugrunde liegende JavaScript-Code seine Arbeit erledigt hat. So kannst du die HTML-Seite inklusive aller Tabellen abrufen. Damit wir für den Rest der Analyse in R bleiben, empfehlen wir, PhantomJS mit der Funktion system() aufzurufen (du musst PhantomJS herunterladen und installieren und es in dein Arbeitsverzeichnis legen):

# Lass PhantomJS TechStars scrapen, die Ausgabe wird in techstars.html geschrieben
system("./phantomjs scrape_techstars.js")

Nach diesem kleinen Umweg hast du endlich eine HTML-Datei, techstars.html, auf deinem lokalen System, die sich mit rvest scrapen lässt. Eine Inspektion der Techstars-Webseite zeigt, dass sich die gesuchten Tabellen in Divs mit der CSS-Klasse batch befinden:

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

class(batches)
[1] "XMLNodeSet"

Du hast jetzt eine Liste von XMLNodeSet-Objekten: Jedes Objekt enthält die Daten eines einzelnen TechStars-Batches. Darin finden wir Informationen zum Standort, Jahr und zur Saison des Batches, aber auch zu den Unternehmen, ihrem aktuellen Hauptsitz, ihrem Status und der insgesamt eingeworbenen Finanzierung. Wir gehen unten nicht im Detail auf die Schritte zur Datensammlung und -bereinigung ein; führe den Code selbst aus und sieh dir an, was er leistet. Du wirst merken, dass einige individuelle Bereinigungen stattfinden, damit jede Information sauber formatiert ist:

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})")
# Standortinformation ist alles im Batch-Titel, was nicht Jahr oder Saison ist
batch_location <- sub("\\s+$", "",
                      sub("([[:digit:]]{4})", "",
                          sub("(Fall|Spring|Winter|Summer)","",batch_titles)))

# Data Frame mit Batch-Infos erstellen.
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)
})

# Batch-Infos zur Breakdown hinzufügen
batch_info_extended <- batch_info[rep(seq_len(nrow(batch_info)),
                                  sapply(breakdown, nrow)),]
breakdown_merged <- rbind.fill(breakdown)

# Alle Informationen zusammenführen
techstars <- tbl_df(cbind(breakdown_merged, batch_info_extended)) %>%
  mutate(funding = as.numeric(gsub(",","",gsub("\\$","",funding))))

Mit einer Kombination aus Base R, rvest, plyr und dplyr haben wir nun den techstars-Data-Frame — einen Datensatz aller TechStars-Unternehmen mit allen öffentlich verfügbaren, sauber formatierten Informationen:

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"

Fazit

Mit der obigen Kombination aus Tools und Code konnten wir Daten von einer Website scrapen, die ihre Inhalte per JavaScript generiert. Wie du siehst, ist das ein klar strukturierter Prozess, der sich leicht umsetzen lässt, sobald der Initialcode steht. Du willst R lernen und suchst nach Data-Science- und R-Tutorials sowie Kursen? Schau dir DataCamp an.

Themen

Ähnlich

Tutorial

Python Datenstrukturen Tutorial

Tutorial

Python JSON-Daten: Ein Leitfaden mit Beispielen