2015-03-08 11 views
6

Sto raschiando http://www.progarchives.com/album.asp?id= e ottenere un messaggio di avviso:Raschiare .asp sito con R

Warning message:
XML content does not seem to be XML:
http://www.progarchives.com/album.asp?id=2
http://www.progarchives.com/album.asp?id=3 http://www.progarchives.com/album.asp?id=4
http://www.progarchives.com/album.asp?id=5

Il raschietto funziona per ogni pagina separatamente, ma non per gli URL b1=2:b2=1000.

library(RCurl) 
library(XML) 

getUrls <- function(b1,b2){ 
    root="http://www.progarchives.com/album.asp?id=" 
    urls <- NULL 
    for (bandid in b1:b2){ 
    urls <- c(urls,(paste(root,bandid,sep=""))) 
    } 
    return(urls) 
} 

prog.arch.scraper <- function(url){ 
SOURCE <- getUrls(b1=2,b2=1000) 
PARSED <- htmlParse(SOURCE) 
album <- xpathSApply(PARSED,"//h1[1]",xmlValue) 
date <- xpathSApply(PARSED,"//strong[1]",xmlValue) 
band <- xpathSApply(PARSED,"//h2[1]",xmlValue) 
return(c(band,album,date)) 
} 

prog.arch.scraper(urls) 

risposta

6

Ecco un approccio alternativo con rvest e dplyr:

library(rvest) 
library(dplyr) 
library(pbapply) 

base_url <- "http://www.progarchives.com/album.asp?id=%s" 

get_album_info <- function(id) { 

    pg <- html(sprintf(base_url, id)) 
    data.frame(album=pg %>% html_nodes(xpath="//h1[1]") %>% html_text(), 
      date=pg %>% html_nodes(xpath="//strong[1]") %>% html_text(), 
      band=pg %>% html_nodes(xpath="//h2[1]") %>% html_text(), 
      stringsAsFactors=FALSE) 

} 

albums <- bind_rows(pblapply(2:10, get_album_info)) 

head(albums) 

## Source: local data frame [6 x 3] 
## 
##      album       date  band 
## 1     FOXTROT Studio Album, released in 1972 Genesis 
## 2    NURSERY CRYME Studio Album, released in 1971 Genesis 
## 3    GENESIS LIVE   Live, released in 1973 Genesis 
## 4  A TRICK OF THE TAIL Studio Album, released in 1976 Genesis 
## 5 FROM GENESIS TO REVELATION Studio Album, released in 1969 Genesis 
## 6   GRATUITOUS FLASH Studio Album, released in 1984 Abel Ganz 

non mi sono sentito come barraging il sito con una tonnellata di reqs così lievitare la sequenza per l'uso. pblapply ti offre una barra di avanzamento gratuita.

Per essere gentili con il sito (specialmente perché non proibisce esplicitamente lo scraping) si potrebbe voler inserire un Sys.sleep(10) alla fine della funzione get_album_info.

UPDATE

per gestire gli errori del server (in questo caso 500, ma funzionerà per gli altri, anche), è possibile utilizzare try:

library(rvest) 
library(dplyr) 
library(pbapply) 
library(data.table) 

base_url <- "http://www.progarchives.com/album.asp?id=%s" 

get_album_info <- function(id) { 

    pg <- try(html(sprintf(base_url, id)), silent=TRUE) 

    if (inherits(pg, "try-error")) { 
    data.frame(album=character(0), date=character(0), band=character(0)) 
    } else { 
    data.frame(album=pg %>% html_nodes(xpath="//h1[1]") %>% html_text(), 
       date=pg %>% html_nodes(xpath="//strong[1]") %>% html_text(), 
       band=pg %>% html_nodes(xpath="//h2[1]") %>% html_text(), 
       stringsAsFactors=FALSE) 
    } 

} 

albums <- rbindlist(pblapply(c(9:10, 23, 28, 29, 30), get_album_info)) 

##      album       date   band 
## 1: THE DANGERS OF STRANGERS Studio Album, released in 1988 Abel Ganz 
## 2: THE DEAFENING SILENCE Studio Album, released in 1994 Abel Ganz 
## 3:    AD INFINITUM Studio Album, released in 1998 Ad Infinitum 

non sarà possibile ottenere alcun voci per le pagine errate (in questo caso restituisce solo le voci 9, 10 e 30).

+0

Grazie! Ha funzionato tranne che ho ricevuto un messaggio di errore che diceva che non esiste alcuna funzione "" bind_rows "'. Ho reinstallato tutti i pacchetti ma ancora senza fortuna. – torentino

+0

