Skip to content

Commit d100337

Browse files
committed
Update for the 2021 season
Changes include: - revamped data fetch script to accomodate the new FantasyPros ranking table - fixed the loading of different formats when using the dropdown
1 parent 7a9a70c commit d100337

12 files changed

Lines changed: 155 additions & 120 deletions

File tree

.gitignore

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
# csv files
2-
/data/rankings_halfppr.json
3-
/data/rankings_ppr.json
4-
/data/rankings_standard.json
1+
# json files
2+
data/rankings*.json
53

64
# dependencies
75
/node_modules
@@ -19,3 +17,5 @@ npm-debug.log*
1917
yarn-debug.log*
2018
yarn-error.log*
2119
package-lock.json
20+
data/geckodriver.exe
21+
data/geckodriver.log

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2018 Jay Zheng, aptmac
3+
Copyright (c) 2018, 2021, Jay Zheng, aptmac
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
1-
# Draft Aid (Fantasy Football) - DEPRICATED
1+
# Draft Aid (Fantasy Football) - UPDATED FOR 2021
22

3-
### Note: I will not be updating this repo any more. Instead I've migrated this code to it's new home at: https://github.com/aptmac/ffdojo
3+
## 2021 Update:
4+
New for the 2021 season Fantasy Pros updated their rankings page, and now my old data fetch script won't work. Not just that, but now only a small subset of the data is loaded on page load, and the rest of the data populates via JavaScript once the user interacts with the page. This makes scraping the data a bit more difficult, but also makes organizing the data into a DataFrame or CSV format even harder.
5+
6+
That's okay though, because the new `fetch_fp.py` script will do everything the old script did, but on this new table format!
7+
8+
There is a catch though! We'll be using Selenium to scrap the table, but you'll need to supply the driver. Because open-source is cool, I opted for the Firefox geckodriver (https://github.com/mozilla/geckodriver/releases). For setting this up, you'll need to download the appropriate geckodriver release for your operating system, unzip the contents, and either:
9+
1. Place the geckodriver file into the `/data` folder
10+
2. Add geckodriver to your PATH and remove the `executable_path='./geckodriver.exe'` bit from the line where the driver is instantiated
11+
12+
When `fetch_fp.py` is running you'll see a Firefox window pop up for a few seconds, and that just means it's working. After that, everything should run as normal.
13+
14+
Happy drafting!
15+
## draftaid-react
416

517
This repository contains a modified version of Jay Zheng's draftaid-react project, which can be found at: [https://github.com/jayjzheng/draftaid-react](https://github.com/jayjzheng/draftaid-react)
618

719
## Installation and usage
820
I have included a script that can be used for easy local deployment. In the top level of this project, use the command `./run.sh` from the terminal to have the project install, build, and run all at once. When built, the application will be served at [http://localhost:3000](http://localhost:3000).
921

22+
Note: for the `fetch_fp.py` script you'll need to install a couple of dependencies, such as Selenium and Beautifulsoup. For the most part, they can just be installed by running `pip install <dependency>`, but pay attention to the logs for the exact package you'll need to install.
23+
1024
If you are interested in external web hosting, please see Jay Zheng's original repos for more details. This is a react-based project, so the commands required to run this application are:
1125

1226
- Install: `npm install`
@@ -15,19 +29,17 @@ If you are interested in external web hosting, please see Jay Zheng's original r
1529

1630
Note that you will need to have npm installed and configured on your machine.
1731

32+
Also note that you'll need geckodriver located in the `/data` folder, see the 2021 update blurb above for more details.
33+
1834
## Differences from the original application
1935

2036
Jay Zheng's original application is great out of the box. However, I wanted to tweak a few things before using it for my own fantasy draft purposes.
2137

38+
- This project uses Fantasy Pros rankings instead of Boris Chen's rankings (blasphemy, I know.. but it allows for an extra ~200+ data points so is it really THAT crazy of a change?)
2239
- Removed the 'reset' button (resetting the page can be done by refreshing)
2340
- Added undrafted lists for kickers and team defenses
2441
- Moved the master list of undrafted players to be inbetween the lists of players by position and drafted players
25-
- Thanks to kellenproctor, the application can be used with a local .json file pulled from fantasy pros, which allows for ~400 data points. This makes it possible to view kickers and defense, as well as showing more overall depth. The code to fetch the remote .json from the draftaid API is commented out on `DraftBoard.js`, but can be easily uncommented for future use.
2642

2743
## Kudos
2844

2945
Thank you to [Jay Zheng](https://github.com/jayjzheng) for creating this application, and elevating my draft results for (hopefully) many upcoming Fantasy Football seasons to come.
30-
31-
Kudos to kellenproctor for their PRs to draftaid-react! I was able to salvage the `fantasyProCsv.py` file to use a local player data .json file for the application. This allowed for an extra ~200 data items for the application, which made it possible to track kickers and more defense options.
32-
33-

data/fantasyProCsv.py

Lines changed: 0 additions & 75 deletions
This file was deleted.

data/fetch_fp.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
2+
# QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
3+
# QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
4+
# QQQQQQQQQQQQQQQQQQQWQQQQQWWWBBBHHHHHHHHHBWWWQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
5+
# QQQQQQQQQQQQQQQD!`__ssaaaaaaaaaass_ass_s____. -~""??9VWQQQQQQQQQQQQQQQQQQQ
6+
# QQQQQQQQQQQQQP'_wmQQQWWBWV?GwwwmmWQmwwwwwgmZUVVHAqwaaaac,"?9$QQQQQQQQQQQQQQ
7+
# QQQQQQQQQQQW! aQWQQQQW?qw#TTSgwawwggywawwpY?T?TYTYTXmwwgZ$ma/-?4QQQQQQQQQQQ
8+
# QQQQQQQQQQW' jQQQQWTqwDYauT9mmwwawww?WWWWQQQQQ@TT?TVTT9HQQQQQQw,-4QQQQQQQQQ
9+
# QQQQQQQQQQ[ jQQQQQyWVw2$wWWQQQWWQWWWW7WQQQQQQQQPWWQQQWQQw7WQQQWWc)WWQQQQQQQ
10+
# QQQQQQQQQf jQQQQQWWmWmmQWU???????9WWQmWQQQQQQQWjWQQQQQQQWQmQQQQWL 4QQQQQQQQ
11+
# QQQQQQQP'.yQQQQQQQQQQQP" <wa,.!4WQQQQQQQWdWP??!"??4WWQQQWQQc ?QWQQQQQ
12+
# QQQQQP'_a.<aamQQQW!<yF "!` .. "??$Qa "WQQQWTVP' "??' =QQmWWV?46/ ?QQQQQ
13+
# QQQP'sdyWQP?!`.-"?46mQQQQQQT!mQQgaa. <wWQQWQaa _aawmWWQQQQQQQQQWP4a7g -WWQQ
14+
# QQ[ j@mQP'adQQP4ga, -????" <jQQQQQWQQQQQQQQQWW;)WQWWWW9QQP?"` -?QzQ7L ]QQQ
15+
# QW jQkQ@ jWQQD'-?$QQQQQQQQQQQQQQQQQWWQWQQQWQQQc "4QQQQa .QP4QQQQfWkl jQQQ
16+
# QE ]QkQk $D?` waa "?9WWQQQP??T?47`_aamQQQQQQWWQw,-?QWWQQQQQ`"QQQD\Qf(.QWQQ
17+
# QQ,-Qm4Q/-QmQ6 "WWQma/ "??QQQQQQL 4W"- -?$QQQQWP`s,awT$QQQ@ "QW@?$:.yQQQQ
18+
# QQm/-4wTQgQWQQ, ?4WWk 4waac -???$waQQQQQQQQF??'<mWWWWWQW?^ ` ]6QQ' yQQQQQ
19+
# QQQQw,-?QmWQQQQw a, ?QWWQQQw _. "????9VWaamQWV???" a j/ ]QQf jQQQQQQ
20+
# QQQQQQw,"4QQQQQQm,-$Qa ???4F jQQQQQwc <aaas _aaaaa 4QW ]E )WQ`=QQQQQQQ
21+
# QQQQQQWQ/ $QQQQQQQa ?H ]Wwa, ???9WWWh dQWWW,=QWWU? ?! )WQ ]QQQQQQQ
22+
# QQQQQQQQQc-QWQQQQQW6, QWQWQQQk <c jWQ ]QQQQQQQ
23+
# QQQQQQQQQQ,"$WQQWQQQQg,."?QQQQ'.mQQQmaa,., . .; QWQ.]QQQQQQQ
24+
# QQQQQQQQQWQa ?$WQQWQQQQQa,."?( mQQQQQQW[:QQQQm[ ammF jy! j( } jQQQ(:QQQQQQQ
25+
# QQQQQQQQQQWWma "9gw?9gdB?QQwa, -??T$WQQ;:QQQWQ ]WWD _Qf +?! _jQQQWf QQQQQQQ
26+
# QQQQQQQQQQQQQQQws "Tqau?9maZ?WQmaas,, --~-- --- . _ssawmQQQQQQk 3QQQQWQ
27+
# QQQQQQQQQQQQQQQQWQga,-?9mwad?1wdT9WQQQQQWVVTTYY?YTVWQQQQWWD5mQQPQQQ ]QQQQQQ
28+
# QQQQQQQWQQQQQQQQQQQWQQwa,-??$QwadV}<wBHHVHWWBHHUWWBVTTTV5awBQQD6QQQ ]QQQQQQ
29+
# QQQQQQQQQQQQQQQQQQQQQQWWQQga,-"9$WQQmmwwmBUUHTTVWBWQQQQWVT?96aQWQQQ ]QQQQQQ
30+
# QQQQQQQQQQWQQQQWQQQQQQQQQQQWQQma,-?9$QQWWQQQQQQQWmQmmmmmQWQQQQWQQW(.yQQQQQW
31+
# QQQQQQQQQQQQQWQQQQQQWQQQQQQQQQQQQQga%,. -??9$QQQQQQQQQQQWQQWQQV? sWQQQQQQQ
32+
# QQQQQQQQQWQQQQQQQQQQQQQQWQQQQQQQQQQQWQQQQmywaa,;~^"!???????!^`_saQWWQQQQQQQ
33+
# QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQWWWWQQQQQmwywwwwwwmQQWQQQQQQQQQQQ
34+
# QQQQQQQWQQQWQQQQQQWQQQWQQQQQWQQQQQQQQQQQQQQQQWQQQQQWQQQWWWQQQQQQQQQQQQQQQWQ
35+
# credit to luiscoms for this beautiful ascii art
36+
# https://gist.github.com/luiscoms/f3703016ee218fd5283b
37+
38+
from bs4 import BeautifulSoup
39+
from selenium import webdriver
40+
from selenium.webdriver import Firefox
41+
from selenium.webdriver.common.by import By
42+
from selenium.webdriver.support.ui import WebDriverWait
43+
from selenium.webdriver.support import expected_conditions as EC
44+
45+
import json, re, sys
46+
47+
BASE_URL = "https://www.fantasypros.com/nfl/rankings/"
48+
49+
STD_URL = "consensus-cheatsheets.php"
50+
HALF_PPR_URL = "half-point-ppr-cheatsheets.php"
51+
PPR_URL = "ppr-cheatsheets.php"
52+
53+
FORMATS = {
54+
"standard" : STD_URL,
55+
"half_ppr" : HALF_PPR_URL,
56+
"ppr" : PPR_URL
57+
}
58+
59+
# Given a URL, use Selenium to open a web browser, fiddle with the page to cause
60+
# the JavaScript to run (and populate the table), then scrape the html
61+
def scrape_html(url):
62+
driver = Firefox(executable_path='./geckodriver.exe')
63+
driver.set_window_size(600, 400)
64+
driver.get(url)
65+
driver.execute_script("window.scrollTo(0, 420)") # blaze it
66+
element = WebDriverWait(driver, 5).until(
67+
EC.presence_of_element_located((By.ID, "ranking-table"))
68+
)
69+
html = driver.page_source
70+
driver.quit()
71+
return html
72+
73+
# Given a string containing the name AND team of a player, return two strings
74+
# containing just the name and just the team.
75+
# e.g., Christian McCaffrey (CAR) -> Christian McCaffrey, CAR
76+
def parse_name_and_team(name_and_team):
77+
m = re.match(r'(.+?)\(([A-Z]+)\)', name_and_team)
78+
return m.group(1).strip(), m.group(2)
79+
80+
# Given a positional ranking score, return the accronym for the position
81+
# e.g., RB1 -> RB
82+
def parse_position(positional_rank):
83+
m = re.match(r'([A-Z]+?)\d+', positional_rank)
84+
return m.group(1)
85+
86+
# Given the html data from the Fantasy Pros ranking table, parse the information
87+
# and return json formatted data
88+
def parse_player_data(html):
89+
soup = BeautifulSoup(html, 'html.parser')
90+
rows = soup.find("table", id="ranking-table").find_all("tr")
91+
rows.pop(0)
92+
93+
players = []
94+
current_tier = 0
95+
for row in rows:
96+
m = re.match(r'Tier\s(\d+)', row.text)
97+
if m:
98+
current_tier = m.group(1)
99+
else:
100+
# PLAYER ROW
101+
# [0] is overall ranking e.g., 1
102+
# [2] is name + (Team) e.g., Christian McCaffrey (CAR)
103+
# [3] is position+rank e.g., RB1
104+
# [4] is bye week e.g., 13
105+
data = row.find_all("td")
106+
player = {}
107+
player['tier'] = current_tier
108+
player['rank'] = data[0].text
109+
player['positional_rank'] = data[3].text
110+
name, team = parse_name_and_team(data[2].text)
111+
player['name'] = name
112+
player['team'] = team
113+
player['position'] = parse_position(data[3].text)
114+
player['bye_week'] = data[4].text
115+
players.append(player)
116+
return players
117+
118+
if __name__ == "__main__":
119+
for f in FORMATS.keys():
120+
data = {}
121+
data['format'] = f
122+
data['rankings'] = parse_player_data(scrape_html(BASE_URL + FORMATS[f]))
123+
with open('rankings_{}.json'.format(f), 'w') as outfile:
124+
json.dump(data, outfile)

deploy.sh

Lines changed: 0 additions & 1 deletion
This file was deleted.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "draftaid",
3-
"version": "0.1.0",
3+
"version": "1.1.0",
44
"private": true,
55
"devDependencies": {
66
"react-scripts": "0.9.0"

run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22
cd data;
3-
python fantasyProCsv.py;
3+
python fetch_fp.py;
44
cd ../;
55
npm install;
66
npm run build;

src/DraftBoard.js

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class DraftBoard extends Component {
1414
isLoading: true,
1515
currentDraft: 0,
1616
fetchError: null,
17-
format: 'standard',
17+
format: 'half_ppr',
1818
query: '',
1919
};
2020
}
@@ -26,7 +26,7 @@ class DraftBoard extends Component {
2626
// using local .json file
2727
fetchPlayers(format) {
2828
// create function to read from .json file
29-
var localJson = require('../data/rankings_standard.json');
29+
var localJson = require('../data/rankings_' + format + '.json');
3030
this.setState({
3131
players: localJson.rankings,
3232
filteredPlayers: localJson.rankings,
@@ -36,31 +36,6 @@ class DraftBoard extends Component {
3636
});
3737
}
3838

39-
// using draftaid-api
40-
//
41-
// fetchPlayers(format) {
42-
// const url = 'https://draftaid-api.herokuapp.com/rankings';
43-
// const self = this;
44-
// fetch(url+'?format='+format, {
45-
// method: 'get'
46-
// }).then(function(response) {
47-
// response.json().then(function(res){
48-
// self.setState({
49-
// players: res.rankings,
50-
// filteredPlayers: res.rankings,
51-
// isLoading: false,
52-
// format: format,
53-
// query: '',
54-
// });
55-
// });
56-
// }).catch(function(err) {
57-
// self.setState({
58-
// fetchError: err,
59-
// isLoading: false,
60-
// });
61-
// });
62-
// }
63-
6439
searchPlayers(query) {
6540
let players = this.state.players.filter(player =>
6641
player.name.toUpperCase().includes(query.toUpperCase())

src/Footer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from 'react';
33
function Footer() {
44
return (
55
<div className="footer">
6-
Draftaid-react 2018.
6+
Draftaid-react 2018-2021.
77
</div>
88
);
99
}

0 commit comments

Comments
 (0)