Class: Alexandria::BookProviders::BarnesAndNobleProvider

Inherits:
WebsiteBasedProvider show all
Includes:
Logging
Defined in:
lib/alexandria/book_providers/barnes_and_noble.rb

Constant Summary collapse

SITE =
'http://www.barnesandnoble.com'
BASE_ISBN_SEARCH_URL =
'http://www.barnesandnoble.com/s/%s'
BASE_SEARCH_URL =
'http://search.barnesandnoble.com/booksearch' \
'/results.asp?%s=%s'

Instance Attribute Summary

Attributes inherited from AbstractProvider

#fullname, #name, #prefs

Instance Method Summary collapse

Methods included from Logging

included, #log

Methods inherited from WebsiteBasedProvider

#html_to_doc, #text_of

Methods inherited from AbstractProvider

#<=>, abstract?, #abstract?, #action_name, #enabled, #reinitialize, #remove, #toggle_enabled, #transport, unabstract, #variable_name

Constructor Details

#initializeBarnesAndNobleProvider

type, term



47
48
49
50
51
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 47

def initialize
  super('BarnesAndNoble', 'BarnesAndNoble')
  @agent = nil
  prefs.read
end

Instance Method Details

#agentObject



53
54
55
56
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 53

def agent
  @agent ||= Alexandria::WWWAgent.new
  @agent
end

#create_search_uri(search_type, search_term) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 101

def create_search_uri(search_type, search_term)
  (search_type_code = {
    SEARCH_BY_AUTHORS => 'ATH',
    SEARCH_BY_TITLE => 'TTL',
    SEARCH_BY_KEYWORD => 'WRD' # SEARCH_BY_PUBLISHER => 'PBL' # not implemented
  }[search_type]) || ''
  if search_type == SEARCH_BY_ISBN
    BASE_ISBN_SEARCH_URL % Library.canonicalise_ean(search_term) # isbn-13
  else
    search_term_encoded = CGI.escape(search_term)
    format(BASE_SEARCH_URL, search_type_code, search_term_encoded)
  end
end

#fetch_redirectly(uri_str, limit = 5) ⇒ Object

Raises:



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 58

def fetch_redirectly(uri_str, limit = 5)
  raise NoResultsError, 'HTTP redirect too deep' if limit.zero?
  if limit < 10
    sleep 0.1
    log.debug { "Redirectly :: #{uri_str}" }
  else
    log.debug { "Fetching   :: #{uri_str}" }
  end
  response = agent.get(uri_str)
  log.debug { response.inspect }
  case response
  when Net::HTTPSuccess     then response
  when Net::HTTPRedirection then
    redirect = URI.parse response['Location']
    redirect = URI.parse(uri_str) + redirect if redirect.relative?
    fetch_redirectly(redirect.to_s, (limit - 1))
  else
    response.error!
  end
end

#get_book_from_search_result(result) ⇒ Object



115
116
117
118
119
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 115

def get_book_from_search_result(result)
  log.debug { "Fetching book from #{result[:url]}" }
  html_data = transport.get_response(URI.parse(result[:url]))
  parse_result_data(html_data.body)
end

#parse_result_data(html, _search_isbn = nil, _recursing = false) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 147

def parse_result_data(html, _search_isbn = nil, _recursing = false)
  doc = html_to_doc(html)
  begin
    book_data = {}

    dl = (doc / 'dl').first
    dts = dl.children_of_type('dt')
    dts.each do |dt|
      value = dt.next_sibling.inner_text
      case dt.inner_text
      when /ISBN-13/
        book_data[:isbn] = Library.canonicalise_ean(value)
      when /Publisher/
        book_data[:publisher] = value
      when /Publication data/
        value =~ /\d{2}.\d{2}.(\d{4})/
        year = Regexp.last_match[1]
        book_data[:publisher] = year
      end
    end

    meta = doc / 'meta'
    meta.each do |it|
      attrs = it.attributes
      property = attrs['property']
      next unless property
      case property
      when 'og:title'
        book_data[:title] = attrs['content']
      when 'og:image'
        book_data[:image_url] = attrs['content']
      end
    end

    author_links = doc / 'span.contributors a'
    authors = author_links.map(&:inner_text)
    book_data[:authors] = authors

    book_data[:binding] = ''
    selected_format = (doc / '#availableFormats li.selected a.tabTitle').first
    book_data[:binding] = selected_format.inner_text if selected_format

    book = Book.new(book_data[:title], book_data[:authors],
                    book_data[:isbn], book_data[:publisher],
                    book_data[:publication_year],
                    book_data[:binding])
    return [book, book_data[:image_url]]
  rescue => ex
    raise ex if ex.instance_of? NoResultsError
    trace = ex.backtrace.join("\n> ")
    log.warn {
      'Failed parsing search results for BarnesAndNoble ' \
      "#{ex.message} #{trace}"
    }
    raise NoResultsError
  end
end

#parse_search_result_data(html) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 121

def parse_search_result_data(html)
  doc = html_to_doc(html)
  book_search_results = []
  begin
    result_divs = doc / 'div[@class*="book-container"]'
    result_divs.each do |div|
      result = {}
      # img = div % 'div.book-image/a/img'
      # result[:image_url] = img['src'] if img
      title_header = div % 'h2'
      title_links = title_header / 'a'
      result[:title] = title_links.first.inner_text
      result[:url] = title_links.first['href']

      book_search_results << result
    end
  rescue => ex
    trace = ex.backtrace.join("\n> ")
    log.warn {
      'Failed parsing search results for Barnes & Noble ' \
      "#{ex.message} #{trace}"
    }
  end
  book_search_results
end

#search(criterion, type) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 79

def search(criterion, type)
  req = create_search_uri(type, criterion)
  puts "Requesting #{req}" if $DEBUG
  html_data = fetch_redirectly(req)

  if type == SEARCH_BY_ISBN
    parse_result_data(html_data.body, criterion)
  else
    results = parse_search_result_data(html_data.body)
    raise NoResultsError if results.empty?

    results.map { |result| get_book_from_search_result(result) }
  end
end

#url(book) ⇒ Object



94
95
96
97
98
99
# File 'lib/alexandria/book_providers/barnes_and_noble.rb', line 94

def url(book)
  create_search_uri(SEARCH_BY_ISBN, book.isbn)
rescue => ex
  log.warn { "Cannot create url for book #{book}; #{ex.message}" }
  nil
end