'rbindlist' ha fatto il trucco. Ho intenzione di entrare in 'rvest' per un po 'di tempo, quindi il tuo codice mi ha permesso di esaminarlo in modo più dettagliato. Grazie a @hrbrmstr. Un'ultima domanda però, che cosa fa effettivamente "sprintf" all'interno della funzione html? – torentino

+0

Ci sono circa 48.000 pagine che sono interessato a raschiare ma ho notato che il raschietto si ferma quando incontra pagine rotte, cioè "Errore interno". Un modo per gestirli è quello di controllare su ciascuna pagina una nota quali sono interrotti e concatenare quelli buoni all'interno dell'oggetto 'album', ma questo richiede molto tempo. Hai qualche suggerimento per trattare pagine rotte? Saluti. – torentino

4

Invece di xpathApply(), è possibile impostare il primo nodo nei set di nodi di ciascun percorso e chiamare xmlValue(). Ecco quello che mi è venuta,

library(XML) 
library(RCurl) 

## define the urls and xpath queries 
urls <- sprintf("http://www.progarchives.com/album.asp?id=%s", 2:10) 
path <- c(album = "//h1", date = "//strong", band = "//h2") 

## define a re-usable curl handle for the c-level nodes 
curl <- getCurlHandle() 
## allocate the result list 
out <- vector("list", length(urls)) 

## do the work  
for(u in urls) { 
    content <- getURL(u, curl = curl) 
    doc <- htmlParse(content, useInternalNodes = TRUE) 
    out[[u]] <- lapply(path, function(x) xmlValue(doc[x][[1]])) 
    free(doc) 
} 

## structure the result 
data.table::rbindlist(out) 
#       album       date  band 
# 1:     FOXTROT Studio Album, released in 1972 Genesis 
# 2:    NURSERY CRYME Studio Album, released in 1971 Genesis 
# 3:    GENESIS LIVE   Live, released in 1973 Genesis 
# 4:  A TRICK OF THE TAIL Studio Album, released in 1976 Genesis 
# 5: FROM GENESIS TO REVELATION Studio Album, released in 1969 Genesis 
# 6:   GRATUITOUS FLASH Studio Album, released in 1984 Abel Ganz 
# 7:   GULLIBLES TRAVELS Studio Album, released in 1985 Abel Ganz 
# 8: THE DANGERS OF STRANGERS Studio Album, released in 1988 Abel Ganz 
# 9:  THE DEAFENING SILENCE Studio Album, released in 1994 Abel Ganz 

Aggiornamento: Per gestire non esistono le id query, possiamo scrivere una condizione con RCurl::url.exists() che gestisce i cattivi. Pertanto, la seguente funzione getAlbums() restituisce un vettore di caratteri dei valori xml recuperati o NA, a seconda dello stato dell'URL. Puoi cambiarlo se vuoi, ovviamente. Quello era solo un metodo che mi è venuto in mente nelle ore piccole.

getAlbums <- function(url, id = numeric(), xPath = list()) { 
    urls <- sprintf("%s?id=%d", url, id) 
    curl <- getCurlHandle() 
    out <- vector("list", length(urls)) 
    for(u in urls) { 
     out[[u]] <- if(url.exists(u)) { 
      content <- getURL(u, curl = curl) 
      doc <- htmlParse(content, useInternalNodes = TRUE) 
      lapply(path, function(x) xmlValue(doc[x][[1]])) 
     } else { 
      warning(sprintf("returning 'NA' for urls[%d] ", id[urls == u])) 
      structure(as.list(path[NA]), names = names(path)) 
     } 
     if(exists("doc")) free(doc) 
    } 
    data.table::rbindlist(out) 
} 

url <- "http://www.progarchives.com/album.asp" 
id <- c(9:10, 23, 28, 29, 30) 
path <- c(album = "//h1", date = "//strong", band = "//h2") 
getAlbums(url, id, path) 
#      album       date   band 
# 1: THE DANGERS OF STRANGERS Studio Album, released in 1988 Abel Ganz 
# 2: THE DEAFENING SILENCE Studio Album, released in 1994 Abel Ganz 
# 3:      NA        NA   NA 
# 4:      NA        NA   NA 
# 5:      NA        NA   NA 
# 6:    AD INFINITUM Studio Album, released in 1998 Ad Infinitum 
# 
# Warning messages: 
# 1: In albums(url, id, path) : returning 'NA' for urls[23] 
# 2: In albums(url, id, path) : returning 'NA' for urls[28] 
# 3: In albums(url, id, path) : returning 'NA' for urls[29] 
+0

@ Richard Scriven. Grazie! Funziona bene, tranne che mi imbatto nello stesso problema di cui sopra con i collegamenti interrotti. – torentino

+0

Funziona! Grazie per l'aggiornamento e la gestione degli errori. – torentino

Problemi correlati