使用Python Selenium从ATP官网SVG获取网球落点圆的cx/cy坐标
解决ATP官网SVG落点元素定位问题
问题原因分析
你遇到的定位失败,主要是这几个因素导致:
- 页面元素动态加载,代码执行时目标元素还未渲染完成
- SVG元素需要特殊的CSS选择器语法定位
- 无头模式默认窗口尺寸过小,引发布局异常、元素未加载
- 未先切换目标球员、发球类型和区域的选项,导致目标SVG元素未被激活
解决方案步骤
1. 优化无头模式配置
默认无头窗口尺寸不足,会导致页面布局错乱,添加窗口大小参数修正:
chrome_options.add_argument("--window-size=1920,1080")
2. 用显式等待替代硬等待
避免使用time.sleep(),改用WebDriverWait等待元素加载完成,确保操作时机正确:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC
3. 正确定位SVG内的circle元素
SVG属于特殊命名空间,定位时需明确上下文,用CSS选择器直接命中目标:
# 等待并获取所有落点circle元素 ball_plots = WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#ball-plots circle")) )
4. 实现选项切换(球员、发球类型、区域)
必须先切换到目标视图,才能获取对应落点数据,示例切换到J.Sinner的一发Deuce区:
# 切换球员为J.Sinner WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'Jannik Sinner')]")) ).click() # 切换发球类型为一发 WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), '1st Serve')]")) ).click() # 切换区域为Deuce区 WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'Deuce')]")) ).click()
完整修正代码
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC chrome_options = webdriver.ChromeOptions() chrome_options.add_experimental_option("excludeSwitches", ['enable-automation']) chrome_options.add_argument("headless") chrome_options.add_argument("--window-size=1920,1080") # 关键:设置窗口尺寸 chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36") # 模拟真实浏览器 driver = webdriver.Chrome(options=chrome_options) year = "2021" url = f"https://www.atptour.com/en/stats/second-screen/archive/{year}/403/ms001" driver.get(url) try: # 切换到J.Sinner WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'Jannik Sinner')]")) ).click() # 切换到一发 WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), '1st Serve')]")) ).click() # 切换到Deuce区 WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//button[contains(text(), 'Deuce')]")) ).click() # 等待落点元素加载并获取所有circle ball_circles = WebDriverWait(driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#ball-plots circle")) ) # 提取每个落点的坐标(cx, cy属性) for circle in ball_circles: cx = circle.get_attribute("cx") cy = circle.get_attribute("cy") print(f"落点坐标:({cx}, {cy})") # 可重复上述切换步骤,获取H.Hurkacz、二发、Add区的数据 finally: driver.quit()
额外注意事项
- 页面按钮文本可能有细微差异(比如全名/缩写),需用浏览器开发者工具确认实际元素属性
- 若发现元素嵌套在iframe内,需先切换iframe:
driver.switch_to.frame("iframe_id") - 若遇到反爬限制,可增加
--disable-blink-features=AutomationControlled参数规避检测
内容的提问来源于stack exchange,提问作者user20451823
相关产品推荐
相关产品推荐

