Injury reports are the most time-sensitive data in fantasy sports. Here's how to build an automated injury report scraper for fantasy analytics.
Why Automate Injury Tracking?
Fantasy sports platforms update injury statuses with varying delays. By scraping primary sources directly, you get earlier access, historical data for drafts, and automated lineup optimization.
Core Setup
import requests
from bs4 import BeautifulSoup
import pandas as pd
from datetime import datetime
import time
import random
PROXY_URL = "http://api.scraperapi.com"
API_KEY = "YOUR_SCRAPERAPI_KEY"
def fetch_page(url, render_js=False):
params = {
"api_key": API_KEY,
"url": url,
"render": str(render_js).lower()
}
resp = requests.get(PROXY_URL, params=params, timeout=30)
resp.raise_for_status()
return BeautifulSoup(resp.text, "html.parser")
Scraping NFL Injury Reports
def scrape_nfl_injuries():
url = "https://www.espn.com/nfl/injuries"
soup = fetch_page(url, render_js=True)
injuries = []
team_sections = soup.find_all("div", class_="injuries-team")
for section in team_sections:
team_name = section.find("h3")
if not team_name:
continue
team = team_name.text.strip()
rows = section.find_all("tr")[1:]
for row in rows:
cols = row.find_all("td")
if len(cols) >= 4:
injuries.append({
"team": team,
"player": cols[0].text.strip(),
"position": cols[1].text.strip(),
"injury": cols[2].text.strip(),
"status": cols[3].text.strip(),
"scraped_at": datetime.now().isoformat()
})
return pd.DataFrame(injuries)
Change Detection System
class InjuryTracker:
def __init__(self):
self.previous_state = {}
def detect_changes(self, current_df):
changes = []
for _, row in current_df.iterrows():
player_key = f"{row['player']}_{row['team']}"
current_status = row["status"]
previous = self.previous_state.get(player_key)
if previous is None:
changes.append({
"player": row["player"],
"team": row["team"],
"change_type": "NEW",
"old_status": None,
"new_status": current_status,
"timestamp": datetime.now().isoformat()
})
elif previous != current_status:
changes.append({
"player": row["player"],
"team": row["team"],
"change_type": "UPDATE",
"old_status": previous,
"new_status": current_status,
"timestamp": datetime.now().isoformat()
})
self.previous_state[player_key] = current_status
return changes
Fantasy Impact Scoring
def calculate_fantasy_impact(injury_df, player_stats):
status_weights = {
"Out": 1.0,
"Doubtful": 0.85,
"Questionable": 0.5,
"Probable": 0.15
}
impacts = []
for _, injury in injury_df.iterrows():
weight = status_weights.get(injury["status"], 0.5)
player_data = player_stats.get(injury["player"], {})
avg_points = player_data.get("avg_fantasy_points", 0)
impacts.append({
"player": injury["player"],
"status": injury["status"],
"risk_score": weight,
"projected_loss": avg_points * weight,
"recommendation": "BENCH" if weight > 0.6 else "MONITOR" if weight > 0.3 else "START"
})
return sorted(impacts, key=lambda x: x["projected_loss"], reverse=True)
Scaling Your Scraper
Sports sites get heavy traffic on game days:
- ScraperAPI — JavaScript rendering for ESPN and dynamic sports sites
- ThorData — Residential proxies for rate-limited sports APIs
- ScrapeOps — Monitor scraper health during peak game-day loads
Conclusion
Automated injury tracking gives fantasy players a real information edge. Build the scraper, set up change detection, and let the data drive your lineup decisions.
Top comments (0)