Anstelle von xpathApply()
können Sie den ersten Knoten in den Knotensets jedes Pfades unterteilen und xmlValue()
darauf aufrufen. Hier ist, was ich kam mit,
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
Update: So behandeln die id
Abfragen nicht existieren, können wir einen Zustand mit RCurl::url.exists()
schreiben, die die schlechten Griffe. Daher gibt die folgende Funktion getAlbums()
je nach dem Status der URL einen Zeichenvektor der abgerufenen XML-Werte oder NA
zurück. Das kannst du natürlich ändern, wenn du willst. Das war nur eine Methode, die mir in den frühen Morgenstunden in den Sinn kam.
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]
Vielen Dank! Es funktionierte, außer ich bekomme eine Fehlermeldung, die besagt, dass es keine Funktion '" bind_rows "' gibt. Ich habe alle Pakete neu installiert, aber immer noch kein Glück. – torentino
'rbindlist' hat den Trick gemacht. Ich wollte schon seit einiger Zeit in 'Rvest' gehen, also hat mich dein Code dazu veranlasst, mich genauer damit zu beschäftigen. Danke @hrbrmstr. Eine weitere Frage: Was macht 'sprintf' eigentlich in der html-Funktion? – torentino
Es gibt ungefähr 48.000 Seiten, an denen ich interessiert bin zu scrapen, aber ich bemerkte, dass der Scraper aufhört, wenn er auf defekte Seiten stößt, d. H. "Interner Fehler". Eine Möglichkeit, mit ihnen umzugehen, besteht darin, auf jeder Seite eine Notiz zu prüfen, welche gebrochen sind und die guten innerhalb des 'Alben'-Objekts zu verketten, aber das ist zeitaufwendig. Haben Sie Vorschläge zum Umgang mit kaputten Seiten? Prost. – torentino