goodreads-books 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/goodreads-books +7 -0
- data/config/environment.rb +9 -0
- data/lib/goodreads_books.rb +5 -0
- data/lib/goodreads_books/book.rb +70 -0
- data/lib/goodreads_books/cli.rb +95 -0
- data/lib/goodreads_books/scraper.rb +77 -0
- data/lib/goodreads_books/version.rb +3 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0e1b1a9e5fb954dad3a7677af78b522a95f6fcbddf4b9d762273361400f7d0b0
|
4
|
+
data.tar.gz: d8c0304c8b443208b591e9abdb2ad906bcb2afecf2b12a8a409561421e1a0eea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 78d5ec945a11ba692ed363281243d73cb6918269619e9a8b8b2d80c338e7e21f9875cce15d12bc45ae02a217596c6633fe20125df05f99860c78e7eb2f8c0afb
|
7
|
+
data.tar.gz: b3ca162e6398e94bbba4000fb32cfca6b2080e0efe77adb10427edfb0e099b5cc89ead21f4eddc7cb31ac748af07c6b990a0c24adcaa1d0f0e338f3a01bcdf7d
|
data/bin/goodreads-books
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'colorize'
|
4
|
+
require 'pry'
|
5
|
+
|
6
|
+
require_relative '../lib/goodreads_books/version'
|
7
|
+
require_relative '../lib/goodreads_books/cli'
|
8
|
+
require_relative '../lib/goodreads_books/scraper'
|
9
|
+
require_relative '../lib/goodreads_books/book'
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class GoodreadsBooks::Book
|
2
|
+
attr_accessor :awards_year, :category, :title, :author, :vote, :description, :cate_url, :url
|
3
|
+
|
4
|
+
BASE_URL = "https://www.goodreads.com"
|
5
|
+
|
6
|
+
@@all = []
|
7
|
+
|
8
|
+
def initialize(attributes)
|
9
|
+
attributes.each do |attr_name, attr_value|
|
10
|
+
self.send("#{attr_name}=", attr_value)
|
11
|
+
end
|
12
|
+
end #-- initialize --
|
13
|
+
|
14
|
+
def self.new_from_web_page(book_hash)
|
15
|
+
book = new(book_hash)
|
16
|
+
book.save
|
17
|
+
end #-- self.new_from_web_page --
|
18
|
+
|
19
|
+
def self.all
|
20
|
+
@@all
|
21
|
+
end #-- self.all --
|
22
|
+
|
23
|
+
def save
|
24
|
+
self.class.all << self
|
25
|
+
end #-- save --
|
26
|
+
|
27
|
+
def self.all_by_year(awards_year)
|
28
|
+
all.select { |book| book.awards_year == awards_year }
|
29
|
+
end #-- find_by_year --
|
30
|
+
|
31
|
+
def author
|
32
|
+
get_book_details if !@author
|
33
|
+
@author
|
34
|
+
end #-- author --
|
35
|
+
|
36
|
+
def vote
|
37
|
+
get_book_details if !@vote
|
38
|
+
@vote
|
39
|
+
end #-- vote --
|
40
|
+
|
41
|
+
def description
|
42
|
+
get_book_details if !@description
|
43
|
+
@description
|
44
|
+
end #-- description --
|
45
|
+
|
46
|
+
def url
|
47
|
+
get_book_details if !@url
|
48
|
+
@url
|
49
|
+
end #-- url --
|
50
|
+
|
51
|
+
def get_book_details
|
52
|
+
# Next level of scraping (get details of winner book within each category_url)
|
53
|
+
book_doc = Nokogiri::HTML(open(self.cate_url))
|
54
|
+
|
55
|
+
self.vote = book_doc.css(".gcaRightContainer .gcaWinnerHeader").text.split(" ")[1]
|
56
|
+
self.author = book_doc.css(".gcaRightContainer h3 .gcaAuthor a.authorName").text
|
57
|
+
self.url = "#{BASE_URL}#{book_doc.css(".gcaRightContainer h3 a.winningTitle").attr("href").text}"
|
58
|
+
self.description = book_doc.css(".gcaRightContainer .readable.stacked").text.strip
|
59
|
+
|
60
|
+
#binding.pry
|
61
|
+
end #-- get_book_details --
|
62
|
+
|
63
|
+
def self.populate_book_details(award_year)
|
64
|
+
all_by_year(awards_year).each do |book|
|
65
|
+
book.get_book_details
|
66
|
+
end
|
67
|
+
#binding.pry
|
68
|
+
end #-- self.populate_book_details --
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class GoodreadsBooks::CLI
|
2
|
+
|
3
|
+
# This application only works for year 2010 to current year - 1.
|
4
|
+
# Goodreads Choice Awards Winner 2009 page setup differs from 2010 onwards.
|
5
|
+
BASE_YEAR = 2010
|
6
|
+
END_YEAR = Time.now.year - 1
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@@choice_awards = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
system "clear"
|
14
|
+
puts ""
|
15
|
+
puts "---------- Welcome to Goodreads Choice Awards Books ----------"
|
16
|
+
puts " ----------------------------------------"
|
17
|
+
puts ""
|
18
|
+
|
19
|
+
load_choice_awards
|
20
|
+
|
21
|
+
main_menu
|
22
|
+
end #-- call --
|
23
|
+
|
24
|
+
def load_choice_awards(awards_year = nil)
|
25
|
+
if awards_year == nil
|
26
|
+
puts "Loading The Latest Winners of Goodreads Choice Awards Books..."
|
27
|
+
else
|
28
|
+
puts "Loading The Winners of #{awards_year} Goodreads Choice Awards Books..."
|
29
|
+
end
|
30
|
+
|
31
|
+
@choice_awards = GoodreadsBooks::Scraper.find_or_create_by_year(awards_year)
|
32
|
+
|
33
|
+
@book_count = GoodreadsBooks::Book.all_by_year(@choice_awards.awards_year).count
|
34
|
+
end #-- load_choice_awards --
|
35
|
+
|
36
|
+
def main_menu
|
37
|
+
system "clear"
|
38
|
+
|
39
|
+
input = nil
|
40
|
+
while input != "exit"
|
41
|
+
list_books
|
42
|
+
|
43
|
+
puts ""
|
44
|
+
puts "Enter a number to view details of the book, or select another Choice Awards year (2010 onwards)."
|
45
|
+
puts "Type 'exit' to end the application."
|
46
|
+
input = gets.strip
|
47
|
+
|
48
|
+
if input.downcase == "exit"
|
49
|
+
break
|
50
|
+
elsif input.to_i.between?(1, @book_count)
|
51
|
+
book = GoodreadsBooks::Book.all_by_year(@choice_awards.awards_year)[input.to_i - 1]
|
52
|
+
view_book(book)
|
53
|
+
elsif input.to_i.between?(BASE_YEAR, END_YEAR)
|
54
|
+
|
55
|
+
load_choice_awards(input.to_i)
|
56
|
+
else
|
57
|
+
puts ""
|
58
|
+
puts "Please enter a number between 1 and #{@book_count} or a valid Choice Awards year".colorize(:red)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
puts ""
|
63
|
+
puts "Thank you for using Goodreads Choice Awards Books."
|
64
|
+
end #-- main_menu --
|
65
|
+
|
66
|
+
def list_books
|
67
|
+
puts ""
|
68
|
+
puts "---------- #{@choice_awards.awards_year} Goodreads Choice Awards Books ----------"
|
69
|
+
puts ""
|
70
|
+
|
71
|
+
GoodreadsBooks::Book.all_by_year(@choice_awards.awards_year).each.with_index(1) do |book, index|
|
72
|
+
puts "#{index}. #{book.category} - #{book.title}"
|
73
|
+
end
|
74
|
+
end #-- display_books --
|
75
|
+
|
76
|
+
def view_book(book)
|
77
|
+
puts ""
|
78
|
+
puts "---------- #{@choice_awards.awards_year} BEST #{book.category.upcase} Winner ----------"
|
79
|
+
puts ""
|
80
|
+
puts "Title: #{book.title}"
|
81
|
+
puts "Author: #{book.author}"
|
82
|
+
puts "Votes: #{book.vote}"
|
83
|
+
puts ""
|
84
|
+
puts " --- Overview ---"
|
85
|
+
puts "#{book.description}"
|
86
|
+
|
87
|
+
puts ""
|
88
|
+
puts "Would you like to visit Goodreads website to view this book? Enter Y or N".colorize(:green)
|
89
|
+
input = gets.strip.downcase
|
90
|
+
|
91
|
+
if input.downcase == "y"
|
92
|
+
system("open #{book.url}")
|
93
|
+
end
|
94
|
+
end #-- view_book --
|
95
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class GoodreadsBooks::Scraper
|
2
|
+
attr_accessor :awards_year, :main_url
|
3
|
+
|
4
|
+
BASE_URL = "https://www.goodreads.com"
|
5
|
+
PAGE_URL = "/choiceawards"
|
6
|
+
|
7
|
+
@@all = []
|
8
|
+
|
9
|
+
def initialize(awards_year = nil)
|
10
|
+
@awards_year = awards_year
|
11
|
+
end #-- initialize --
|
12
|
+
|
13
|
+
def self.all
|
14
|
+
@@all
|
15
|
+
end #-- self.all --
|
16
|
+
|
17
|
+
def save
|
18
|
+
self.class.all << self
|
19
|
+
end #-- save --
|
20
|
+
|
21
|
+
def self.find_or_create_by_year(awards_year = nil)
|
22
|
+
if !(choice_awards = find_by_year(awards_year))
|
23
|
+
choice_awards = create(awards_year)
|
24
|
+
choice_awards.scrape_books
|
25
|
+
end
|
26
|
+
find_by_year(choice_awards.awards_year)
|
27
|
+
end #-- self.find_or_create_by_year --
|
28
|
+
|
29
|
+
def self.find_by_year(awards_year = nil)
|
30
|
+
all.detect { |r| r.awards_year == awards_year }
|
31
|
+
end #-- self.find_by_year --
|
32
|
+
|
33
|
+
def self.create(awards_year = nil)
|
34
|
+
#choice_awards = new(awards_year)
|
35
|
+
#choice_awards.save
|
36
|
+
choice_awards = new(awards_year).tap { |s| s.save }
|
37
|
+
|
38
|
+
# if awards_year is missing from the url,
|
39
|
+
# goodreads.com defaults to latest choice awards
|
40
|
+
# /best-books-#{latest awards year}"
|
41
|
+
if awards_year == nil
|
42
|
+
choice_awards.main_url = "#{BASE_URL}#{PAGE_URL}"
|
43
|
+
doc = Nokogiri::HTML(open(choice_awards.main_url))
|
44
|
+
choice_awards.awards_year = doc.css("head title").text.split(" ")[2].to_i
|
45
|
+
else
|
46
|
+
choice_awards.main_url = "#{BASE_URL}#{PAGE_URL}/best-books-#{awards_year}"
|
47
|
+
choice_awards.awards_year = awards_year
|
48
|
+
end
|
49
|
+
|
50
|
+
choice_awards # return instance of scraper
|
51
|
+
end #-- self.create --
|
52
|
+
|
53
|
+
def scrape_books
|
54
|
+
doc = Nokogiri::HTML(open(@main_url))
|
55
|
+
|
56
|
+
# Category winners page
|
57
|
+
doc.css(".category.clearFix").each do |category|
|
58
|
+
cate_name = category.css("h4").text
|
59
|
+
cate_url = category.css("a").attr("href").text
|
60
|
+
cate_title = category.css("img").attr("alt").text
|
61
|
+
# cate_book_id = category.css("input")[2].attr("value") # don't need to keep book_id
|
62
|
+
|
63
|
+
# for each winner element, assemble the book_details hash
|
64
|
+
book_details = {
|
65
|
+
:awards_year => @awards_year,
|
66
|
+
:category => cate_name,
|
67
|
+
:title => cate_title,
|
68
|
+
:cate_url => "#{BASE_URL}#{cate_url}"
|
69
|
+
}
|
70
|
+
|
71
|
+
GoodreadsBooks::Book.new_from_web_page(book_details)
|
72
|
+
end
|
73
|
+
|
74
|
+
#binding.pry
|
75
|
+
end #-- scrape_books --
|
76
|
+
|
77
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: goodreads-books
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ni Chia
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-09-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: nokogiri
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: colorize
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Goodreads CLI gem is a command line application that lists the yearly
|
98
|
+
Goodreads Choice Awards Winning Books.
|
99
|
+
email:
|
100
|
+
- nichia@gmail.com
|
101
|
+
executables:
|
102
|
+
- goodreads-books
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- bin/goodreads-books
|
107
|
+
- config/environment.rb
|
108
|
+
- lib/goodreads_books.rb
|
109
|
+
- lib/goodreads_books/book.rb
|
110
|
+
- lib/goodreads_books/cli.rb
|
111
|
+
- lib/goodreads_books/scraper.rb
|
112
|
+
- lib/goodreads_books/version.rb
|
113
|
+
homepage: https://github.com/nichia/goodreads_books.git
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
- lib/goodreads_books
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 2.7.6
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: Goodreads Choice Awards Books
|
138
|
+
test_files: []
|