Vai al contenuto principale

Web scraping con R e PhantomJS

Breve tutorial sullo scraping di dati generati da Javascript con R usando PhantomJS.
· 5 min leggi

Scopri con l'IA

Quando devi fare web scraping, in genere useresti il pacchetto rvest di Hadley Wickham. Questo pacchetto offre una soluzione pronta all'uso per recuperare il codice HTML che genera una pagina web. Tuttavia, quando il sito o la pagina usa JavaScript per mostrare i dati che ti interessano, al pacchetto rvest manca la funzionalità necessaria. Una soluzione è usare PhantomJS.

(Vuoi esercitarti a importare più dati in R? Prova questo tutorial.)

Carica i pacchetti necessari

Come primo passo, dovrai caricare nell'ambiente di lavoro tutti i pacchetti necessari per questa analisi (se non li hai ancora installati in locale, usa install.packages() per renderli disponibili):

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

Scraping di dati generati da Javascript con R

Il passo successivo è la raccolta dei dati di TechStars usando PhantomJS. Dai un'occhiata al seguente file .js di base:

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

Lo script in pratica renderizza la pagina HTML dopo che il codice javascript sottostante ha fatto il suo lavoro, permettendoti di recuperare la pagina HTML con tutte le tabelle al suo interno. Per restare in R per il resto dell'analisi, ti suggeriamo di usare la funzione system() per invocare PhantomJS (dovrai scaricare e installare PhantomJS e metterlo nella tua directory di lavoro):

# Fai eseguire a phantomJS lo scraping di techstars, l'output viene scritto in techstars.html
system("./phantomjs scrape_techstars.js")

Dopo questa piccola deviazione, hai finalmente un file HTML, techstars.html, sul sistema locale, che può essere analizzato con rvest. Un'ispezione della pagina Techstars rivela che le tabelle che ci interessano si trovano in div con la classe CSS batch:

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

class(batches)
[1] "XMLNodeSet"

Ora hai un elenco di oggetti XMLNodeSet: ciascun oggetto contiene i dati per un singolo batch di TechStars. Al loro interno troviamo informazioni sulla sede del batch, l'anno, la stagione, ma anche sulle aziende, le attuali sedi centrali, lo stato attuale e l'ammontare complessivo dei finanziamenti raccolti. Non entreremo nel dettaglio dei passaggi di raccolta e pulizia dei dati qui sotto; puoi eseguire tu stesso il codice e ispezionare cosa fa. Vedrai che c'è un po' di pulizia personalizzata per assicurarsi che ogni informazione sia ben formattata:

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})")
# le informazioni sulla località sono tutto ciò che nel titolo del batch non è anno o stagione
batch_location <- sub("\\s+$", "",
                      sub("([[:digit:]]{4})", "",
                          sub("(Fall|Spring|Winter|Summer)","",batch_titles)))

# crea un data frame con le info del batch.
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)
})

# Aggiungi le info del batch al breakdown
batch_info_extended <- batch_info[rep(seq_len(nrow(batch_info)),
                                  sapply(breakdown, nrow)),]
breakdown_merged <- rbind.fill(breakdown)

# Unisci tutte le informazioni
techstars <- tbl_df(cbind(breakdown_merged, batch_info_extended)) %>%
  mutate(funding = as.numeric(gsub(",","",gsub("\\$","",funding))))

Con una combinazione di funzioni base di R, rvest, plyr e dplyr, ora abbiamo il data frame techstars; un dataset di tutte le aziende TechStars, con tutte le informazioni pubblicamente disponibili ben formattate:

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"

Conclusione

Usando la combinazione di strumenti e codice qui sopra, siamo riusciti a estrarre dati da un sito che usa JavaScript per generare i contenuti. Come si può vedere, è un processo molto strutturato, che può essere eseguito facilmente una volta disponibile il codice iniziale. Vuoi imparare R e cerchi tutorial e corsi su data science e R? Dai un'occhiata a DataCamp

Argomenti

Correlato