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

如何用Python爬取2023版PGA Tour网站数据

PGA Tour官网数据爬取问题及后续排查

背景与需求

PGA Tour官网在2023年2月7日完成更新,彻底导致我之前的数据爬取方法失效。此前我通过开发者工具的Network标签找到隐藏URL,结合Python的Requests库拉取数据表。

现在需要实现两个爬取需求:

  • 爬取任意赛事任意年份/赛季的历史数据(新站示例为赛事过往结果页)
  • 爬取任意统计项任意年份/赛季的数据(新站示例为统计详情页)

我的初始尝试只能拉取当前页面的表格(无法获取往年数据),且部分数据显示为格式代码而非文本,初始代码如下:

import requests
import pandas as pd
tournament_url = 'https://www.pgatour.com/tournaments/2023/fortinet-championship/R2023464/past-results'
headers = {'User-Agent': 'Mozilla/5.0'}
t = pd.read_html(requests.get(tournament_url, headers=headers).text)[0]
t

编辑1:发现GraphQL接口

从回复中得知网站采用GraphQL,我在Network标签的graphql项里看到Payload包含变量:{ "tournamentPastResultsId": "R2023464", "year": 2022 }。这些变量包含赛事ID和年份,理论上修改变量就能爬取任意赛事任意年份的数据,但我不清楚怎么把它集成到爬取逻辑里,打算研究用Selenium传递这些变量。

编辑2:统计数据爬取成功实现

下方回复给出了赛事数据的爬取方法,我修改代码后成功获取了统计数据(示例为某统计项详情页),代码如下(感谢@Jurakin!):

import pandas as pd
from numpy import NaN
import requests
# 请求头中需要固定的x-api-key令牌
X_API_KEY = "da2-gsrx5bibzbb4njvhl7t37wqyl4"
YEAR = 2022  # 统计赛季
STAT_ID = "02567"  # 统计项ID
# 构造请求负载
payload = {
    "operationName": "StatDetails",
    "variables": {
        "tourCode": "R",
        "statId": STAT_ID,
        "year": YEAR,
        "eventQuery": None
    },
    "query": "query StatDetails($tourCode: TourCode!, $statId: String!, $year: Int, $eventQuery: StatDetailEventQuery) {\n  statDetails(\n    tourCode: $tourCode\n    statId: $statId\n    year: $year\n    eventQuery: $eventQuery\n  ) {\n    tourCode\n    year\n    displaySeason\n    statId\n    statType\n    tournamentPills {\n      tournamentId\n      displayName\n    }\n    yearPills {\n      year\n      displaySeason\n    }\n    statTitle\n    statDescription\n    tourAvg\n    lastProcessed\n    statHeaders\n    statCategories {\n      category\n      displayName\n      subCategories {\n        displayName\n        stats {\n          statId\n          statTitle\n        }\n      }\n    }\n    rows {\n      ... on StatDetailsPlayer {\n        __typename\n        playerId\n        playerName\n        country\n        countryFlag\n        rank\n        rankDiff\n        rankChangeTendency\n        stats {\n          statName\n          statValue\n          color\n        }\n      }\n      ... on StatDetailTourAvg {\n        __typename\n        displayName\n        value\n      }\n    }\n  }\n}"  
  }
# 发送POST请求
page = requests.post("https://orchestrator.pgatour.com/graphql", json=payload, headers={"x-api-key": X_API_KEY})
# 检查响应状态码
page.raise_for_status()
# 提取数据
data = page.json()["data"]["statDetails"]["rows"]
# print(data)
# 格式化为网页中的表格结构
table = map(lambda item: {
    "rank": item["rank"],
    "player": item["playerName"],
    "average": item["stats"][0]["statValue"],
}, data)
# 转换为DataFrame
s = pd.DataFrame(table)
s

编辑3:后续问题 - 3位字符Stat ID的适配问题

上述统计数据爬取代码适用于5位字符的Stat ID,但对于3位字符的Stat ID(示例为Birdie Average的156),数据能正常获取,但表格映射部分失败,我找不到原因,对应代码如下:

import pandas as pd
from numpy import NaN
import requests
# 请求头中需要固定的x-api-key令牌
X_API_KEY = "da2-gsrx5bibzbb4njvhl7t37wqyl4"
YEAR = 2022  # 统计赛季
# STAT_ID = "02567"  # 统计项ID SGOTT
STAT_ID = "156"  # Birdie Average - 三位数字的统计项无法正常工作,找不到原因
# 构造请求负载
payload = {
    "operationName": "StatDetails",
    "variables": {
        "tourCode": "R",
        "statId": STAT_ID,
        "year": YEAR,
        "eventQuery": None
    },
    "query": "query StatDetails($tourCode: TourCode!, $statId: String!, $year: Int, $eventQuery: StatDetailEventQuery) {\n  statDetails(\n    tourCode: $tourCode\n    statId: $statId\n    year: $year\n    eventQuery: $eventQuery\n  ) {\n    tourCode\n    year\n    displaySeason\n    statId\n    statType\n    tournamentPills {\n      tournamentId\n      displayName\n    }\n    yearPills {\n      year\n      displaySeason\n    }\n    statTitle\n    statDescription\n    tourAvg\n    lastProcessed\n    statHeaders\n    statCategories {\n      category\n      displayName\n      subCategories {\n        displayName\n        stats {\n          statId\n          statTitle\n        }\n      }\n    }\n    rows {\n      ... on StatDetailsPlayer {\n        __typename\n        playerId\n        playerName\n        country\n        countryFlag\n        rank\n        rankDiff\n        rankChangeTendency\n        stats {\n          statName\n          statValue\n          color\n        }\n      }\n      ... on StatDetailTourAvg {\n        __typename\n        displayName\n        value\n      }\n    }\n  }\n}"  
  }
# 发送POST请求
page = requests.post("https://orchestrator.pgatour.com/graphql", json=payload, headers={"x-api-key": X_API_KEY})
# 检查响应状态码
page.raise_for_status()
# 提取数据
data = page.json()["data"]["statDetails"]["rows"]
print(data)
# 格式化为网页中的表格结构
table = map(lambda item: {
    "RANK": item["rank"],
    "PLAYER": item["playerName"],
    "AVERAGE": item["stats"][0]["statValue"],
}, data)
# 转换为DataFrame
s = pd.DataFrame(table)
s

内容的提问来源于Stack Exchange,提问作者Ryan Miller

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:50:23