使用bs4爬取stats.nba.com时含::符号的<a>标签问题咨询
搞定stats.nba.com爬取的模板占位符问题
嘿,这个问题我之前帮朋友排查过,太懂那种明明审查元素能看到真实数据,但用bs4爬出来却是一堆::player.PLAYER_NAME的挫败感了!
先搞懂::到底是什么
你看到的::是AngularJS 1.x版本里的「一次性数据绑定」语法。简单说就是:页面加载时,框架会把player.PLAYER_NAME这个变量的真实值替换到模板里,之后就算这个变量的值变了,页面也不会再更新。这种写法是为了减少页面的性能开销,避免不必要的监听。
为啥审查元素能看到真实姓名?因为浏览器已经执行了页面里的AngularJS代码,把模板渲染成了最终的页面;但bs4只会爬服务器返回的原始HTML模板文件,这时候JS还没运行,自然只能看到未替换的占位符啦。
两种靠谱的爬取方法
方法1:直接薅NBA的API(强烈推荐)
stats.nba.com的所有数据都是靠后台API加载的,直接请求API比折腾HTML高效100倍,还不容易被反爬。
- 打开浏览器F12切到「Network」标签,刷新页面后过滤「XHR/Fetch」请求,很快就能找到加载球员数据的接口(比如类似
leagueLeaders这类命名的接口)。 - 用
requests直接请求这个接口,返回的是JSON格式,直接解析就能拿到球员姓名、得分这些数据,完全不用管HTML模板那套。
给你个简单的代码示例:
import requests # 要带上请求头,不然容易被NBA的服务器拦截 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', 'Referer': 'https://stats.nba.com/' } # 这是2023-24赛季常规赛得分榜的接口,你可以根据需求改参数 api_url = 'https://stats.nba.com/stats/leagueLeaders?LeagueID=00&PerMode=PerGame&Scope=S&Season=2023-24&SeasonType=Regular%20Season&StatCategory=PTS' response = requests.get(api_url, headers=headers) player_data = response.json() # 解析数据,rowSet里的每一项就是一个球员的数据 for player in player_data['resultSet']['rowSet']: name = player[2] # 对应球员姓名的索引,看返回的JSON结构就能找到 ppg = player[12] # 场均得分 print(f"{name}: {ppg}分/场")
方法2:用浏览器自动化工具渲染页面
如果懒得找API,就用Selenium或者Playwright这类工具模拟真实浏览器打开页面,等JS把模板渲染完再爬:
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浏览器 driver = webdriver.Chrome() driver.get('https://stats.nba.com/leaders/') # 等页面渲染完成,这里等球员姓名的元素加载出来 wait = WebDriverWait(driver, 10) player_elements = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'a[title="View Stats Profile"]'))) # 提取球员姓名 for elem in player_elements: print(elem.text) driver.quit()
内容的提问来源于stack exchange,提问作者WoonBoon
相关产品推荐
相关产品推荐

