LearningSlowly
LearningSlowly

Reputation: 9431

Google-maps for Business Authentication Issue with Python-Requests

I am having issues incorporating my new Business credentials into the Python-Requests module I had previously been using with the free user API key to create requests.

I have been following the authentication directions given by Google - (1) parsing the base URL into its path and query parts (2) decoding the private key to binary (3) creating a signature using this private key and the URL-encoded string using HMAC SHA1 (4) encoding the binary signature using base64

My code is as follows -

import requests
import csv
import json
import urlparse
import hashlib
import base64
import hmac
from pprint import pprint

url_raw = 'https://maps.googleapis.com/maps/api/directions/json&client=REMOVED'
Private_Key = 'REMOVED'

with open('./Origins.csv', 'rU') as csvfile:
    reader = csv.DictReader(csvfile)
    origincoords = ['{Y},{X}'.format(**row) for row in reader]

with open('./Destinations.csv', 'rU') as csvfile:
    reader = csv.DictReader(csvfile)
    destinationcoords = ['{Y},{X}'.format(**row) for row in reader]

results=[] 

url = urlparse.urlparse('url_raw')

url_to_sign = url.path + '?' + url.query

decodedKey = base64.urlsafe_b64decode(Private_Key)

signature = hmac.new(decodedKey, url_to_sign, hashlib.sha1)

encodedSignature = base64.urlsafe_b64encode(signature.digest())

originalUrl = url.scheme + "://" + url.netloc + url.path + '?' + url.query

Full_URL = originalUrl + "&signature=" + encodedSignature

for i in range(0,(len(origincoords))):
    params ={'origin': origincoords[i],
          'destination': destinationcoords[i],
          'encodedSignature': encodedSignature}

    directions = requests.get(Full_URL, params=params).json()
    results.append(directions)

At this point I parse the results of the relevant information for my research. Thanks to @fredtantini for the help here previously.

The error message I am currently getting relates to a requests.exception, stating that I am not using a valid schema. However, in my code I have included the url.scheme in the originalUrl variable, which makes up part of the Full_URL which is used in the requests generation.

Upvotes: 1

Views: 155

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121724

You are not parsing the value of url_raw. You are parsing the string with the contents 'url_raw':

url = urlparse.urlparse('url_raw')

Remove the quotes around url_raw, you want to parse the value associated with the variable, not a string that just happens to contain text that is equal to the variable name.

Demo:

>>> import urlparse
>>> url_raw = 'https://maps.googleapis.com/maps/api/directions/json&client=REMOVED'
>>> urlparse.urlparse('url_raw')
ParseResult(scheme='', netloc='', path='url_raw', params='', query='', fragment='')
>>> urlparse.urlparse(url_raw)
ParseResult(scheme='https', netloc='maps.googleapis.com', path='/maps/api/directions/json&client=REMOVED', params='', query='', fragment='')

Note how in the first case scheme is an empty string, while when parsing the actual URL the scheme is set to 'https' instead.

Next, you to produce the signature for the query string as a whole, not just the path plus client id.

You can best do that by creating a prepared request, after attaching the query parameters:

session = requests.Session()

for origin, destination in zip(origincoords, destinationcoords):
    params ={'origin': origin, 'destination': destination}
    request = requests.Request('GET', Full_URL, params=params).prepare()
    parsed_url = urlparse.urlparse(request.url)
    signature = hmac.new(decodedKey, '{}?{}'.format(parsed_url.path, parsed_url.query),
                         hashlib.sha1).digest()
    request.prepare_url(request.url, {'signature': base64.urlsafe_b64encode(signature)})
    response = session.send(request)
    directions = response.json()

    results.append(directions)

This uses a PreparedRequest object to produce the URL for us, so we can parse that again for ease of signing. The PreparedRequest.prepare_url() method then lets us add the signature to the URL of that object, after which we can easily send it with a session object.

Upvotes: 1

Related Questions