You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R中爬取PGA Tour动态JSON数据:历史赛事/年份数据获取困境

解决PGA Tour Stats历史赛事/年份数据爬取问题(动态页面适配)

问题背景

PGA Tour官网统计页面原URL格式为https://www.pgatour.com/stats/stat.STAT_ID.y.YEAR_ID.eoff.TOURNAMENT_ID.html,可通过遍历ID组合批量爬取多届赛事、多年份数据。但官网现已改为单页应用,切换年份/赛事时URL保持不变,仅通过动态加载更新数据,导致原方法失效。目前已通过rvest获取到最新赛事数据,但不知如何利用JSON中的tournamentId和year字段获取历史数据,纠结是否需要改用RSelenium。

解决方案:无需切换RSelenium,直接调用后台API

官网切换年份/赛事时,实际是通过后台API接口获取数据,无需启动浏览器模拟操作。以下是具体实现步骤:

1. 获取统计项的可选年份与赛事ID

先通过rvest读取页面的__NEXT_DATA__脚本,提取该统计项支持的所有年份和赛事ID:

library(tidyverse)
library(rvest)
library(jsonlite)

# 获取指定统计项的所有可选年份、赛事组合
get_filters <- function(stat_id) {
  page_url <- paste0("https://www.pgatour.com/stats/detail/", stat_id)
  page <- read_html(page_url)
  
  # 解析页面内嵌的JSON数据
  datascript <- page %>% html_elements(xpath = ".//script[@id='__NEXT_DATA__']") %>% html_text()
  page_data <- fromJSON(datascript)
  
  # 提取年份选项
  year_list <- page_data$props$pageProps$statDetails$filters$year$options %>%
    select(year = value, year_name = label)
  # 提取赛事选项
  tournament_list <- page_data$props$pageProps$statDetails$filters$tournament$options %>%
    select(tournament_id = value, tournament_name = label)
  
  # 生成所有年份+赛事的组合
  expand_grid(stat_id = stat_id, year_list, tournament_list)
}

# 示例:获取统计项02675的所有可选组合
filters_df <- get_filters("02675")

2. 批量请求API获取历史数据

根据提取到的年份、赛事ID组合,构造API请求URL并批量获取数据:

# 单条组合的数据请求函数
get_historical_stats <- function(stat_id, year, tournament_id) {
  # 构造API地址
  api_url <- paste0("https://statdata.pgatour.com/", stat_id, "/tournament/", tournament_id, "/year/", year, ".json")
  
  # 请求并解析JSON数据
  api_data <- fromJSON(api_url)
  
  # 数据整理(沿用原有的数据处理逻辑)
  raw_rows <- api_data$rows %>% drop_na(playerName)
  
  # 展开stats列表并转换为宽表
  stats_table <- raw_rows$stats %>%
    bind_rows(.id = "row_id") %>%
    select(-color) %>%
    pivot_wider(values_from = statValue, names_from = statName)
  
  # 合并基础信息与统计数据,补充年份、赛事标识
  final_data <- bind_cols(raw_rows, stats_table) %>%
    mutate(stat_id = stat_id, year = year, tournament_id = tournament_id)
  
  return(final_data)
}

# 批量处理所有组合,跳过请求失败的项
historical_data <- filters_df %>%
  mutate(stats_data = pmap(list(stat_id, year, tournament_id), possibly(get_historical_stats, otherwise = tibble()))) %>%
  unnest(stats_data)

3. 为什么不推荐RSelenium?

  • 效率更高:直接调用API获取JSON数据,比启动浏览器模拟交互快数倍
  • 稳定性更强:动态页面的元素定位易受版本更新影响,API接口的格式相对更稳定
  • 资源占用低:无需加载浏览器实例,节省内存与CPU资源

注意事项

  • 部分统计项可能没有全量的年份/赛事数据,possibly函数可自动跳过请求失败的组合
  • 避免高频请求,可在get_historical_stats函数中添加Sys.sleep(1)控制请求间隔,防止触发反爬机制
  • 定期验证API格式是否变更,官网可能会调整接口路径

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 23:10:56