espnff icon indicating copy to clipboard operation
espnff copied to clipboard

Roster endpoint

Open fgblomqvist opened this issue 8 years ago • 11 comments

Is there a known endpoint for getting a team's roster (for future support)?

fgblomqvist avatar Aug 24 '17 18:08 fgblomqvist

I wrote/adapted the following code as a simple standalone code to extract team's roster. I am still working a improving it but I am not "git-savvy" enough yet to make a PR to add this.

Maybe it will inspire some people here.

class InvalidPlayerRow(ValueError):
    pass

def _from_content_row(content_row, headers):
    data = {}
    for child, header in zip(content_row.children, headers):
        if header:
            # Special casing for name/pos
            if header == 'PLAYER, TEAM POS':
                player_name = list(child.children)[0].string.strip()
                if not player_name:
                    raise InvalidPlayerRow('Could not parse a player name')
                data['name'] = player_name
                pos = list(child.children)[1].split()
                if len(pos) == 1:
                    data[attr_lineup['TEAM']] = None
                    data[attr_lineup['POS']] = pos[0]
                elif len(pos) == 3:
                    data[attr_lineup['TEAM']] = pos[1]
                    data[attr_lineup['POS']] = pos[2]
                player_slot = list(child.children)[3].string.strip() # having some issue here
                data['slot'] = player_slot # having some issue with player slot
            else:
                # something doesn't work here, might be because the league isn't fully active
                #data[attr_lineup[header]] = child.string
                pass
    return data


def _get_headers(row):
    """Given a subheader in the player table, return a list of column names"""
    return [header.string for header in row.children]


def _get_players(soup):
    # Get all the rows in the player table
    table = soup.find('table', 'playerTableTable')
    rows = table.find_all('tr')
    players = []
    # Grab all the headers so we can match stats to names
    headers = _get_headers(rows[1])
    # Skip first 2 rows (headers)
    for row in rows[2:]:
        if 'pncPlayerRow' in row.attrs['class']:
            try:
                players.append(_from_content_row(row, headers))
            except InvalidPlayerRow:
                continue
        elif ('playerTableBgRowHead' in row.attrs['class'] or 'playerTableBgRowSubhead' in row.attrs['class']):
            # Ignore headers
            pass
        else:
            # Unknown row.
            print('Error')

    return players


def scrape_lineup(league_id, team_id, year):
    kwargs = {
        'league': league_id,
        'team': team_id,
        'year': year,
    }
    url = "http://games.espn.go.com/ffl/clubhouse?leagueId={league}&teamId={team}&seasonId={year}".format(**kwargs)
    content = requests.get(url).content
    soup = BeautifulSoup(content, "lxml")
    return _get_players(soup)

irish1986 avatar Aug 24 '17 19:08 irish1986

Found it: "playerInfo" retrieves all athletes ("players") currently on a fantasy team roster.

Credit: https://github.com/nickrobinson/fflpy/blob/master/fflpy.py

edit: "scoringPeriodId" can be passed as a parameter to fetch a specific week. Omitting it returns the latest week.

segfaultmagnet avatar Aug 25 '17 01:08 segfaultmagnet

Did some reverse engineering earlier today and found a rosterInfo endpoint that takes leagueId and teamIds as parameters (can't remember whether teamIds is comma-separated or not, but shouldn't be hard to figure out). This is what's needed to get all the players on the specified team(s) in the specified league.

fgblomqvist avatar Aug 25 '17 04:08 fgblomqvist

They are structured a little differently, but otherwise appear to be identical in content. The only real difference I'm seeing is that playerInfo pulls down every team's roster, while rosterInfo only returns one team's roster (mine). Specifying one or more teamIds didn't change anything for me. However, both of those behaviors may be because I'm sending cookies to access a private league. Are you able to get more than one roster in the same request?

segfaultmagnet avatar Aug 25 '17 06:08 segfaultmagnet

Yes, I just tried and by comma-separating multiple team/entry IDs, I can get multiple (whichever I specify). If I have time this weekend I could contribute at least a little bit to this project. More specifically the sign-in part of it. Just figured it out yesterday.

fgblomqvist avatar Aug 25 '17 13:08 fgblomqvist

Just created a pull request that implements rosters. You can call get_roster(week) and will return player info along with projected and actual scores for the week.

`league = espnff.League(league_id, year, espn_s2, swid)

roster = league.teams[0].get_roster(week=1)

for player in roster:

print(player['player_id'])

print(player['name'])

print(player['position'])

print(player['actual_score'])

print(player['projected_score'])`

drewski3420 avatar Sep 10 '17 13:09 drewski3420

boxscore gives lineup information that can be served up by week and team_id. However it's only available for 2016, 2017

try: http://games.espn.com/ffl/api/v2/boxscore?leagueId=1773242&seasonId=2016&teamId=1&scoringPeriodId=1

CMorton737 avatar Sep 16 '17 18:09 CMorton737

My pull request uses rosterInfo which seems more straightforward to me.

I haven't gone through the exercise of mapping the individual score breakdowns (yards, TDs, etc) however.

drewski3420 avatar Sep 16 '17 18:09 drewski3420

Sure, I agree. I just wanted to add because it seemed relevant. You've probably noticed that after two years ESPN purges Full Boxscore/Bench/Scoring Breakdown information. It appears -- at first glance -- that "boxscore" is the API handle for the Scoring Breakdown/Full Boxscore, and "rosterInfo" just serves up the most basic lineup.

CMorton737 avatar Sep 16 '17 18:09 CMorton737

Actually, looking into it more I see that's not the case. But it has the same issue where it is not accessible for seasons two years old.

What I would like to find out is API handle that serves up the minimized lineup information used to populate "QUICK BOX SCORE" from a season more than 2 years old (Im a big fan of league history)

CMorton737 avatar Sep 16 '17 19:09 CMorton737

If you're interested in the mapping of score breakdowns, I've done that in my fork. Feel free to use it. https://github.com/CMorton737/espyn/blob/master/statmap.py

CMorton737 avatar Sep 16 '17 19:09 CMorton737