'HTTP Error 400: Bad Request' with Python Bottlenose

Question:

I have the following file (associate.py)

import bottlenose
from bs4 import BeautifulSoup
from urllib2 import HTTPError

;; My real keys are here in reality, I just replaced them 
;; with the following because they are private.
credentials = {'access-key' : 'MY_ACCESS_KEY_GOES_HERE',
               'secret-key' : 'MY_SECRET_KEY_GOES_HERE',
               'associate-tag' : 'MY_ASSOCIATE_TAG'}

def error_handler(err):
  ex = err['exception']
  if isinstance(ex, HTTPError) and ex.code == 503:
    time.sleep(random.expovariate(0.1))
    return True

api = bottlenose.Amazon(credentials['access-key'],
                        credentials['secret-key'],
                        credentials['associate-tag'],
                        Parser=BeautifulSoup,
                        ErrorHandler=error_handler,
                        MaxQPS=0.9)

def get_item(asin):
  return api.ItemLookup(ItemId=asin,
                        ResponseGroup="Large,Images,ItemAttributes")

Here is the result of my Ipython session using the above:

In [1]: import associate

In [2]: associate.get_item("B003K1IJWC")
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
<ipython-input-2-31ffe6a959e1> in <module>()
----> 1 associate.get_item("B003K1IJWC")

/home/stephen/work/pantry/associate.pyc in get_item(asin)
     22 def get_item(asin):
     23   return api.ItemLookup(ItemId=asin,
---> 24                         ResponseGroup="Large,Images,ItemAttributes")

/home/stephen/work/pantry/bottlenose/api.pyc in __call__(self, **kwargs)
    240         # make the actual API call
    241         response = self._call_api(api_url,
--> 242                                   {'api_url': api_url, 'cache_url': cache_url})
    243 
    244         # decompress the response if need be

/home/stephen/work/pantry/bottlenose/api.pyc in _call_api(self, api_url, err_env)
    201                 else:
    202                     # the simple way
--> 203                     return urllib2.urlopen(api_request, timeout=self.Timeout)
    204             except:
    205                 if not self.ErrorHandler:

/usr/lib/python2.7/urllib2.pyc in urlopen(url, data, timeout)
    125     if _opener is None:
    126         _opener = build_opener()
--> 127     return _opener.open(url, data, timeout)
    128 
    129 def install_opener(opener):

/usr/lib/python2.7/urllib2.pyc in open(self, fullurl, data, timeout)
    408         for processor in self.process_response.get(protocol, []):
    409             meth = getattr(processor, meth_name)
--> 410             response = meth(req, response)
    411 
    412         return response

/usr/lib/python2.7/urllib2.pyc in http_response(self, request, response)
    521         if not (200 <= code < 300):
    522             response = self.parent.error(
--> 523                 'http', request, response, code, msg, hdrs)
    524 
    525         return response

/usr/lib/python2.7/urllib2.pyc in error(self, proto, *args)
    446         if http_err:
    447             args = (dict, 'default', 'http_error_default') + orig_args
--> 448             return self._call_chain(*args)
    449 
    450 # XXX probably also want an abstract factory that knows when it makes

/usr/lib/python2.7/urllib2.pyc in _call_chain(self, chain, kind, meth_name, *args)
    380             func = getattr(handler, meth_name)
    381 
--> 382             result = func(*args)
    383             if result is not None:
    384                 return result

/usr/lib/python2.7/urllib2.pyc in http_error_default(self, req, fp, code, msg, hdrs)
    529 class HTTPDefaultErrorHandler(BaseHandler):
    530     def http_error_default(self, req, fp, code, msg, hdrs):
--> 531         raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
    532 
    533 class HTTPRedirectHandler(BaseHandler):

HTTPError: HTTP Error 400: Bad Request

The odd thing is that the above request associate.get_item("B003K1IJWC") works just fine on my laptop, but fails as seen on my desktop.

My laptop is Crouton Ubuntu 12.04 with python 2.7.5, my Desktop is Ubuntu 14.04 with Python 2.7.6 . I am at quite a loss to explain why this works on one and not the other. If you have any guesses as to why the previous code does not work on the desktop, I would appreciate it. Is it a crypto problem with the signed request? I am just stumped and don’t know how to proceed. Thank you.

Asked By: Stephen Cagle

||

Answers:

After firing up wireshark and looking at the response, Amazon politely informs me that “Timezone Expired”.

Ok, that was a bit embarrassing. See, my desktop is dual booting windows 8 (for games) and Ubuntu. I have been having a lot of trouble with the clock skewing all over the place when I boot from one os to another. So, genius, I decided to just set my timezone to Reykjavik, Iceland (UTC offset +0), disable internet time synching, and manually set my time to the local time (on both windows and ubuntu). That fixed my time skewing problems.

Amazon does not like this. It says that the timestamp has expired on the request I make. So, I re-enabled internet syncing and set my time zone to Los Angeles, USA. Everything works.

Answered By: Stephen Cagle