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]
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
'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
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