Saturday, September 20, 2014

Simple Google API access from Python (part 1 of 2)

NOTE: You can also watch a video walkthrough of the common code covered in this blogpost here.

UPDATE (Aug 2016): The code has been modernized to recognize that the Client Library is available for Python 2 or 3.

Introduction

Back in 2012 when I published Core Python Applications Programming, 3rd ed., I
posted about how I integrated Google technologies into the book. The only problem is that I presented very specific code for Google App Engine and Google+ only. I didn't show a generic way how, using pretty much the same boilerplate Python snippet, you can access any number of Google APIs; so here we are.

In this multi-part series, I'll break down the code that allows you to leverage Google APIs to the most basic level (even for Python), so you can customize as necessary for your app, whether it's running as a command-line tool or something server-side in the cloud backending Web or mobile clients. If you've got the book and played around with our Google+ API example, you'll find this code familiar, if not identical — I'll go into more detail here, highlighting the common code for generic API access and then bring in the G+-relevant code later.

We'll start in this first post by demonstrating how to access public or unauthorized data from Google APIs. (The next post will illustrate how to access authorized data from Google APIs.) Regardless of which you use, the corresponding boilerplate code stands alone. In fact, it's probably best if you saved these generic snippets in a library module so you can (re)use the same bits for any number of apps which access any number of modern Google APIs.

Google API access

In order to access Google APIs, follow these instructions:
  • Go to the Google Developers Console and login.
    • Use your Gmail or Google credentials; create an account if needed
  • Click "Create Project" button
    • Enter a Project Name (mutable, human-friendly string only used in the console)
    • Enter a Project ID (immutable, must be unique and not already taken)
  • Once project has been created, click "Enable an API" button
    • You can toggle on any API(s) that support(s) simple API access (not authorized).
    • For the code example below, we use the Google+ API.
    • Other ideas: YouTube Data API, Google Maps API, etc.
    • Find more APIs (and version#s which you need) at the OAuth Playground.
  • Select "Credentials" in left-nav under "APIs & auth"
    • Go to bottom half and click "Create new Key" button
    • Grab long "API KEY" cryptic string and save to Python script
    NOTE: You can also watch a video walkthrough of this app setup process in the "DevConsole" here.

    Accessing Google APIs from Python

    Now that you're set up, everything else is done on the Python side. To talk to a Google API, you need the Google APIs Client Library for Python, specifically the apiclient.discovery.build() function. Download and install the library in your usual way, for example:

    $ pip install -U google-api-python-client  # or pip3 for 3.x
    NOTE: If you're building a Python App Engine app, you'll need something else, the Google APIs Client Library for Python on Google App Engine. It's similar but has extra goodies (specifically decorators — brief generic intro to those in my previous post) just for cloud developers that must be installed elsewhere. As App Engine developers know, libraries must be in the same location on the filesystem as your source code.
    Once everything is installed, make sure that you can import apiclient.discovery:

    $ python
    Python 2.7.6 (default, Apr  9 2014, 11:48:52)
    [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.38)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import apiclient.discovery
    >>>

    In discovery.py is the build() function, which is what we need to create a service endpoint for interacting with an API. Now craft the following lines of code in your command-line tool, using the shorthand from-import statement instead:

    from apiclient import discovery

    API_KEY = # copied from project credentials page
    SERVICEdiscovery.build(API, VERSION, developerKey=API_KEY)

    Take the API key you copied from the credentials page and assign to the API_KEY variable as a string. Obviously, embedding an API key in source code isn't something you'd so in practice as it's not secure whatsoever — stick it in a database, key broker, encrypt, or at least have it in a separate byte code (.pyc/.pyo) file that you import — but we'll allow it now solely for illustrative purposes of a simple command-line script.

    In our short example we're going to do a simple search for "python" in public Google+ posts, so for the API variable, use the string 'plus'. The API version is currently on version 1 (at the time of this writing), so use 'v1' for VERSION. (Each API will use a different name and version string... again, you can find those in the OAuth Playground or in the docs for the specific API you want to use.) Here's the call once we've filled in those variables:

    GPLUS = discovery.build('plus', 'v1', developerKey=API_KEY)

    We need a template for the results that come back. There are many fields in a Google+ post, so we're only going to pick three to display... the user name, post timestamp, and a snippet of the post itself:

    TMPL = '''
        User: %s
        Date: %s
        Post: %s
    '''

    Now for the code. Google+ posts are activities (known as "notes;" there are other activities as well). One of the methods you have access to is search(), which lets you query public activities; so that's what we're going to use. Add the following call using the GPLUS service endpoint you already created using the verbs we just described and execute it:

    items = GPLUS.activities().search(query='python').execute().get('items', [])

    If all goes well, the (JSON) response payload will contain a set of 'items' (else we assign an empty list for the for loop). From there, we'll loop through each matching post, do some minor string manipulation to replace all whitespace characters (including NEWLINEs [ \n ]) with spaces, and display if not blank:

    for data in items:
        post = ' '.join(data['title'].strip().split())
        if post:
            print(TMPL % (data['actor']['displayName'],
                          data['published'], post))


    Conclusion

    To find out more about the input parameters as well as all the fields that are in the response, take a look at the docs. Below is the entire script missing only the API_KEY which you'll have to fill in yourself.

    from __future__ import print_function
    from apiclient import discovery
    
    TMPL = '''
        User: %s
        Date: %s
        Post: %s
    '''
    
    API_KEY = # copied from project credentials page
    GPLUS = discovery.build('plus', 'v1', developerKey=API_KEY)
    items = GPLUS.activities().search(query='python').execute().get('items', [])
    for data in items:
        post = ' '.join(data['title'].strip().split())
        if post:
            print(TMPL % (data['actor']['displayName'],
                          data['published'], post))
    

    When you run it, you should see pretty much what you'd expect, a few posts on Python, some on Monty Python, and of course, some on the snake — I called my script plus_search.py:

    $ python plus_search.py # or python3
    
        User: Jeff Ward
        Date: 2014-09-20T18:08:23.058Z
        Post: How to make python accessible in the command window.
    
    
        User: Fayland Lam
        Date: 2014-09-20T16:40:11.512Z
        Post: Data Engineer http://findmjob.com/job/AB7ZKitA5BGYyW1oAlQ0Fw/Data-Engineer.html #python #hadoop #jobs...
    
    
        User: Willy's Emporium LTD
        Date: 2014-09-20T16:19:33.851Z
        Post: MONTY PYTHON QUOTES MUG Take a swig to wash down all that albatross and crunchy frog. Featuring 20 ...
    
    
        User: Doddy Pal
        Date: 2014-09-20T15:49:54.405Z
        Post: Classic Monty Python!!!
    
    
        User: Sebastian Huskins
        Date: 2014-09-20T15:33:00.707Z
        Post: Made a small python script to get shellcode out of an executable. I found a nice commandlinefu.com oneline...
    

    EXTRA CREDIT: To test your skills, check the docs and add a fourth line to each output which is the URL/link to that specific post, so that you (and your users) can open a browser to it if of interest.

    If you want to build on from here, check out the larger app using the Google+ API featured in Chapter 15 of the book — it adds some brains to this basic code where the Google+ posts are sorted by popularity using a "chatter" score. That just about wraps it up this post. Once you're good to go, then you're ready to learn how to perform authorized Google API access in part 2 of this two-part series!

    No comments:

    Post a Comment