如何用Python爬取Rotowire MLB球员新闻并构建可增量更新的DataFrame表格
解决方案:MLB新闻爬取、DataFrame整理与定时增量更新
1. 核心问题解决:数据解析与DataFrame字段映射
Rotowire的新闻条目有固定结构,通过BeautifulSoup精准定位元素,拆分日期、球员、标题和内容字段,直接映射到DataFrame的行中。
2. 完整代码实现
import requests from bs4 import BeautifulSoup import pandas as pd import schedule import time import hashlib # 初始化全局DataFrame,优先读取本地已保存数据 try: global_df = pd.read_csv("mlb_news.csv") except FileNotFoundError: global_df = pd.DataFrame(columns=["日期", "球员", "标题", "新闻内容", "唯一标识"]) def parse_mlb_news(): url = "https://www.rotowire.com/baseball/news.php" # 模拟浏览器请求,避免反爬拦截 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" } response = requests.get(url, headers=headers) response.raise_for_status() # 捕获请求错误 soup = BeautifulSoup(response.text, "html.parser") news_items = soup.find_all("div", class_="news-item") batch_data = [] for item in news_items: # 提取日期 date_tag = item.find("span", class_="news-item__date") news_date = date_tag.get_text(strip=True) if date_tag else "未知日期" # 提取标题与球员:标题格式多为「球员名: 新闻标题」 title_tag = item.find("a", class_="news-item__title") raw_title = title_tag.get_text(strip=True) if title_tag else "无标题" if ": " in raw_title: player_name, clean_title = raw_title.split(": ", 1) player_name = player_name.strip() clean_title = clean_title.strip() else: player_name = "未知球员" clean_title = raw_title # 提取新闻内容 content_tag = item.find("div", class_="news-item__content") news_content = content_tag.get_text(strip=True) if content_tag else "无内容" # 生成唯一标识,用于去重 unique_key = hashlib.md5(f"{news_date}{player_name}{clean_title}".encode()).hexdigest() batch_data.append({ "日期": news_date, "球员": player_name, "标题": clean_title, "新闻内容": news_content, "唯一标识": unique_key }) return pd.DataFrame(batch_data) def update_news_database(): global global_df # 爬取最新新闻 new_news_df = parse_mlb_news() # 合并数据并去重,保留首次出现的条目(避免重复爬取) combined_df = pd.concat([global_df, new_news_df]).drop_duplicates(subset="唯一标识", keep="first") global_df = combined_df.reset_index(drop=True) # 保存到本地文件,防止程序重启数据丢失 global_df.to_csv("mlb_news.csv", index=False, encoding="utf-8-sig") print(f"数据更新完成,当前累计新闻条数:{len(global_df)}") def start_scheduled_crawl(): # 立即执行一次初始爬取 update_news_database() # 设置每5分钟爬取一次 schedule.every(5).minutes.do(update_news_database) # 持续运行调度器 while True: schedule.run_pending() time.sleep(1) if __name__ == "__main__": start_scheduled_crawl()
3. 关键细节说明
- 字段拆分逻辑:利用标题中「球员名: 标题」的固定格式拆分球员与标题,无明确球员的条目标记为「未知球员」,兼容所有新闻类型。
- 去重机制:通过日期+球员+标题的哈希值生成唯一标识,确保重复爬取的新闻不会重复添加到DataFrame中。
- 数据持久化:每次更新后将DataFrame保存为CSV文件,程序重启时自动加载历史数据,实现增量积累。
- 反爬处理:设置浏览器UA头,避免被网站直接拦截;添加
response.raise_for_status()捕获请求异常,方便排查问题。
4. 注意事项
- 若网站更新页面结构(如class名称变化),需重新检查并修改BeautifulSoup的定位标签。
- 频繁爬取可能触发网站反爬机制,可根据实际情况调整爬取间隔,或添加随机延迟(如
time.sleep(random.randint(1,3)))。 - 若需要处理更多异常情况(如网络波动),可在
parse_mlb_news函数中添加try-except块捕获请求或解析错误。
内容的提问来源于stack exchange,提问作者buttermilk
相关产品推荐
相关产品推荐

