You need to enable JavaScript to run this app. R语言Web Scraping爬取fbref英超数据时生成多命名data frame的方法
优惠活动
大模型
产品
解决方案
定价
更多

R语言Web Scraping爬取fbref英超数据时生成多命名data frame的方法

核心实现思路

针对你目前的需求,两个最核心的解决逻辑:

  • 不需要手动给每个球队的dataframe单独命名存入全局环境,最高效的存储方式是用R的列表(list)承接所有球队的dataframe,列表的每个元素用对应球队名命名,既不会出现变量名冲突,也方便后续批量调用、导出。
  • 循环内所有重复的、不需要的页面抓取操作全部删掉,只保留当前球队页面的请求,减少无效请求。

现有代码的问题修正

  • 循环内部重复执行了抓取全联赛球队名的操作,完全冗余,会无谓增加请求次数、拖慢爬取速度,直接使用循环外提前拿到的球队名列表即可。
  • 用大括号包裹赋值的写法没有把最终生成的dataframe作为输出返回,每一轮循环生成的临时df会被下一轮循环直接覆盖,根本不会保留下来。
  • 组装dataframe时漏了提前提取的Result字段,最终生成的表格会缺列。
  • 部分CSS选择器没有限定在目标比赛表格#matchlogs_for的范围内,可能抓取到页面其他位置的同类元素,导致字段错位。
  • 没有做请求容错,某一支球队页面爬取失败就会导致整个循环中断,之前爬取到的数据全部丢失。

优化后的完整代码

library(rvest)

# 提前抓取联赛页面基础信息
page <- "https://fbref.com/en/comps/9/Premier-League-Stats"
scraped_page <- read_html(page)

# 提取所有球队的跳转链接
teamLinks <- scraped_page %>%
  html_nodes("#stats_squads_standard_for a") %>%
  html_attr("href")
teamLinks <- paste0("https://fbref.com/", teamLinks)

# 提取所有球队名称列表
Team <- scraped_page %>%
  html_nodes('#stats_squads_standard_for .left') %>%
  html_text() %>%
  as.character()

# 提前定义所有字段对应的CSS选择器(增加表格范围限定,避免抓错元素)
comp_sel <- "#matchlogs_for .left:nth-child(3) a"
venue_sel <- "#matchlogs_for .left:nth-child(6)"
result_sel <- "#matchlogs_for .left+ .center"
GF_sel <- "#matchlogs_for .right:nth-child(8)"
GA_sel <- "#matchlogs_for .right:nth-child(9)"
Opp_sel <- "#matchlogs_for .left:nth-child(10)"
xg_sel <- "#matchlogs_for td.left+ .right"
xga_sel <- "#matchlogs_for .right:nth-child(12)"
poss_sel <- "#matchlogs_for td:nth-child(13)"
formation_sel <- "#matchlogs_for .left:nth-child(16)"

# 初始化空列表存储所有球队的数据
team_data_list <- vector("list", length = length(Team))
names(team_data_list) <- Team

# 逐队爬取数据
for (i in seq_along(teamLinks)) {
  # 容错处理,单队爬取失败不中断整体流程
  tryCatch({
    current_url <- teamLinks[i]
    scraped_url <- read_html(current_url)
    
    # 逐字段提取比赛数据
    Comp <- scraped_url %>% html_nodes(comp_sel) %>% html_text()
    Venue <- scraped_url %>% html_nodes(venue_sel) %>% html_text()
    Result <- scraped_url %>% html_nodes(result_sel) %>% html_text()
    Goals_For <- scraped_url %>% html_nodes(GF_sel) %>% html_text()
    Goals_Against <- scraped_url %>% html_nodes(GA_sel) %>% html_text()
    Opponent <- scraped_url %>% html_nodes(Opp_sel) %>% html_text()
    xG <- scraped_url %>% html_nodes(xg_sel) %>% html_text()
    xGA <- scraped_url %>% html_nodes(xga_sel) %>% html_text()
    Possession <- scraped_url %>% html_nodes(poss_sel) %>% html_text()
    Formation <- scraped_url %>% html_nodes(formation_sel) %>% html_text()
    
    # 组装当前球队的dataframe
    current_df <- data.frame(
      Comp, Venue, Result, Goals_For, Goals_Against,
      Opponent, xG, xGA, Possession, Formation
    )
    
    # 将当前df存入列表的对应位置
    team_data_list[[i]] <- current_df
    
    # 控制请求频率,避免被站点限制访问
    Sys.sleep(3)
    
  }, error = function(e) {
    # 记录爬取失败的球队信息
    message(paste("爬取失败:", Team[i], ",错误原因:", e$message))
  })
}

列表存储的使用方法

不需要手动把每个球队的df命名成全局变量,直接通过列表索引就能访问对应数据:

# 查看某支球队的比赛数据,比如曼城
team_data_list[["Manchester City"]]

# 批量导出所有球队的数据为单独的CSV文件
for (team_name in names(team_data_list)) {
  write.csv(
    team_data_list[[team_name]],
    file = paste0(team_name, "_match_stats.csv"),
    row.names = FALSE,
    fileEncoding = "UTF-8"
  )
}

进一步优化的小技巧

  • 用seq_along(teamLinks)替代1:length(teamLinks)的写法,避免当链接列表为空时出现索引错误。
  • 如果你需要把所有球队的数据合并成一个大表,直接用dplyr::bind_rows(team_data_list, .id = "球队名称")就能一键合并,比单独存储全局变量效率高很多。
  • 爬取完成后可以检查每个df的列长度是否一致,如果出现长度不匹配的情况,说明对应的CSS选择器抓错了元素,需要调整选择器的范围。

内容的提问来源于stack exchange,提问作者BruDonnelly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.30 22:15:04