Вместо xpathApply()
вы можете подмножить первый узел в наборах узлов каждого пути и вызвать xmlValue()
. Вот что я придумал,
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: Для обработки запросов на id
не существует, мы можем написать условие с RCurl::url.exists()
, который обрабатывает плохие. Таким образом, следующая функция getAlbums()
возвращает вектор символа либо полученных значений xml, либо NA
, в зависимости от состояния URL-адреса. Вы можете это изменить, если хотите, конечно. Это был всего лишь метод, который приходил на ум в самые короткие часы.
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]
Спасибо! Он работал, за исключением того, что я получаю сообщение об ошибке, говорящее, что нет функции '" bind_rows ". Я снова установил все пакеты, но все равно не повезло. – torentino
'rbindlist' сделал трюк. Я уже давно хотел попасть в «rvest», чтобы ваш код заставил меня заглянуть в него более подробно. Спасибо @hrbrmstr. Еще один вопрос: что делает 'sprintf' фактически внутри функции html? – torentino
Есть примерно 48 000 страниц, которые мне интересны, но я заметил, что скребок останавливается, когда дело касается сломанных страниц, т. Е. 'Внутренняя ошибка'. Один из способов борьбы с ними - проверить на каждой странице заметку, которые были разбиты, и объединить хорошие объекты внутри объекта «альбомы», но это требует много времени. Есть ли у вас предложения по работе со сломанными страницами? Приветствия. – torentino