33# ' General wrapper for fetching team statistics from a specified source.
44# '
55# ' @param season Integer. The season to fetch stats for (e.g. 2024).
6- # ' @param summary_type Character. Either `"totals"` (default) or `"averages"`.
7- # ' @param source Character. Currently only `"afltables"` is supported.
6+ # ' @param summary_type Character. Either `"totals"` (default), `"averages"`, or other type depending on source.
7+ # ' If `source = "footywire"` and `summary_type = NULL`, returns all types.
8+ # ' @param source Character. One of `"afltables"` or `"footywire"`.
89# ' @param ... Additional arguments passed to the underlying data source function.
910# '
1011# ' @return A data frame of team stats for the season.
1112# ' @export
1213# '
1314# ' @examples
14- # ' fetch_team_stats(2023 )
15- # ' fetch_team_stats(2024 , summary_type = "averages")
15+ # ' fetch_team_stats(2024 )
16+ # ' fetch_team_stats(2023 , summary_type = "averages", source = "footywire ")
1617fetch_team_stats <- function (season ,
1718summary_type = " totals" ,
18- source = " afltables" ,
19+ source = c( " afltables" , " footywire " ) ,
1920... ) {
21+ source <- match.arg(source )
22+
2023dat <- switch (source ,
21- " afltables" = fetch_team_stats_afltables(season ,summary_type ),
24+ " afltables" = fetch_team_stats_afltables(season = season ,summary_type = summary_type ),
25+ " footywire" = fetch_team_stats_footywire(season = season ,
26+ summary_type = if (identical(summary_type ," totals" ))" totals" else summary_type ,
27+ ... ),
2228NULL
2329 )
2430
@@ -29,7 +35,6 @@ fetch_team_stats <- function(season,
2935return (dat )
3036}
3137
32-
3338# ' Fetch Team Statistics from AFLTables
3439# '
3540# ' Scrapes team-level statistics from AFLTables.com for a given season.
@@ -128,3 +133,87 @@ fetch_team_stats_afltables <- function(season, summary_type = "totals") {
128133
129134return (team_stats_final )
130135}
136+
137+ # ' Fetch Team Statistics from Footywire
138+ # '
139+ # ' @description
140+ # ' Scrapes team-level statistics (averages, totals, opponent stats) from Footywire for a given season.
141+ # '
142+ # ' @param season Integer. A year between 2003 and the current year.
143+ # ' @param summary_type Character. One of "totals", "averages", "opp_totals", "opp_averages", "diff_totals", "diff_averages".
144+ # ' If NULL (default), all types will be fetched and combined.
145+ # '
146+ # ' @return A data frame of team stats, including team name, games, and all statistical columns.
147+ # ' @export
148+ # '
149+ # ' @examples
150+ # ' fetch_team_stats_footywire(2024, summary_type = "totals")
151+ # ' fetch_team_stats_footywire(2023)
152+ fetch_team_stats_footywire <- function (season ,
153+ summary_type = c(" totals" ," averages" ,
154+ " opp_totals" ," opp_averages" ,
155+ " diff_totals" ," diff_averages" )) {
156+ summary_type <- match.arg(summary_type ,
157+ choices = c(" totals" ," averages" ," opp_totals" ," opp_averages" ," diff_totals" ," diff_averages" ),
158+ several.ok = TRUE )
159+
160+ type_map <- c(
161+ totals = " TT" ,
162+ averages = " TA" ,
163+ opp_totals = " OT" ,
164+ opp_averages = " OA" ,
165+ diff_totals = " DT" ,
166+ diff_averages = " DA"
167+ )
168+
169+ fetch_single_type <- function (season ,type ) {
170+ type_code <- type_map [type ]
171+ url <- glue :: glue(" https://www.footywire.com/afl/footy/ft_team_rankings?year={season}&type={type_code}&sby=2" )
172+
173+ page <- tryCatch(
174+ rvest :: read_html(url ),
175+ error = function (e )rlang :: abort(glue :: glue(" Could not read page: <{url}>" ))
176+ )
177+
178+ tables <- page %> %rvest :: html_elements(" table" )
179+ if (length(tables )< 11 )rlang :: abort(" Expected data table not found on page." )
180+
181+ rows <- tables [[11 ]] %> %rvest :: html_elements(" tr" ) %> %. [- 1 ]
182+
183+ parsed <- purrr :: map_dfr(rows ,function (row ) {
184+ cols <- row %> %rvest :: html_elements(" td" )
185+ if (length(cols )< 20 )return (NULL )
186+
187+ tibble :: tibble(
188+ Team = cols [[2 ]] %> %rvest :: html_element(" a" ) %> %rvest :: html_text(trim = TRUE ),
189+ Games = cols [[3 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
190+ K = cols [[4 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
191+ HB = cols [[5 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
192+ D = cols [[6 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
193+ M = cols [[7 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
194+ G = cols [[8 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
195+ GA = cols [[9 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
196+ I50 = cols [[10 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
197+ BH = cols [[11 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
198+ T = cols [[12 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
199+ HO = cols [[13 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
200+ FF = cols [[14 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
201+ FA = cols [[15 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
202+ CL = cols [[16 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
203+ CG = cols [[17 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
204+ R50 = cols [[18 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
205+ AF = cols [[19 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number(),
206+ SC = cols [[20 ]] %> %rvest :: html_text(trim = TRUE ) %> %readr :: parse_number()
207+ )
208+ })
209+
210+ parsed %> %
211+ dplyr :: mutate(
212+ Season = !! season ,
213+ Source = !! type
214+ ) %> %
215+ dplyr :: relocate(dplyr :: all_of(c(" Season" ," Team" ," Games" ," Source" )))
216+ }
217+
218+ purrr :: map_dfr(summary_type ,~ fetch_single_type(season ,.x ))
219+ }