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
相关产品推荐
相关产品推荐

