From abe8ba563e92daa0d35d24e9f44b87c0fd3a2568 Mon Sep 17 00:00:00 2001 From: JesseKartes Date: Wed, 6 Aug 2025 19:30:06 -0500 Subject: [PATCH] Added FanDuel scrape --- R/scrape_funcs.R | 18 ++- R/source_objects.R | 48 ++++++++ R/source_scrapes.R | 279 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+), 3 deletions(-) diff --git a/R/scrape_funcs.R b/R/scrape_funcs.R index a457d1b..6ee6bfe 100644 --- a/R/scrape_funcs.R +++ b/R/scrape_funcs.R @@ -7,7 +7,7 @@ #' @param src the sources that data should be scraped from should be one or more #' of \code{c("CBS", "ESPN", "FantasyData", "FantasyPros", "FantasySharks", #' "FFToday", "FleaFlicker", "NumberFire", "Yahoo", "FantasyFootballNerd", "NFL", -#' "RTSports","Walterfootball")} +#' "RTSports","Walterfootball", "FanDuel)} #' @param pos the posistions that data should be scraped for. Should be one or more #' of \code{c("QB", "RB", "WR", "TE", "K", "DST", "DL", "LB", "DB")} #' @param season The seaon for which data should be scraped. Should be set to the @@ -18,7 +18,7 @@ scrape_data <- function( src = c("CBS", "ESPN", "FantasyPros", "FantasySharks", "FFToday", "FleaFlicker", "NumberFire", "FantasyFootballNerd", "NFL", - "RTSports", "Walterfootball"), + "RTSports", "Walterfootball", "FanDuel"), pos = c("QB", "RB", "WR", "TE", "K", "DST", "DL", "LB", "DB"), season = NULL, week = NULL, ...){ @@ -32,7 +32,19 @@ scrape_data <- function( src = match.arg(src, several.ok = TRUE, c("CBS", "ESPN", "FantasyData", "FantasyPros", "FantasySharks", "FFToday", "FleaFlicker", "NumberFire", "FantasyFootballNerd", "NFL", - "RTSports","Walterfootball")) + "RTSports", "Walterfootball", "FanDuel")) + + # Check for NumberFire in src and convert to FanDuel + if("NumberFire" %in% src) { + message("\nHeads up! NumberFire is now FanDuel... Using FanDuel for scrape") + + src <- src[src != "NumberFire"] + + if(!("FanDuel" %in% src)) { + src <- c(src, "FanDuel") + } + } + pos = match.arg(pos, several.ok = TRUE, c("QB", "RB", "WR", "TE", "K", "DST", "DL", "LB", "DB")) diff --git a/R/source_objects.R b/R/source_objects.R index b966ed9..d857a91 100644 --- a/R/source_objects.R +++ b/R/source_objects.R @@ -464,6 +464,54 @@ fleaflicker_columns = c( "Availability % Own" = "pct_owned" ) +# FanDuel columns ---- +fanduel_columns <- c( + "player_numberFireId" = "src_id", + "player_name" = "player", + "player_position" = "pos", + "team_numberFireId" = "team_id", + "team_name" = "team_name", + "team_abbreviation" = "team", + "gameInfo_homeTeam_numberFireId" = "home_team_id", + "gameInfo_homeTeam_name" = "home_team", + "gameInfo_homeTeam_abbreviation" = "home_abbr", + "gameInfo_awayTeam_numberFireId" = "away_team_id", + "gameInfo_awayTeam_name" = "away_team", + "gameInfo_awayTeam_abbreviation" = "away_abbr", + "salary" = "salary", + "value" = "value", + "pass_comp" = "pass_comp", + "pass_att" = "pass_att", + "passingYards" = "pass_yds", + "passingTouchdowns" = "pass_tds", + "interceptionsThrown" = "pass_int", + "rushingAttempts" = "rush_att", + "rushingYards" = "rush_yds", + "rushingTouchdowns" = "rush_tds", + "receptions" = "rec", + "targets" = "rec_tgt", + "receivingYards" = "rec_yds", + "receivingTouchdowns" = "rec_tds", + "fantasy" = "site_pts", + "positionRank" = "pos_rank", + "overallRank" = "overall_rank", + "opponentDefensiveRank" = "opp_def_rank", + "pointsAllowed" = "dst_pts_allowed", + "yardsAllowed" = "dst_yds_allowed", + "sacks" = "dst_sacks", + "interceptions" = "dst_int", + "touchdowns" = "dst_td", + "extraPointsAttempted" = "fg_att", + "extraPointsMade" = "xp", + "fieldGoalsAttempted" = "xp_att", + "fieldGoalsMade" = "fg", + "fieldGoalsMade0To19" = "fg_0019", + "fieldGoalsMade20To29" = "fg_2029", + "fieldGoalsMade30To39" = "fg_3039", + "fieldGoalsMade40To49" = "fg_4049", + "fieldGoalsMade50Plus" = "fg_50" +) + # NFL FastR calc player stats ---- diff --git a/R/source_scrapes.R b/R/source_scrapes.R index e9a5475..03a8d53 100644 --- a/R/source_scrapes.R +++ b/R/source_scrapes.R @@ -1278,6 +1278,285 @@ scrape_fantasydata = function(pos = NULL, season = NULL, week = NULL, ) } +# FanDuel ---- +scrape_fanduel <- function(pos = c("QB", "RB", "WR", "TE", "K", "DST"), + season = NULL, week = 0, draft = TRUE, weekly = FALSE) { + + if(is.null(week)) { + season = get_scrape_year() + } + + if(is.null(week)) { + week = get_scrape_week() + } + + if(week > 0) { + proj_type = "WEEKLY" + } else { + proj_type = "REMAINING" + } + + if(is.null(pos)) { + pos = c("QB", "RB", "WR", "TE", "K", "DST") + } else { + pos + } + + message(paste0("\nScraping FanDuel projections for ", paste(pos, collapse = ", "), "...")) + + query <- ' + query GetProjections($input: ProjectionsInput!) { + getProjections(input: $input) { + ... on NflSkill { + player { + numberFireId + name + position + } + team { + numberFireId + name + abbreviation + } + gameInfo { + homeTeam { + numberFireId + name + abbreviation + } + awayTeam { + numberFireId + name + abbreviation + } + gameTime + } + salary + value + completionsAttempts + passingYards + passingTouchdowns + interceptionsThrown + rushingAttempts + rushingYards + rushingTouchdowns + receptions + targets + receivingYards + receivingTouchdowns + fantasy + positionRank + overallRank + opponentDefensiveRank + } + ... on NflKicker { + player { + numberFireId + name + position + } + team { + numberFireId + name + + abbreviation + } + gameInfo { + homeTeam { + numberFireId + name + abbreviation + } + awayTeam { + numberFireId + name + abbreviation + } + gameTime + } + salary + value + extraPointsAttempted + extraPointsMade + fieldGoalsAttempted + fieldGoalsMade + fieldGoalsMade0To19 + fieldGoalsMade20To29 + fieldGoalsMade30To39 + fieldGoalsMade40To49 + fieldGoalsMade50Plus + fantasy + positionRank + opponentDefensiveRank + } + ... on NflDefenseSt { + player { + numberFireId + name + position + } + team { + numberFireId + name + abbreviation + } + gameInfo { + homeTeam { + numberFireId + name + abbreviation + } + awayTeam { + numberFireId + name + abbreviation + } + gameTime + } + salary + value + pointsAllowed + yardsAllowed + sacks + interceptions + fumblesRecovered + touchdowns + fantasy + positionRank + opponentOffensiveRank + } + ... on NflDefensePlayer { + player { + numberFireId + name + position + } + team { + numberFireId + name + abbreviation + } + gameInfo { + homeTeam { + numberFireId + name + abbreviation + } + awayTeam { + numberFireId + name + abbreviation + } + gameTime + } + tackles + sacks + interceptions + touchdowns + passesDefended + fumblesRecovered + opponentOffensiveRank + } + } + } + ' + + data_payload <- list( + query = query, + variables = list( + input = list( + type = proj_type, + position = "NFL_SKILL", + sport = "NFL" + ) + ), + operationName = "GetProjections" + ) + + req <- httr2::request("https://fdresearch-api.fanduel.com/graphql") %>% + httr2::req_headers( + Accept = "*/*", + "Content-Type" = "application/json", + "Sec-Fetch-Site" = "same-site", + Origin = "https://www.fanduel.com", + "Sec-Fetch-Dest" = "empty", + "Accept-Language" = "en-US,en;q=0.9", + "Sec-Fetch-Mode" = "cors", + "User-Agent" = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.1 Safari/605.1.15", + "Accept-Encoding" = "gzip, deflate, br" + ) + + position_groups <- list( + NFL_SKILL = c("QB","RB","WR","TE"), + NFL_KICKER = c("K"), + NFL_D_ST = c("DST") + ) + + graphql_positions <- names(position_groups)[ + vapply(position_groups, function(x) any(pos %in% x), logical(1)) + ] + + all_dfs <- map(graphql_positions, function(graph_pos) { + data_payload$variables$input$position <- graph_pos + + resp <- req %>% + httr2::req_body_json(data_payload) %>% + httr2::req_perform() + + result <- httr2::resp_body_json( + resp, + simplifyDataFrame = TRUE, + flatten = TRUE + ) + + df <- tibble::as_tibble(result$data$getProjections) + + names(df) <- gsub("\\.", "_", names(df)) + names(df) <- rename_vec(names(df), fanduel_columns) + + df <- df %>% + type.convert(as.is = TRUE) %>% + dplyr::mutate( + data_src = "FanDuel", + proj_type = proj_type, + pos = ifelse(pos == "D", "DST", pos), + id = get_mfl_id(df$src_id, + player_name = df$player, + team = df$team, + pos = df$pos), + src_id = as.character(src_id) + ) + + if ("completionsAttempts" %in% names(df)) { + df <- df %>% + tidyr::separate( + completionsAttempts, + into = c("pass_comp", "pass_att"), + sep = "/", + convert = TRUE + ) + } + + df %>% dplyr::filter(pos %in% position_groups[[graph_pos]]) + }) + + out_df <- all_dfs %>% + dplyr::bind_rows() %>% + dplyr::select(id, src_id, player, pos, team, everything()) + l_pos <- split(out_df, out_df$pos) + l_pos <- l_pos[pos[pos %in% names(l_pos)]] + l_pos <- purrr::map(l_pos, function(df) { + df %>% + dplyr::select(-salary, -value) %>% + dplyr::select(where(~ !all(is.na(.)))) + }) + + attr(l_pos, "season") <- season + attr(l_pos, "week") <- week + + l_pos +} + # Depreceated ---- scrape_yahoo = function(pos = NULL, season = NULL, week = NULL, draft = TRUE, weekly = TRUE) {