使用Python爬取MLB首发阵容时数据缺失问题求助
MLB首发阵容爬取人员不全问题排查
我尝试用Python爬取https://www.baseballpress.com/lineups/2022-08-04页面的MLB首发阵容,运行下方代码后数据存入DataFrame,客队阵容在first_lineup列、主队在second_lineup列,但存在人员爬取不全的问题。附数据框输出和网页截图,求解释原因。
import requests import pandas as pd from bs4 import BeautifulSoup url = "https://www.baseballpress.com/lineups/2022-08-04" def get_names(item): try: player_name = item.get('data-razz').split("/")[-2].replace("+"," ") except IndexError: player_name = "" return player_name r = requests.get(url) soup = BeautifulSoup(r.text,'lxml') first_lineup = [get_names(item) for item in soup.select(".col--min:nth-of-type(1) > a.player-link, [class$='col--min']:nth-of-type(1) .player > a.player-link")] second_lineup = [get_names(item) for item in soup.select(".col--min:nth-of-type(2) > a.player-link, [class$='col--min']:nth-of-type(2) .player > a.player-link")] df = pd.DataFrame({"first_lineup":first_lineup,"second_lineup":second_lineup}) df.to_csv("baseballpress.csv", encoding='utf-8', index=False) print(df)
截图:
- 数据框输出:

- 网页示例:

问题原因分析
CSS选择器覆盖范围不全
你的选择器仅针对.col--min:nth-of-type(1)和:nth-of-type(2)下的直接子链接、特定嵌套层级的.player > a.player-link,但网页中部分球员的DOM结构可能不在这些范围内,导致该部分球员被遗漏。比如部分球员可能处于更深的嵌套容器,或是页面布局存在例外情况,你的选择器未覆盖到。名字提取依赖不稳定属性
你通过data-razz属性拆分获取球员名字,但部分球员的链接可能没有这个属性,触发IndexError后返回空字符串,看起来像是爬取不全,实际是名字提取失败。依赖这类非标准属性的方式容错性差,页面属性一旦变更就会失效。动态加载数据未被捕获
页面部分阵容数据可能通过JavaScript动态渲染,requests.get()只能获取初始静态HTML,无法加载JS异步请求生成的内容。如果有球员数据是页面加载后才填充的,静态HTML里就没有这些信息,自然爬取不到。
修正建议
- 优化CSS选择器:先定位每个比赛的阵容容器(
.lineup),再分别提取客队和主队的所有球员链接,确保覆盖所有阵容区块。 - 更换名字提取方式:优先提取链接的文本内容,比依赖
data-razz更稳定。 - 处理动态内容:如果确认是JS动态加载,可改用Selenium等工具模拟浏览器加载页面,获取完整渲染后的HTML。
修正后的示例代码:
import requests import pandas as pd from bs4 import BeautifulSoup url = "https://www.baseballpress.com/lineups/2022-08-04" def get_names(item): # 优先提取链接文本,备用data-razz属性 try: name = item.text.strip() if not name: name = item.get('data-razz', '').split("/")[-2].replace("+", " ") return name except: return "" r = requests.get(url) soup = BeautifulSoup(r.text, 'lxml') first_lineup = [] second_lineup = [] # 遍历每个比赛的阵容区块 for lineup_block in soup.select(".lineup"): # 提取客队所有球员链接 away_players = lineup_block.select(".col--min:first-of-type .player-link") first_lineup.extend([get_names(p) for p in away_players]) # 提取主队所有球员链接 home_players = lineup_block.select(".col--min:nth-of-type(2) .player-link") second_lineup.extend([get_names(p) for p in home_players]) # 对齐两队阵容长度,避免DataFrame行列不匹配 max_length = max(len(first_lineup), len(second_lineup)) first_lineup += [""] * (max_length - len(first_lineup)) second_lineup += [""] * (max_length - len(second_lineup)) df = pd.DataFrame({"first_lineup": first_lineup, "second_lineup": second_lineup}) df.to_csv("baseballpress.csv", encoding='utf-8', index=False) print(df)
内容的提问来源于stack exchange,提问作者Ben Ahnen
相关产品推荐
相关产品推荐

