aftermarket_research 1.0.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/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: []
|