hoopscrape

HoopScrape allows you to easily integrate logistic and statistical information about your favorite basketball teams in your own development projects. Easily access data via Structs for dot notation, Hashes for ActiveRecord integration or String arrays.


Keywords
api, basketball, basketball-stats, nba-data, ruby
License
GPL-3.0
Install
gem install hoopscrape -v 1.1

Documentation

hoopscrape is not associated with ESPN or the NBA

Gem Version Code Climate Build Status Test Coverage

Table of Contents

Introduction

The hoopscrape Ruby gem is a scraper for NBA data. It provides a number of ways to simplify data interaction, including :

  • Structs - Intuitively access data via dot notation.
  • Hashes - Pass data directly to ActiveRecord CRUD methods for easy database interaction.
  • String arrays - Raw data for you to manipulate as you see fit.

Version 1.1

  • Fixed security vulnerabilities with Nokogiri and Rubocop. Unfortunately, this means HoopScrape now requires Ruby >= 2.1.0

  • Updated test suite.

  • Please report any issues you encounter!

Installation

Rails

In your application's Gemfile, include :

gem 'hoopscrape'

In your project dir, execute :

$ bundle install

Manual

$ gem install hoopscrape

Arrays, Hashes or Structs

If you intend to work with a single format, you can specify it at initialization. When working with multiple formats you should start with the default and convert as necessary using Array#to_structs or Array#to_hashes.

  require 'hoopscrape'
  hs   = HoopScrape.new                      # String Arrays
  hs_h = HoopScrape.new(format: :to_hashes)  # Hash Arrays
  hs_s = HoopScrape.new(format: :to_structs) # Struct Arrays

Working With Multiple Formats

Arrays can easily be converted to Hashes or Structs

Default format
hs    = HoopScrape.new
bs    = es.boxscore(400828991)   # Return an NbaBoxscore object
stats = bs.homePlayers           # Returns a multidimensional array of Home Player stats
stats[4][2]                      # Player Name   # => 'R. Hood'
stats[4][20]                     # Player Points # => '30'
Same data using Hashes
s_hashes = stats.to_hashes       # Returns array of Hashes
s_hashes[4][:name]               # Player Name   # => 'R. Hood'
s_hashes[4][:points]             # Player Points # => '30'
Same data using Structs
s_structs = stats.to_structs     # Returns array of Structs
s_structs[4].name                # Player Name   # => 'R. Hood'
s_structs[4].points              # Player Points # => '30'

Customize Field Names for Hash and Struct Conversion

The Array#to_hashes and Array#to_structs methods can be passed an array of Symbols to use in place of the default field names.

team_list   = HoopScrape.teamList
team_list_s = t.to_structs([:abbrev, :long_team_name, :div, :conf]) # New Field Names
team_list_s.last.long_team_name    # => 'Utah Jazz'

Defaults are defined in the SymbolDefaults module. You can overwrite them or use them as templates, replacing individual symbols using the Array#change_sym! method.

Default As Template

Safe method

my_names = S_ROSTER.dup.change_sym!(:p_name, :full_name).change_sym!(:salary, :crazy_money)
players  = HoopScrape.roster('CLE').players.to_structs(my_names)
players[3].full_name    # => 'LeBron James'
players[3].crazy_money  # => '22970500'
Overwrite Default

Note: Changes affect all instances of hoopscrape

S_TEAM    # => [:team,  :name, :division, :conference]
S_TEAM.replace [:short, :long, :div, :conf]
t = HoopScrape.teamList.to_structs

t.first.short # => 'BOS'
t.first.long  # => 'Boston Celtics'

Working with Navigators

Table data is wrapped in a Navigator class which provides helper methods for moving through the table. The type of object the Navigator returns matches the format provided at hoopscrape instantiation.

Note: Data converted using Array#to_structs or Array#to_hashes is not wrapped in a Navigator.

Navigator Methods

# <Navigator> A Navigator for Home Player Stats Table
navigator = HoopScrape.boxscore(400878158).homePlayers

