Sunday, October 08, 2006

Using the Google GData API with Python

I did a very quick search online for examples that I could use to see how to interact with Google's GData API for creating and posting Blogger-beta blog entries. The best I could find was this post to Jon Udell's Infoworld weblog which illustrates making an entry to Google Calendar. Now even though it is a different web application, most of this example code is still relevent as apparently they use the same API.

From this, I tried the following code:

#!/usr/bin/env python

import httplib2

h = httplib2.Http()
h.add_credentials('MY_GMAIL_ACCOUNT', 'MY_GMAIL_PASSWORD')
h.follow_all_redirects = True
uri = 'http://www.blogger.com/feeds/BLOGID/posts/full'

post_xml = """
<?xml version="1.0" ?>
<entry xmlns='http://www.w3.org/2005/Atom'>
  <title type='text'>Test Post from Python</title>
  <content type='xhtml'>
    <div xmlns="http://www.w3.org/1999/xhtml">
      <p>If you can see this, then it worked!</p>
    </div>
  </content>
  <author>
    <name>Dennis</name>
  </author>
</entry>
"""

headers = {'Content-Type': 'application/atom+xml'}
response, content = h.request(uri, 'POST', body=post_xml, headers=headers)
print response, content

With this code, the last print line revealed a Error 401 of Missing auth parameter in GoogleLogin. I assumed that there was a problem with the method of authentication in the httplib2 library. Possibly it was out of date as apparently this API is a bit of a moving target still these days. As a result, I had a look at the Google Account Authentication docs to see if I would have better luck doing the authentication manually. After a bit of fiddling, I ended up with this:

#!/usr/bin/env python

import httplib2
import re

def postEntry(auth):

    entry = """
    <?xml version="1.0" ?>
    <entry xmlns='http://www.w3.org/2005/Atom'>
      <title type='text'>Test Post from Python</title>
      <content type='xhtml'>
        <div xmlns="http://www.w3.org/1999/xhtml">
          <p>If you can see this, then it worked!</p>
        </div>
      </content>
      <author>
        <name>Dennis</name>
      </author>
    </entry>
    """

    headers = {'Content-Type': 'application/atom+xml', 'Authorization': 'GoogleLogin Auth=%s' % auth.strip()}
    response, content = h.request(uri, 'POST', body=entry, headers=headers)
    print response, content

def authenticate():
    auth_uri = 'https://www.google.com/accounts/ClientLogin'
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    myrequest = "Email=MY_GMAIL_ACCOUNT&Passwd=MY_GMAIL_PASSWORD&service=blogger&service=Dcraven-TestApp-0.0"
    response, content = h.request(auth_uri, 'POST', body=myrequest, headers=headers)
    if response['status'] == '200':
        return re.search('Auth=(\S*)', content).group(1)
    else:
        return None

h = httplib2.Http()
h.follow_all_redirects = True
uri = 'http://www.blogger.com/feeds/BLOGID/posts/full'

cert = authenticate()
if cert:
    postEntry(cert)

The result of this was exactly the same thing, although the actual authenticate() function worked fine, so the problem doesn't lie there. The response from the POST call in this method was in fact 200, and I did get an Auth code from the server. Even after manually packing this code into the header as specified by the Google documentation, I still got an Error 401: Missing auth parameter in GoogleLogin returned by the postEntry() function.

I think this code should work, but alas, it does not. If anyone has any ideas why, or can spot an error in the code I'd really appreciate you pointing it out in a comment below. In the meantime I think I'll make posts the old fashioned way until I get the ambition to try again.

NOTE: I suppose I should note that in the above code, the words BLOGID, MY_GMAIL_PASSWORD, and MY_GMAIL_ACCOUNT actually contained the appropriate values when I tried to run the program :)

UPDATE: I just wanted to post this update for anyone landing here from a Google search or something. I ended up having success with this as posted here.

3 comments:

  1. What is most likely happening is that you get a redirect, and if you follow the redirect, your custom headers will not be posted again (at least that happens in other http environments). Meaning-> you add the Google:auth header, you get a 302 back, python http lib follows, posts again, but without the auth header.

    You might need to handle redirects yourself.

    Frank Mantek

    ReplyDelete
  2. I'm having exactly the same problem. Did you get a solution to it ?

    ReplyDelete
  3. @gabriel,

    Sure did, have a look here.
    :)

    ReplyDelete