我的目标是将代码生成的嵌套列表转换为数据帧。我有以下内容,它从循环中的多个网址中提取一些数据并将它们存储在列表中。
library(rvest)
library(XML)
library(purrr)
library(stringr)
library(dplyr)
# declare variables
month = c('07','09')
year = c('2022','2023')
day = c('040','050')
# initialize the empty list
final = list()
# perform the loop
for (i in year) {
for (j in month) {
for (k in day) {
skip_to_next <- FALSE
url <- paste0('https://www.baseball-reference.com/boxes/ARI/ARI', i, j, k, '.shtml')
Sys.sleep(5)
game_path <- tryCatch(url |>
read_html() |>
html_nodes(xpath = '//div[contains(@id, "batting")]') |>
map(\(x) x |>
as.character() |>
str_remove_all("<!--|-->") |>
read_html() |>
html_table()) |>
unlist(recursive = FALSE), error = function(e) {skip_to_next <<- TRUE} )
if(skip_to_next) {next}
url <- read_html(url)
list_url <- url %>%
html_nodes(xpath = "//td/a") %>%
html_text()
List_2_letters = as.list(list_url[nchar(list_url) > 5])
game_path <- mapply(cbind, game_path, "Date" = paste(gsub('.{1}$', '', k), j, i, sep = '-'), SIMPLIFY=F)
game_path <- Map(cbind, game_path, "Team" = List_2_letters)
final[[i]][[j]][[k]] <- game_path
}
}
}
我得到了一堆如下所示的列表:
我想做的是合并所有具有
data.frame
值的列表。
我尝试了所有这些:
final_2 = map_dfr(final, ~ bind_rows(.x))
final_2 <- as.data.frame(do.call(cbind, final))
final_2 <- do.call("rbind", final)
但它们都只是并排生成 2 个列表。我实际上很困惑如何解决这个问题?
通过不创建嵌套列表,您可以更轻松地实现所需的结果。我重构了您的代码,首先将主要的抓取代码放入一个函数中,以便于调试和测试。在此函数中,我已使用
dplyr::bind_rows
将团队表绑定到一个数据帧中。该函数还应该更高效一些,因为它避免了像代码中那样读取 HTML 两次。
对于循环部分,我切换到
purrr::map
。为此,创建一个包含日期和相应 URL 的数据框。这样你就可以直接循环 url,而不需要嵌套的 for 循环。结果,您将获得一个数据帧列表,您最终可以将其按行绑定在一起。
最后,请注意,我删除了
tryCatch
并使用 purrr::safely
代替进行错误处理。
library(rvest)
library(purrr)
library(stringr)
library(dplyr)
make_url <- function(year, month, day) {
paste0(
"https://www.baseball-reference.com/boxes/ARI/ARI",
year, month, day, ".shtml"
)
}
scrape_table <- function(url) {
html <- read_html(url)
nodes <- html |>
html_elements(xpath = '//div[starts-with(@id, "all_") and contains(@id, "batting")]')
teams <- html %>%
html_elements(xpath = "//td/a") %>%
html_text()
nodes |>
purrr::set_names(teams) |>
purrr::map(\(x) {
x |>
as.character() |>
str_remove_all("<!--|-->") |>
read_html() |>
html_table()
}) |>
unlist(recursive = FALSE) |>
dplyr::bind_rows(.id = "Team")
}
# declare variables
month <- c("07", "09")
year <- c("2022")
day <- c("040")
dates <- expand.grid(
year = year, month = month, day = day
)
urls <- dates |>
mutate(
url = make_url(year, month, day),
date = paste(year, month, day, sep = "-"),
.keep = "unused"
)
safe_scrape_table <- purrr::safely(scrape_table)
final <- purrr::map(urls$url, \(url) {
Sys.sleep(5)
safe_scrape_table(url)
}) |>
set_names(urls$date)
final <- final |>
purrr::transpose() |>
pluck("result") |>
bind_rows(.id = "Date")
head(final)
#> # A tibble: 6 × 26
#> Date Team Batting AB R H RBI BB SO PA BA OBP
#> <chr> <chr> <chr> <int> <int> <int> <int> <int> <int> <int> <dbl> <dbl>
#> 1 2022-07-0… San … Austin… 2 0 0 0 1 1 3 0.243 0.367
#> 2 2022-07-0… San … Mike Y… 2 0 0 0 0 1 2 0.236 0.338
#> 3 2022-07-0… San … Wilmer… 3 1 0 0 0 2 4 0.242 0.331
#> 4 2022-07-0… San … Darin … 2 1 0 0 1 2 4 0.22 0.335
#> 5 2022-07-0… San … Evan L… 3 1 1 0 1 0 4 0.248 0.333
#> 6 2022-07-0… San … LaMont… 4 0 1 2 0 0 4 0.22 0.313
#> # ℹ 14 more variables: SLG <dbl>, OPS <dbl>, Pit <int>, Str <int>, WPA <dbl>,
#> # aLI <dbl>, `WPA+` <dbl>, `WPA-` <chr>, cWPA <chr>, acLI <dbl>, RE24 <dbl>,
#> # PO <int>, A <int>, Details <chr>