navigator[]      # Array<Object> Returns the underlying Array of the Navigator
navigator[5]     # <Object> 6th row of data
navigator.size   # <Fixnum> Number of table rows
navigator.first  # <Object> Access the first data row
navigator.last   # <Object> Access the last data row
navigator.next   # <Object> Access the next data row     (nil if there is no more data)
navigator.curr   # <Object> Access the current data row  (nil at initialization)
navigator.prev   # <Object> Access the previous data row (nil if there is no more data)

Data Access

NBA Team List

hs        = HoopScrape.new
team_list = es.teamList      # multidimensional array of Team info
team_list.last               # => ['UTA', 'Utah Jazz', 'Northwest', 'Western']
team_list.last[0]            # => 'UTA'
team_list.last[1]            # => 'Utah Jazz'
team_list.last[2]            # => 'Northwest'
team_list.last[3]            # => 'Western'

Boxscore

Boxscore #homePlayers, #awayPlayers return a Navigator

hs    = HoopScrape.new(format: :to_structs)
bs    = es.boxscore(400875892)   # Return an NbaBoxscore object

bs.id                 # <String> Boxscore ID    # => '400875892'
bs.gameDate           # <String> Game DateTime  # => '2016-05-07 00:00:00'

bs.homeName           # <String> Full Team Name
bs.homeScore          # <String> Team Score
bs.homeTotals         # <Object> Access the cumulative team totals
bs.homePlayers        # <Navigator> A Navigator for Home Player Stats Table

bs.awayName           # <String> Full Team Name
bs.awayScore          # <String> Team Score
bs.awayTotals         # <Object> Access the cumulative team totals
bs.awayPlayers        # <Navigator> A Navigator for Home Player Stats Table
Player Data
wade = bs.homePlayers[4] # <Object> of data for Row 5

wade.team       # <String> Team ID          # => 'MIA'
wade.id         # <String> Player ID        # => '1987'
wade.name       # <String> Short Name       # => 'D. Wade'
wade.position   # <String> Position         # => 'SG'
wade.minutes    # <String> Minutes          # => '36'
wade.fgm        # <String> Shots Made       # => '13'
wade.fga        # <String> Shots Attempted  # => '25'
wade.tpm        # <String> 3P Made          # => '4'
wade.tpa        # <String> 3P Attempted     # => '6'
wade.ftm        # <String> Freethrows Made  # => '8'
wade.fta        # <String> Freethrows Att.  # => '8'
wade.oreb       # <String> Offensive Reb.   # => '1'
wade.dreb       # <String> Defensive Reb.   # => '7'
wade.rebounds   # <String> Total Rebounds   # => '8'
wade.assists    # <String> Assists          # => '4'
wade.steals     # <String> Steals           # => '0'
wade.blocks     # <String> Blocks           # => '0'
wade.tos        # <String> Turnovers        # => '4'
wade.fouls      # <String> Personal Fouls   # => '1'
wade.plusminus  # <String> Plus/Minus       # => '-8'
wade.points     # <String> Points           # => '38'
wade.starter    # <String> Starter?         # => 'true'
Team Data
miami = bs.homeTotals   # <Object> Access the team totals
miami.team
miami.fgm
miami.fga
miami.tpm
miami.tpa
miami.ftm
miami.fta
miami.oreb
miami.dreb
miami.rebounds
miami.assists
miami.steals
miami.blocks
miami.turnovers
miami.fouls
miami.points

Roster

Roster #players is a Navigator.

roster  = es.roster('UTA')
r_hash  = es.roster('UTA', format: :to_hashes)  # Pre-format players data
players = roster.players                        # Returns multidimensional array of Roster info
coach   = roster.coach                          # Coach Name # => 'Quinn Snyder'

# Roster as an array of objects
players = players.to_structs   # Returns array of Structs

