cli_nbadraft2019 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/run +6 -0
- data/lib/cli_nbadraft19.rb +107 -0
- data/lib/player.rb +73 -0
- data/lib/scraper.rb +116 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9cfa23e6bc57d3e16c909d8fd6128f2bcd63e5d68d2ebfa5f35b547c5658dab2
|
4
|
+
data.tar.gz: 23825f0afda64ce7435cb18e37cb13df42c4d0ca4af2235ca2de1c028b35123d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 79fe1606c9fb483479febcce67acede3fa7b933c3058507d994553068d82a5258a3c98f5f550495586418ca7843a727df038ed180643218417544738b37387e7
|
7
|
+
data.tar.gz: 47799b3be73a3722b6369c36195ed950c07d92dd67bae8f17a76cee5d567c15997f05eefc87003764e9930019882946b87f8fcc0c3ea121c1abf0db960e038a2
|
data/bin/run
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
require_relative "../lib/scraper.rb"
|
6
|
+
require_relative "../lib/player.rb"
|
7
|
+
|
8
|
+
# Make URL a class constant
|
9
|
+
URL = "https://www.si.com/nba/2019/nba-draft-big-board-top-100-player-rankings"
|
10
|
+
|
11
|
+
# class for CLI for NBA Draft 19 application
|
12
|
+
class CLI_NBADraft19
|
13
|
+
# run method to first scrape, then put the results into a Player object
|
14
|
+
def run
|
15
|
+
playarray=Scraper.scrape_player(URL)
|
16
|
+
#binding.pry
|
17
|
+
|
18
|
+
# add players to Player class from playarray (player array)
|
19
|
+
Player.add_players(playarray)
|
20
|
+
|
21
|
+
# use the player_menu to give user a CLI
|
22
|
+
player_menu
|
23
|
+
end
|
24
|
+
|
25
|
+
# Player menu -- displays a basic menu and asks for a user choice
|
26
|
+
def player_menu
|
27
|
+
#call menu_marquee
|
28
|
+
menu_marquee
|
29
|
+
choice = 0
|
30
|
+
|
31
|
+
# keep asking for input until an X or x is received (for Exit)
|
32
|
+
until (choice == "X" || choice == 'x') do
|
33
|
+
puts "\nEnter choice>"
|
34
|
+
choice=gets.chomp
|
35
|
+
|
36
|
+
# case statement highlighting different choices for the menu
|
37
|
+
case choice
|
38
|
+
|
39
|
+
when "1" # choice 1: lists all players (not paginated)
|
40
|
+
Player.all.each_with_index {|player,i| puts "#{i+1} #{player.name}"}
|
41
|
+
when "2" # choice 2: ask for a player name and display more info for that player
|
42
|
+
# calls method for player display
|
43
|
+
player_submenu
|
44
|
+
when "3" # choice 3 calls method for submenu for searching on a player's name and display more info
|
45
|
+
# for that player
|
46
|
+
player_name_submenu
|
47
|
+
when "4"
|
48
|
+
# Choice 4 asks for player position to search on and lists all players that play a given position
|
49
|
+
puts "Enter player position"
|
50
|
+
posn_choice=gets.chomp
|
51
|
+
puts "\nThese are all players that play #{posn_choice}"
|
52
|
+
(Player.find_by_posn (posn_choice)).each {|player| puts "#{player.name}"}
|
53
|
+
when "5"
|
54
|
+
# Choice 5 Enter the school or club name
|
55
|
+
puts "Enter the school/club name"
|
56
|
+
sclub_choice = gets.chomp
|
57
|
+
puts "\nThese are the players from school or club: #{sclub_choice} :"
|
58
|
+
# calls class method for finding by school or club (sclub)
|
59
|
+
(Player.find_by_sclub (sclub_choice)).each {|player| puts "#{player.name}"}
|
60
|
+
when "X","x"
|
61
|
+
puts "\nExiting the CLI Program"
|
62
|
+
else
|
63
|
+
# calls method for displaying the menu again...displaying the choices for
|
64
|
+
menu_marquee
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
# method for executing code that asks for a player number
|
71
|
+
def player_submenu
|
72
|
+
# Asks for the player number in the Top 100 list
|
73
|
+
puts "Enter the number (e.g. 1, 2,...100) of the player>"
|
74
|
+
# uses get.chomp to get the player number, convert to integer
|
75
|
+
choice=gets.chomp.to_i
|
76
|
+
# finds the player
|
77
|
+
player=Player.all[choice-1]
|
78
|
+
# displays player_info (2 steps for easier debugging)
|
79
|
+
player.display_info
|
80
|
+
end
|
81
|
+
|
82
|
+
# method for executing code that asks for a player name and returns more detailed info on
|
83
|
+
# that player
|
84
|
+
def player_name_submenu
|
85
|
+
puts "Enter player name (can be only first or last name)>"
|
86
|
+
# get user input for name
|
87
|
+
name_choice = gets.chomp
|
88
|
+
# searches for first occurrence of name, displays player info
|
89
|
+
(Player.find_by_name (name_choice)).display_info
|
90
|
+
end
|
91
|
+
|
92
|
+
private #make menu_marquee so that it can't be called by other methods
|
93
|
+
# clears the screen and displays the user choices
|
94
|
+
def menu_marquee
|
95
|
+
system ("clear")
|
96
|
+
puts"---------------------------------------------------"
|
97
|
+
puts "Welcome to the 2019 NBA Draft Top 100 Prospect List"
|
98
|
+
puts "\tData (c) 2019 Sports Illustrated"
|
99
|
+
puts "(1) List Top 100 by name"
|
100
|
+
puts "(2) Get in-depth info about player by number on list"
|
101
|
+
puts "(3) Get in-depth info about player by name"
|
102
|
+
puts "(4) Search for All Players by Position"
|
103
|
+
puts "(5) Search for All Players by School/Club"
|
104
|
+
puts "X to Exit"
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
data/lib/player.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
#class Player for objects that each represent one player
|
6
|
+
|
7
|
+
|
8
|
+
class Player
|
9
|
+
|
10
|
+
# attr_accessors (macros for setter and getter methods) for different attribues of Player
|
11
|
+
|
12
|
+
attr_accessor :name,:position,:schoolclub,:class_year,:height,:weight,:age,:last_rank,:blurb,:rank
|
13
|
+
@@all=[]
|
14
|
+
|
15
|
+
# initialize a new instance of Player...reads in a player hash, creates a new instance
|
16
|
+
def initialize(phashrow)
|
17
|
+
|
18
|
+
phashrow.each do |key, val|
|
19
|
+
self.send("#{key}=", "#{val}")
|
20
|
+
end
|
21
|
+
# then shovels the instance into @@all
|
22
|
+
@@all << self
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# iterate through an array of player hashes: for each player, instantiate a new Player instance
|
27
|
+
def self.add_players(playerarray)
|
28
|
+
playerarray.each do |player_hash|
|
29
|
+
self.new(player_hash)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#return all Player elements
|
34
|
+
def self.all
|
35
|
+
@@all
|
36
|
+
end
|
37
|
+
|
38
|
+
#return first player by name
|
39
|
+
def self.find_by_name(name)
|
40
|
+
playr=@@all.find {|player| player.name.downcase.include?(name.downcase)}
|
41
|
+
# binding.pry
|
42
|
+
playr
|
43
|
+
end
|
44
|
+
|
45
|
+
# return an array of all players in the Player Class by school/club
|
46
|
+
def self.find_by_sclub(sclub)
|
47
|
+
@@all.select {|player| player.schoolclub == sclub}
|
48
|
+
end
|
49
|
+
|
50
|
+
#return an array of all players in the Player Class by a given position
|
51
|
+
|
52
|
+
def self.find_by_posn(posn)
|
53
|
+
@@all.select {|player| player.position.include?(posn)}
|
54
|
+
end
|
55
|
+
# displays detailed info for an instance of Player
|
56
|
+
def display_info
|
57
|
+
|
58
|
+
puts "\nPlayer Name: #{self.name}"
|
59
|
+
puts "Current Rank: #{self.rank}"
|
60
|
+
puts "Position: #{self.position}"
|
61
|
+
puts "School/Club: #{self.schoolclub}"
|
62
|
+
# binding.pry
|
63
|
+
#if there is no class_year, nothing is displayed
|
64
|
+
puts "Year: #{self.class_year}"
|
65
|
+
#
|
66
|
+
puts "Height/Weight: #{self.height}, #{self.weight} "
|
67
|
+
puts "Age: #{self.age}"
|
68
|
+
puts "Last rank: #{self.last_rank}"
|
69
|
+
puts "Scouting Report: #{self.blurb}"
|
70
|
+
puts "------------------------------------"
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/scraper.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'pry'
|
4
|
+
# require_relative '../lib/player.rb'
|
5
|
+
|
6
|
+
NBSP = 160.chr("UTF-8")
|
7
|
+
|
8
|
+
class Scraper
|
9
|
+
|
10
|
+
@@all=[]
|
11
|
+
|
12
|
+
# scrape the player data from url / html file
|
13
|
+
def self.scrape_player(url)
|
14
|
+
newarray=[]
|
15
|
+
# I decided to scrape twice because I found too many irregular HTML elements and an irregular number of elements between the headline block and the particular heading-container
|
16
|
+
# I was looking to scrape. Sometimes this included a second heading-container containing a NBSP or other elements.
|
17
|
+
# There was no easy way to go from the block containing the name to the correct heading-container block (maybe iterate until you can find the next correct heading-container block.)
|
18
|
+
|
19
|
+
#scrape the name, school, year, etc for all players
|
20
|
+
player_id_arr=scrape_page_head(url)
|
21
|
+
#scrape the height, weight, scouting report
|
22
|
+
player_info_arr=scrape_phys_meas(url)
|
23
|
+
|
24
|
+
# merge the two parts of data -- the player info and some of the additional data
|
25
|
+
newarray=(player_id_arr.zip (player_info_arr)).map do |row|
|
26
|
+
row[0].merge(row[1])
|
27
|
+
|
28
|
+
end
|
29
|
+
newarray
|
30
|
+
end
|
31
|
+
|
32
|
+
# the two scraper methods are made Class private so that they cannot be called from outside the Class
|
33
|
+
class << self
|
34
|
+
private
|
35
|
+
|
36
|
+
# had to scrape the data from the page in two passes because of too many irregularities in the HTML/CSS code
|
37
|
+
# first scrape the names
|
38
|
+
def scrape_page_head(url)
|
39
|
+
i=0
|
40
|
+
html = open(url)
|
41
|
+
# binding.pry
|
42
|
+
playerindex = Nokogiri::HTML(html)
|
43
|
+
|
44
|
+
# initialize array of player info hashes
|
45
|
+
playhasharr = []
|
46
|
+
|
47
|
+
# sxcrape to get all the player information headline-blocks (player name, position, school/club)
|
48
|
+
pheadarr=playerindex.css ("div.component.headline-block")
|
49
|
+
|
50
|
+
# put the name, position, school/club, year elements into a hash, then put the hash into an array
|
51
|
+
pheadarr.each do |phrow|
|
52
|
+
playhashrow={}
|
53
|
+
# create playhashrow -- a hash for name, position, school/club, year
|
54
|
+
|
55
|
+
# Get the name from div.head (div tag, class=head). The split(",").first is to remove a comma
|
56
|
+
# in the last name on the list
|
57
|
+
playhashrow[:name]=(phrow.css("div.text-block div.head").text).split(",").first
|
58
|
+
|
59
|
+
# schoolclub variable here actually is "position|schoolclub, year"
|
60
|
+
schoolclub=phrow.css("div.text-block div.subhead").text
|
61
|
+
# get the player's position here in the first part of schoolclub (originally named split1)
|
62
|
+
playhashrow[:position]=schoolclub.split(",").first
|
63
|
+
# split1=prevexp.split(",").last.split("|").first
|
64
|
+
|
65
|
+
# Get the school / club name from the headline block
|
66
|
+
playhashrow[:schoolclub]=schoolclub.split(",").last.split("|").first.strip
|
67
|
+
# Get the class year
|
68
|
+
class_year = schoolclub.split(",").last.split("|").last.strip
|
69
|
+
# The way that the schoolclub variabkle
|
70
|
+
if class_year !=playhashrow[:schoolclub]
|
71
|
+
playhashrow[:class_year]=class_year
|
72
|
+
|
73
|
+
end
|
74
|
+
# put the current rank into the player hash (to avoid having to search later)
|
75
|
+
playhashrow[:rank]=i+1
|
76
|
+
# binding.pry
|
77
|
+
playhasharr << playhashrow
|
78
|
+
# puts ("number #{i+1}")
|
79
|
+
i+=1
|
80
|
+
end
|
81
|
+
|
82
|
+
playhasharr
|
83
|
+
end
|
84
|
+
|
85
|
+
# need to scrape in two passes because of odd extra div nodes /lines in the HTML
|
86
|
+
def scrape_phys_meas(url)
|
87
|
+
html = open(url)
|
88
|
+
playerindex = Nokogiri::HTML(html)
|
89
|
+
containarr=playerindex.css ("div.component.heading-container")
|
90
|
+
|
91
|
+
containarr_mod=containarr.select {|prow| (prow.text).include?("Height")}
|
92
|
+
containhasharr=[]
|
93
|
+
containarr_mod.each do |pmeas_node|
|
94
|
+
row_hash={}
|
95
|
+
pm_node_text=pmeas_node.text
|
96
|
+
# binding.pry
|
97
|
+
htwt = pm_node_text.gsub(NBSP,"").strip
|
98
|
+
blurb=pmeas_node.next_element.text
|
99
|
+
|
100
|
+
row_hash[:height]=(htwt.split("|").first).split(":").last.strip
|
101
|
+
row_hash[:weight]=(htwt.split("|")[1]).split(":").last.strip
|
102
|
+
row_hash[:age]=((htwt.split("|")[2]).split(":").last.strip).to_i
|
103
|
+
lastitem=(htwt.split("|")[3])
|
104
|
+
if lastitem != nil # this is because most entries with no last rank have "Last Rank: NR" BUT one does not have any entries
|
105
|
+
row_hash[:last_rank]=lastitem.split(":").last.strip
|
106
|
+
# binding.pry
|
107
|
+
end
|
108
|
+
row_hash[:blurb] = blurb
|
109
|
+
containhasharr << row_hash
|
110
|
+
end
|
111
|
+
# binding.pry
|
112
|
+
containhasharr
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cli_nbadraft2019
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kane Koo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-06-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: CLI gem reads/scrapes Top NBA draft prospect page. Allows user to get
|
14
|
+
more in-depth information by name or by rank.
|
15
|
+
email: rubycoder@example.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/run
|
21
|
+
- lib/cli_nbadraft19.rb
|
22
|
+
- lib/player.rb
|
23
|
+
- lib/scraper.rb
|
24
|
+
homepage: https://rubygems.org/gems/cli_nbadraft2019
|
25
|
+
licenses:
|
26
|
+
- MIT
|
27
|
+
metadata: {}
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubygems_version: 3.0.3
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: CLI for reading SI's top 100 NBA draft prospect page
|
47
|
+
test_files: []
|