aftermarket_research 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/aftermarket_research +3 -0
- data/bin/runner.rb +4 -0
- data/lib/aftermarket_research.rb +3 -0
- data/lib/analyzer.rb +32 -0
- data/lib/environment.rb +9 -0
- data/lib/query.rb +26 -0
- data/lib/runner.rb +85 -0
- data/lib/scraper.rb +33 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e4ecca47e251a96412c5eca41997580b197ee3f6
|
4
|
+
data.tar.gz: 15c0bb6ff8f9d4fc08a05e988e39797ad7f47a7f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 113338a90aa8723ffd2792bc069ac97f6b86785d6867a94ed9f664075b992283f3588515c4d72931d14bf9a82cbb31079b475b44ea7fab9f3f69f8967d8e4750
|
7
|
+
data.tar.gz: e135e4ff8f0f141a584c9309b6d55ebbe934e0fff6a5e51dc6b4f12b6b3dbc68250b9524307d0d7557ffed23810b3d66cc15cc8cc5f1b3bffd988d10b73534fc
|
data/bin/runner.rb
ADDED
data/lib/analyzer.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
class Analyzer
|
2
|
+
|
3
|
+
attr_accessor :num_array
|
4
|
+
|
5
|
+
def initialize(num_array)
|
6
|
+
self.num_array = num_array
|
7
|
+
end
|
8
|
+
|
9
|
+
def mean
|
10
|
+
self.num_array.inject(&:+)/num_array.count
|
11
|
+
# [a, b, c].inject(&:+) # => ((a + b)+ c)+ d)
|
12
|
+
# [b, c, d].inject(a, &:+) # => ((a + b)+ c)+ d)
|
13
|
+
end
|
14
|
+
|
15
|
+
def median
|
16
|
+
sorted = self.num_array.sort!
|
17
|
+
sorted[sorted.count/2]
|
18
|
+
end
|
19
|
+
|
20
|
+
def mode
|
21
|
+
freq = self.num_array.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
|
22
|
+
self.num_array.max_by { |v| freq[v] }
|
23
|
+
end
|
24
|
+
|
25
|
+
def print_calculations
|
26
|
+
puts "Analysis complete, based on #{num_array.length} craigslist listings!"
|
27
|
+
puts "The mean price for your search is #{mean}"
|
28
|
+
puts "The median price for your search is #{median}"
|
29
|
+
puts "The mode price for your search is #{mode}"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/environment.rb
ADDED
data/lib/query.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class Query
|
2
|
+
|
3
|
+
attr_accessor :keyword, :search_query, :min_price, :max_price
|
4
|
+
|
5
|
+
def parse(query_string)
|
6
|
+
query_array = query_string.split(" ")
|
7
|
+
self.keyword = query_array.shift
|
8
|
+
self.max_price = query_array.pop.to_i
|
9
|
+
self.min_price = query_array.pop.to_i
|
10
|
+
self.search_query = query_array.join(" ")
|
11
|
+
return self
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_query?
|
15
|
+
Runner.keywords.keys.include?(self.keyword) && self.search_query.is_a?(String) && self.max_price.is_a?(Integer) && self.min_price.is_a?(Integer)
|
16
|
+
end
|
17
|
+
|
18
|
+
def keyword_to_url
|
19
|
+
if keyword == "nyc"
|
20
|
+
""
|
21
|
+
else
|
22
|
+
"/#{keyword}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/runner.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
class Runner
|
2
|
+
|
3
|
+
def self.keywords
|
4
|
+
{
|
5
|
+
"mnh" => "Manhattan",
|
6
|
+
"brk" => "Brooklyn",
|
7
|
+
"que" => "Queens",
|
8
|
+
"brx" => "The Bronx",
|
9
|
+
"stn" => "Staten Island",
|
10
|
+
"jsy" => "New Jersey",
|
11
|
+
"lgi" => "Long Island",
|
12
|
+
"wch" => "Westchester",
|
13
|
+
"fct" => "Fairfield",
|
14
|
+
"nyc" => "New York City",
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def interface
|
19
|
+
on = true
|
20
|
+
|
21
|
+
while on do
|
22
|
+
|
23
|
+
puts "======================================="
|
24
|
+
puts "Hello! Welcome to AfterMarket Research."
|
25
|
+
puts
|
26
|
+
puts "Please enter your query in the following format:"
|
27
|
+
puts "[location keyword] [product query] [min price] [max price]"
|
28
|
+
puts
|
29
|
+
puts "(Type 'help' for more information, and type 'exit' to exit)"
|
30
|
+
|
31
|
+
raw_user_command = gets.chomp.downcase
|
32
|
+
query = Query.new.parse(raw_user_command)
|
33
|
+
# binding.pry
|
34
|
+
if query.valid_query?
|
35
|
+
puts "running search..."
|
36
|
+
puts
|
37
|
+
scraper = Scraper.new(query)
|
38
|
+
scraper.scrape
|
39
|
+
if scraper.product_rows.length > 0
|
40
|
+
analyzer = Analyzer.new(scraper.number_extraction)
|
41
|
+
analyzer.print_calculations
|
42
|
+
# puts
|
43
|
+
# puts "Would you like to see your search results? (Y/n)"
|
44
|
+
# to_web = gets.chomp.downcase
|
45
|
+
# if to_web == "y"
|
46
|
+
# binding.pry
|
47
|
+
# scraper.open_url
|
48
|
+
# end
|
49
|
+
else
|
50
|
+
puts "Sorry, your search returned no results."
|
51
|
+
end
|
52
|
+
elsif query.keyword == "help"
|
53
|
+
help
|
54
|
+
elsif query.keyword == "exit"
|
55
|
+
exit
|
56
|
+
on = false
|
57
|
+
else
|
58
|
+
puts "Invalid Query! Please try again. Hit 'enter' to continue."
|
59
|
+
gets
|
60
|
+
"Invalid Query"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def help
|
66
|
+
puts "Run a craigslist search by including four query terms, as follows:"
|
67
|
+
puts
|
68
|
+
puts "FIRST, the area key -- #{Runner.keywords} "
|
69
|
+
# area_array = keywords.each_with_object([]) {|key, value, array| array << "#{key}: #{value}"}
|
70
|
+
puts
|
71
|
+
puts "NEXT, your actual search. Feel free to format however you like, but please write it in quotes."
|
72
|
+
puts
|
73
|
+
puts "Finally, your minimum and maximum prices, as integers. These will be used to narrow the search."
|
74
|
+
puts
|
75
|
+
puts "Here is a sample search: \"brk 'macbook pro' 200 800\" "
|
76
|
+
puts
|
77
|
+
puts "Hit 'enter' to continue."
|
78
|
+
gets
|
79
|
+
end
|
80
|
+
|
81
|
+
def exit
|
82
|
+
puts "Thanks for using AfterMarket Research! Go get that surplus value!"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/lib/scraper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class Scraper
|
2
|
+
|
3
|
+
attr_accessor :query, :product_rows, :encoded_url
|
4
|
+
|
5
|
+
def initialize(query_object)
|
6
|
+
self.query = query_object
|
7
|
+
self.product_rows = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def htmlify(string)
|
11
|
+
string.gsub(" ", "+")
|
12
|
+
end
|
13
|
+
|
14
|
+
def scrape
|
15
|
+
self.encoded_url = URI.encode("https://newyork.craigslist.org/search/sss#{query.keyword_to_url}?zoomToPosting=&catAbb=sss&query=#{htmlify(query.search_query)}&minAsk=#{query.min_price}&maxAsk=#{query.max_price}&sort=rel&excats=")
|
16
|
+
html = open(URI.parse(self.encoded_url)) # /sss?zo
|
17
|
+
cl_page = Nokogiri::HTML(html.read)
|
18
|
+
cl_page.css('div.content p.row').map do |product_row|
|
19
|
+
self.product_rows << product_row
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def number_extraction
|
24
|
+
self.product_rows.map do |product_row|
|
25
|
+
product_row.css('span.l2 span.price').text[1..-1].to_i
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def open_url # in development, not implemented
|
30
|
+
`open #{self.encoded_url}`
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aftermarket_research
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Kronovet
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
description: Research and analyze second-hand price information through Craiglist
|
28
|
+
email: kronovet@gmail.com
|
29
|
+
executables:
|
30
|
+
- aftermarket_research
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- bin/aftermarket_research
|
35
|
+
- bin/runner.rb
|
36
|
+
- lib/aftermarket_research.rb
|
37
|
+
- lib/analyzer.rb
|
38
|
+
- lib/environment.rb
|
39
|
+
- lib/query.rb
|
40
|
+
- lib/runner.rb
|
41
|
+
- lib/scraper.rb
|
42
|
+
homepage: http://rubygems.org/gems/aftermarket_research
|
43
|
+
licenses:
|
44
|
+
- GNU GPL V2
|
45
|
+
metadata: {}
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 2.2.1
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: Aftermarket Research from your Terminal
|
66
|
+
test_files: []
|