players[2].team                # Team ID          # => 'UTA'
players[2].jersey              # Jersey Number    # => '11'
players[2].name                # Name             # => 'Alec Burks'
players[2].id                  # ID               # => '6429'
players[2].position            # Position         # => 'SG'
players[2].age                 # Age              # => '24'
players[2].height_ft           # Height (ft)      # => '6'
players[2].height_in           # Height (in)      # => '6'
players[2].salary              # Salary           # => '9463484'
players[2].weight              # Weight           # => '214'
players[2].college             # College          # => 'Colorado'
players[2].salary              # Salary           # => '9463484'

Player

player = es.player(2991473) # Returns an NbaPlayer object
player.name                 #=> "Anthony Bennett"
player.age                  #=> "23"
player.weight               #=> "245"
player.college              #=> "UNLV"
player.height_ft            #=> "6"
player.height_in            #=> "8"

Schedule

Schedule #allGames, #pastGames, #futureGames return a Navigator

schedule = es.schedule('UTA')                      # Gets latest available year and season type data
schedule = es.schedule('LAC', format: :to_structs) # Pre-format data (:to_hashes / :to_structs)
schedule = es.schedule('SAS', year: 2005)          # Gets historical data for season ending in 2005
schedule = es.schedule('POR', season: 1)           # Get specific season type (1 Pre/ 2 Regular/3 Post)

schedule.nextGame                            # <Object> Next unplayed game info
schedule.lastGame                            # <Object> Previously completed game info
schedule.nextTeamId                          # <String> Team ID of next opponent # => 'OKC'
schedule.nextGameId                          # <Fixnum> Index of next unplayed game

schedule.pastGames[]                         # Completed Games : [Object]
schedule.futureGames[]                       # Upcoming Games  : [Object]

past     = schedule.pastGames                # Completed Games : <Navigator>
future   = schedule.futureGames              # Upcoming Games  : <Navigator>
Past Schedule Games as Structs
past = schedule.pastGames # Completed Games : <Navigator>
game = past.next          # <Object> Game info
game.team                 # Team ID
game.game_num             # Game # in Season
game.date                 # Game Date
game.home                 # Home?
game.opponent             # Opponent ID
game.win                  # Win?
game.team_score           # Team Score
game.opp_score            # Opponent Score
game.boxscore_id          # Boxscore ID
game.wins                 # Team Win Count
game.losses               # Team Loss Count
game.datetime             # Game DateTime
game.season_type          # Season Type
Future Schedule Games as Structs
future = schedule.futureGames  # Upcoming Games  : <Navigator>
game   = future.next           # <Object> Game info
game.team                      # Team ID
game.game_num                  # Game # in Season
game.date                      # Game Date
game.home                      # Home?
game.opponent                  # Opponent ID
game.time                      # Game Time
game.win                       # Win?
game.tv                        # Game on TV?
game.opp_score                 # Opponent Score
game.datetime                  # Game DateTime
game.season_type               # Season Type
Select a specific Season Type
preseason = es.schedule('BOS', season: 1)   # Get Preseason schedule
regular   = es.schedule('NYK', season: 2)   # Get Regular schedule
playoffs  = es.schedule('OKC', season: 3)   # Get Playoff schedule
Select Historic Schedule data

The year parameter should correspond to the year in which the season ended.

schedule = es.schedule('SAS', year: 2005)    # Data for 2004-05 Season

Chaining it all together

# Get a Boxscore from a past game
HoopScrape.schedule('OKC', season: 2).allGames[42].boxscore(:to_structs).awayPlayers.first.name

# Get a Roster from a Team ID
HoopScrape.boxscore(400827977).homeTotals[0].roster(:to_hashes).players.first[:name]

'cle'.roster(:to_structs).players.next.position

# Get a Schedule from a Team ID
HoopScrape.teamList.last[0].schedule(:to_hashes).lastGame.boxscore

'gsw'.schedule(:to_structs).lastGame.boxscore

Documentation

Available on RubyDoc.info or locally:

$ yard doc
$ yard server

Requirements

Ruby version

  • Ruby >= 1.9.3

Dependencies

  • Nokogiri ~> 1.6
  • Rake
  • minitest

Testing

$ rake

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/meissadia/hoopscrape


© 2016 Meissa Dia