yelp4r 1.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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Tom Cocca
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,41 @@
1
+ yelp4r
2
+ ======
3
+
4
+ Yelp4r is a Ruby wrapper for the Yelp API. It utilizes the amazing HTTParty gem by John Nunemaker.
5
+ If you need to build an API wrapper that gem is an enourmous help, refer to the following:
6
+
7
+ http://railstips.org/2008/7/29/it-s-an-httparty-and-everyone-is-invited
8
+
9
+
10
+ Yelp API Docs
11
+ =========
12
+
13
+ http://www.yelp.com/developers/documentation
14
+
15
+ You will need to register for a Yelp API Key.
16
+
17
+
18
+ USAGE
19
+ ==========
20
+
21
+ See http://github.com/tcocca/yelp4r/tree/master/examples
22
+
23
+
24
+ INSTALLATION
25
+ ==========
26
+
27
+ sudo gem install yelp4r -s http://gemcutter.org
28
+
29
+
30
+ QUESTION/CONCERNS/COMMENTS
31
+ ==========
32
+
33
+ Send me an email through github or at tom dot cocca at gmail dot com
34
+ Feel free to fork and submit changes
35
+ There is a test suite built on rspec.
36
+
37
+
38
+ COPYRIGHT
39
+ =========
40
+
41
+ Copyright (c) 2008 Tom Cocca. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |s|
7
+ s.name = "yelp4r"
8
+ s.summary = %Q{Yelp API wrapper in Ruby}
9
+ s.email = "tom.cocca@gmail.com"
10
+ s.homepage = "http://github.com/tcocca/yelp4r"
11
+ s.description = "Simple Ruby wrapper for the Yelp API built on HTTParty with parsers for available Neighborhoods and Categories"
12
+ s.files = [
13
+ "README",
14
+ "LICENSE",
15
+ "Rakefile",
16
+ "VERSION.yml",
17
+ "lib/yelp4r.rb",
18
+ "lib/rubyify_keys.rb",
19
+ "lib/yelp4r/categories.rb",
20
+ "lib/yelp4r/client.rb",
21
+ "lib/yelp4r/neighborhood_search.rb",
22
+ "lib/yelp4r/neighborhoods.rb",
23
+ "lib/yelp4r/phone_search.rb",
24
+ "lib/yelp4r/response.rb",
25
+ "lib/yelp4r/review_search.rb",
26
+ "spec/rcov.opts",
27
+ "spec/spec.opts",
28
+ "spec/spec_helper.rb",
29
+ "spec/fixtures/categories.html",
30
+ "spec/fixtures/neighborhoods.html",
31
+ "spec/yelp4r/categories_spec.rb",
32
+ "spec/yelp4r/client_spec.rb",
33
+ "spec/yelp4r/neighborhood_search_spec.rb",
34
+ "spec/yelp4r/neighborhoods_spec.rb",
35
+ "spec/yelp4r/phone_search_spec.rb",
36
+ "spec/yelp4r/review_search_spec.rb",
37
+ "examples/yelp.rb"
38
+ ]
39
+ s.authors = ["Tom Cocca"]
40
+ s.add_dependency 'httparty'
41
+ s.add_dependency 'nokogiri'
42
+ s.add_dependency 'mash'
43
+ s.add_development_dependency "rspec"
44
+ end
45
+ Jeweler::GemcutterTasks.new
46
+ rescue LoadError
47
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
48
+ end
49
+
50
+ Rake::RDocTask.new do |rdoc|
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = 'yelp4r'
53
+ rdoc.options << '--line-numbers' << '--inline-source'
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
57
+
58
+ Dir['tasks/**/*.rake'].each { |t| load t }
59
+
60
+ task :default => :spec
data/VERSION.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 1
4
+ :minor: 1
5
+ :build:
data/examples/yelp.rb ADDED
@@ -0,0 +1,166 @@
1
+ require File.dirname(__FILE__) + '/../lib/yelp4r'
2
+
3
+ # Read the Yelp API Documentation
4
+ # http://www.yelp.com/developers/documentation
5
+
6
+ # Initialize a new Yelp Client
7
+ client = Yelp4r::Client.new('your_ywsid_key')
8
+
9
+
10
+ # Initialize a new Phone Search object
11
+ phone_search = Yelp4r::PhoneSearch.new(client)
12
+
13
+ # Phone search only does one thing, looks up a business by a phone number
14
+ results = phone_search.search_by_phone_number('1234567890')
15
+
16
+ # A response object is returned
17
+ # The response is either a Mash Object or an array of Mash objects, these are hashes that have been converted into OpenStruct-esque objects
18
+ # Also all keys have been rubyified, eg: results.body['camelCase] is now results.body.camel_case
19
+ # The following response methods are available to all Yelp searches in the 3 search classes
20
+ # PhoneSearch, ReveiwSearch, NeighborhoodSearch
21
+ if results.success?
22
+ puts results.data # Pure data of the repsonse
23
+ puts results.body # Entire body of the response
24
+ else
25
+ puts results.reponse_code # Yelp repsonse code
26
+ puts results.error_message # The text of the error message
27
+ end
28
+
29
+
30
+ # Initialize a Neighborhood Search Object
31
+ neigh_search = Yelp4r::NeighborhoodSearch.new(client)
32
+
33
+ # Search by a geocode point
34
+ # Pass a Lat and Long respectively
35
+ results = neigh_search.search_by_geocode(37.788022, -122.399797)
36
+
37
+ # Search by a location (with optional country code)
38
+ results = neigh_search.search_by_location('Boston, MA')
39
+ # or
40
+ results = neigh_search.search_by_location('Boston, MA', 'US')
41
+
42
+ # Again the response methods above apply here as well
43
+
44
+
45
+ # Initialize a Review Search Object
46
+ review_search = Yelp4r::ReviewSearch.new(client)
47
+
48
+ # Each search method for the review_search object takes the following optional parameters
49
+ # :category => this is a Yelp category term (see the categories list below)
50
+ # :term => this is a search term
51
+ # :num_biz_requested => a number 1 through 20 (default is 10)
52
+
53
+ # Search by a bouding box
54
+ # Pass a top left lat/long and bottom right lat/long
55
+ results = review_search.search_by_bounding_box(38, -122.6, 37.788022, -122.399797)
56
+ results = review_search.search_by_bounding_box(38, -122.6, 37.788022, -122.399797, :term => "bars", :num_biz_requested => 15)
57
+
58
+ # Search by geocode and radius
59
+ # This method accepts and optional param of
60
+ # :radius => max of 25
61
+ results = review_search.search_by_geocode_and_radius(37.788022, -122.399797)
62
+ results = review_search.search_by_geocode_and_radius(37.788022, -122.399797, :radius => .5)
63
+ results = review_search.search_by_geocode_and_radius(37.788022, -122.399797, :category => "bars", :radius => 1)
64
+
65
+ # Search by location
66
+ # This method accepts and optional params of:
67
+ # :radius => max of 25
68
+ # :cc => country code
69
+ results = review_search.search_by_location('Boston, MA')
70
+ results = review_search.search_by_location('Boston, MA', :num_biz_requested => 10)
71
+ results = review_search.search_by_location('Boston, MA', :radius => 5)
72
+ results = review_search.search_by_location('Boston, MA', :term => "doctors")
73
+
74
+ # Again the response methods above apply here as well
75
+
76
+
77
+ # Yelp4r provides a couple of html parsed lists powered by Hpricot
78
+ # This first is the list of neighborhoods
79
+ # see the page here: http://www.yelp.com/developers/documentation/neighborhood_list
80
+
81
+ # Create a new Neighborhoods object
82
+ neighborhoods = Yelp4r::Neighborhoods.new
83
+
84
+ # Get the list
85
+ # This list is designed to keep the parent and its children in a hash with the parent being the key and the children in an array
86
+ # which is generated by a recursive function
87
+ # see the spec_helper method yelp4r_test_neighs_list for an example.
88
+
89
+ puts neighborhoods.list
90
+
91
+ # There is also a method to return a list of <option> tags to be used in a select tag in HTML
92
+ # The method takes an optional string for selected or an array if used with a multiple select
93
+ # The value of the options return the full string of the location
94
+
95
+ puts neighborhoods.options_from_list
96
+ # or
97
+ puts neighborhoods.options_from_list('Deep Cove, Vancouver, BC, Canada')
98
+ # or
99
+ puts neighborhoods.options_from_list(['Vancouver, BC, Canada', 'Deep Cove, Vancouver, BC, Canada'])
100
+
101
+ # To use this in a form see the following example:
102
+
103
+ =begin
104
+ #controller
105
+
106
+ def index
107
+ @neighs = Yelp4r::Neighborhoods.new
108
+ end
109
+
110
+ #view - index.html.erb
111
+
112
+ <% form_tag "/yelp" do %>
113
+ <%= select_tag :neighs, @neighs.options_from_list(params[:neighs] || []) %>
114
+ <br />
115
+ <%= select_tag :mult_neighs, @neighs.options_from_list(params[:mult_neighs] || []), :multiple => true, :size => 10 %>
116
+ <br />
117
+ <%= submit_tag "submit" %>
118
+ <% end %>
119
+
120
+ =end
121
+
122
+
123
+ # The second parser is the list of categories.
124
+ # See the page here: http://www.yelp.com/developers/documentation/category_list
125
+
126
+ # Create a Categories object
127
+ categories = Yelp4r::Categories.new
128
+
129
+ # Get the list
130
+ # As above the list keeps parents and children
131
+ # However this list is different as there is a "display" value and the "input" or "parameter" value
132
+ # In the hash the key is the "input" value, there is a :display value and if the element has children their is a :children value that is an array
133
+ # see the spec_helper method yelp4r_test_cats_list
134
+
135
+ puts categories.list
136
+
137
+ # As with Neighborhoods there is also a method to return a list of <option> tags to be used in a select tag in HTML for Categories
138
+ # The method takes an optional string for selected or an array if used with a multiple select
139
+ # This works exactly the same as Neighborhoods
140
+
141
+ puts categories.options_from_list
142
+ # or
143
+ puts categories.options_from_list("beaches")
144
+ # or
145
+ puts categories.options_from_list(["diving", "dancestudio", "golf"])
146
+
147
+ # To use this in a form see the following example: (basically the same exact thing as neighborhoods)
148
+
149
+ =begin
150
+ #controller
151
+
152
+ def index
153
+ @cats = Yelp4r::Categories.new
154
+ end
155
+
156
+ #view - index.html.erb
157
+
158
+ <% form_tag "/yelp" do %>
159
+ <%= select_tag :cats, @cats.options_from_list(params[:cats] || []) %>
160
+ <br />
161
+ <%= select_tag :mult_cats, @cats.options_from_list(params[:mult_cats] || []), :multiple => true, :size => 10 %>
162
+ <br />
163
+ <%= submit_tag "submit" %>
164
+ <% end %>
165
+
166
+ =end
@@ -0,0 +1,29 @@
1
+ class Hash
2
+
3
+ # Converts all of the keys to strings, optionally formatting key name
4
+ def rubyify_keys!
5
+ keys.each{|k|
6
+ v = delete(k)
7
+ new_key = k.to_s.to_underscore!
8
+ self[new_key] = v
9
+ v.rubyify_keys! if v.is_a?(Hash)
10
+ v.each{|p| p.rubyify_keys! if p.is_a?(Hash)} if v.is_a?(Array)
11
+ }
12
+ self
13
+ end
14
+
15
+ end
16
+
17
+ class String
18
+
19
+ # converts a camel_cased string to a underscore string,
20
+ # Same way ActiveSupport does string.underscore
21
+ def to_underscore!
22
+ self.to_s.gsub(/::/, '/').
23
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
24
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
25
+ tr("-", "_").
26
+ downcase
27
+ end
28
+
29
+ end
@@ -0,0 +1,76 @@
1
+ module Yelp4r
2
+ class Categories
3
+
4
+ attr_accessor :parse_url
5
+
6
+ def initialize
7
+ @parse_url = "http://www.yelp.com/developers/documentation/category_list"
8
+ end
9
+
10
+ def list
11
+ require 'nokogiri'
12
+ require 'open-uri'
13
+ doc = Nokogiri::HTML(open(@parse_url))
14
+ html = doc.at("ul.attr-list")
15
+ neighborhoods = process_list(html)
16
+ return neighborhoods
17
+ end
18
+
19
+ def options_from_list(selected = [])
20
+ selected_opts = selected.collect {|s| s.strip}
21
+ process_options(list, selected_opts)
22
+ end
23
+
24
+ private
25
+
26
+ def process_list(item)
27
+ item.children.find_all{|t| t.name == "li"}.map do |child|
28
+ unless child.inner_text.nil?
29
+ if child.next_sibling
30
+ next_list = child.next_sibling.name == "ul" ? child.next_sibling : child.next_sibling.next_sibling
31
+ end
32
+ if next_list && !next_list.children.find_all{|c| c.name == "li"}.empty?
33
+ str_vals = decode_string(child.inner_text)
34
+ {str_vals[2] => {:display => str_vals[1], :children => process_list(next_list)}}
35
+ elsif child.name == "li"
36
+ str_vals = decode_string(child.inner_text)
37
+ {str_vals[2] => {:display => str_vals[1]}}
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def decode_string(str)
44
+ /(.*)\s\(([^\)]*)/.match(str)
45
+ end
46
+
47
+ def process_options(item, selected, depth = 0, options = [])
48
+ if item.is_a?(Hash)
49
+ depth += 1
50
+ item.each do |key, val|
51
+ opt_selected = selected.include?(key) ? ' selected="selected"' : ''
52
+ options << %(<option value="#{key}"#{opt_selected}>#{option_prefix(depth)}#{val[:display]}</option>)
53
+ process_options(val[:children], selected, depth, options) if val[:children]
54
+ end
55
+ depth -= 1
56
+ elsif item.is_a?(Array)
57
+ item.each do |i|
58
+ process_options(i, selected, depth, options)
59
+ end
60
+ end
61
+ return options
62
+ end
63
+
64
+ def option_prefix(depth)
65
+ s = ""
66
+ if depth > 1
67
+ (depth - 1).times do
68
+ s += "&nbsp;-"
69
+ end
70
+ s += "&nbsp;"
71
+ end
72
+ return s
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,13 @@
1
+ module Yelp4r
2
+ class Client
3
+
4
+ include HTTParty
5
+ base_uri 'api.yelp.com'
6
+ format :json
7
+
8
+ def initialize(ywsid)
9
+ self.class.default_params :ywsid => ywsid
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ module Yelp4r
2
+ class NeighborhoodSearch
3
+
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def search_by_geocode(lat, long)
11
+ options = {:lat => lat, :long => long}
12
+ process(options)
13
+ end
14
+
15
+ def search_by_location(location, cc = "")
16
+ options = {:location => location}
17
+ options.merge!({:cc => cc}) unless cc.blank?
18
+ process(options)
19
+ end
20
+
21
+ private
22
+
23
+ def process(options)
24
+ Response.new(@client.class.get('/neighborhood_search', :query => options))
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+ module Yelp4r
2
+ class Neighborhoods
3
+
4
+ attr_accessor :parse_url
5
+
6
+ def initialize
7
+ @parse_url = "http://www.yelp.com/developers/documentation/neighborhood_list"
8
+ end
9
+
10
+ def list
11
+ require 'nokogiri'
12
+ require 'open-uri'
13
+ doc = Nokogiri::HTML(open(@parse_url))
14
+ html = doc.at("ul.attr-list")
15
+ neighborhoods = process_list(html)
16
+ return neighborhoods
17
+ end
18
+
19
+ def options_from_list(selected = [])
20
+ selected_opts = selected.collect {|s| s.strip}
21
+ process_options(list, selected_opts)
22
+ end
23
+
24
+ private
25
+
26
+ def process_list(item)
27
+ item.children.find_all{|t| t.name == "li"}.map do |child|
28
+ unless child.inner_text.nil?
29
+ if child.next_sibling
30
+ next_list = child.next_sibling.name == "ul" ? child.next_sibling : child.next_sibling.next_sibling
31
+ end
32
+ if next_list && !next_list.children.find_all{|c| c.name == "li"}.empty?
33
+ {child.inner_text => process_list(next_list)}
34
+ elsif child.name == "li"
35
+ child.inner_text
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def process_options(item, selected, depth = 0, options = [], parent = "")
42
+ if item.is_a?(Hash)
43
+ depth += 1
44
+ item.each do |key, val|
45
+ unless parent.blank?
46
+ opt = "#{key}, #{parent}"
47
+ else
48
+ opt = key
49
+ end
50
+ opt_selected = selected.include?(opt) ? ' selected="selected"' : ''
51
+ options << %(<option value="#{opt}"#{opt_selected}>#{option_prefix(depth)}#{key}</option>)
52
+ process_options(val, selected, depth, options, opt)
53
+ end
54
+ depth -= 1
55
+ elsif item.is_a?(Array)
56
+ item.each do |i|
57
+ process_options(i, selected, depth, options, parent)
58
+ end
59
+ else
60
+ depth += 1
61
+ unless parent.blank?
62
+ opt = "#{item}, #{parent}"
63
+ else
64
+ opt = item
65
+ end
66
+ opt_selected = selected.include?(opt) ? ' selected="selected"' : ''
67
+ options << %(<option value="#{opt}"#{opt_selected}>#{option_prefix(depth)}#{item}</option>)
68
+ depth -= 1
69
+ end
70
+ return options
71
+ end
72
+
73
+ def option_prefix(depth)
74
+ s = ""
75
+ if depth > 1
76
+ (depth - 1).times do
77
+ s += "&nbsp;-"
78
+ end
79
+ s += "&nbsp;"
80
+ end
81
+ return s
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,21 @@
1
+ module Yelp4r
2
+ class PhoneSearch
3
+
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def search_by_phone_number(phone)
11
+ process({:phone => phone})
12
+ end
13
+
14
+ private
15
+
16
+ def process(options)
17
+ Response.new(@client.class.get('/phone_search', :query => options))
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ module Yelp4r
2
+ class Response
3
+
4
+ attr_accessor :body
5
+
6
+ def initialize(response)
7
+ @body = mash_response(response)
8
+ end
9
+
10
+ def response_code
11
+ @body.message.code
12
+ end
13
+
14
+ def success?
15
+ response_code == 0
16
+ end
17
+
18
+ def error_message
19
+ @body.message.text unless success?
20
+ end
21
+
22
+ def data
23
+ if !@body.businesses.blank?
24
+ @body.businesses
25
+ elsif !@body.neighborhoods.blank?
26
+ @body.neighborhoods
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def mash_response(response)
33
+ if response.is_a?(Array)
34
+ @body = []
35
+ response.each do |b|
36
+ if b.is_a?(Hash)
37
+ @body << Mash.new(b.rubyify_keys!)
38
+ else
39
+ @body << b
40
+ end
41
+ end
42
+ elsif response.is_a?(Hash)
43
+ @body = Mash.new(response.rubyify_keys!)
44
+ else
45
+ @body = response
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,33 @@
1
+ module Yelp4r
2
+ class ReviewSearch
3
+
4
+ attr_accessor :client
5
+
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def search_by_bounding_box(tl_lat, tl_long, br_lat, br_long, optional = {})
11
+ options = {:tl_lat => tl_lat, :tl_long => tl_long, :br_lat => br_lat, :br_long => br_long}
12
+ process(options, optional)
13
+ end
14
+
15
+ def search_by_geocode_and_radius(lat, long, optional = {})
16
+ options = {:lat => lat, :long => long}
17
+ process(options, optional)
18
+ end
19
+
20
+ def search_by_location(location, optional = {})
21
+ options = {:location => location}
22
+ process(options, optional)
23
+ end
24
+
25
+ private
26
+
27
+ def process(options, optional)
28
+ options.merge!(optional) unless optional.blank?
29
+ Response.new(@client.class.get('/business_review_search', :query => options))
30
+ end
31
+
32
+ end
33
+ end
data/lib/yelp4r.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'httparty'
3
+ require 'mash'
4
+ require 'rubyify_keys'
5
+
6
+ require 'yelp4r/client'
7
+ require 'yelp4r/phone_search'
8
+ require 'yelp4r/neighborhood_search'
9
+ require 'yelp4r/review_search'
10
+ require 'yelp4r/response'
11
+ require 'yelp4r/neighborhoods'
12
+ require 'yelp4r/categories'
@@ -0,0 +1,40 @@
1
+ <html>
2
+ <body>
3
+ <div>
4
+ <div>
5
+ <ul class="attr-list">
6
+ <li>Active Life (active)</li>
7
+ <ul>
8
+ <li>Amusement Parks (amusementparks)</li>
9
+ <li>Fitness & Instruction (fitness)</li>
10
+ <ul>
11
+ <li>Dance Studios (dancestudio)</li>
12
+ <li>Gyms (gyms)</li>
13
+ <li>Martial Arts (martialarts)</li>
14
+ </ul>
15
+ </ul>
16
+ <li>Automotive (auto)</li>
17
+ <ul>
18
+ <li>Auto Detailing (auto_detailing)</li>
19
+ <li>Auto Glass Services (autoglass)</li>
20
+ </ul>
21
+ <li>Shopping (shopping)</li>
22
+ <ul>
23
+ <li>Home & Garden (homeandgarden)</li>
24
+ <ul>
25
+ <li>Appliances (appliances)</li>
26
+ <li>Furniture Stores (furniture)</li>
27
+ </ul>
28
+ <li>Sporting Goods (sportgoods)</li>
29
+ <ul>
30
+ <li>Bikes (bikes)</li>
31
+ <li>Outdoor Gear (outdoorgear)</li>
32
+ <li>Sports Wear (sportswear)</li>
33
+ </ul>
34
+ <li>Thrift Stores (thrift_stores)</li>
35
+ </ul>
36
+ </ul>
37
+ </div>
38
+ </div>
39
+ </body>
40
+ </html>