Correctly posting blog posts to a Blogger blog using the BloggerAPI

Topics: 

Google conveniently provides a fair bit of documentation and example code to use the Blogger API and do things with blogspot.com based blogs.  What I want to do in this post is walk through using the Blogger API to post blog entries to a blogger blog.  The Blogger API is one of the standard blog API's and for example is directly supported in applications like MarsEdit (which I'm using to write this post).  You may have other scenarios driving the need to programmatically manipulate your blogger blog.  In my case I'm migrating blog posts from my old Drupal based blog sites, over to blogspot based blogs.  While Drupal is a wonderful platform I realized my time is better spent writing blog posts (creating content) than maintaining the servers required to run my blogs.

We'll be using the Java based client library to the Blogger API.  Under the covers it creates ATOM based XML requests.  Because this is totally obscured by the Java client library, I think users of the library are in the dark about what they're actually doing.  It's instructive to understand something about the ATOM XML format as well as the Blogger protocol reference page.  There are useful links below to all of this.

The outline of the process is:

  • Authenticate your program with Blogger/Google
    • Gives you an authenticated com.google.gdata.client.blogger.BloggerService object
  • For each post you want to create
    • Create a BlogEntry object, filling it with the data representing the blog post
    • call service.insert with that object
    • Catch any errors, and note that blogger limits you to creating 50 posts per day via the API

Authenticating with Blogger

Google supports three authentication methods: OAuth, AuthSub, and ClientLogin.  Unfortunately their description is obfuscatory enough that I wasn't able to work out  anything but the ClientLogin method.  I'd like to use OAuth but it links over to some generic documentation that doesn't make enough sense.

The ClientLogin method is the same as e.g. going to blogger.com and entering your user name (email address) and password for a Google Account.  They mean it to be used for standalone, single-user "installed" client applications (such as a desktop application).  When applications like MarsEdit ask you for Google Account credentials, they're using the ClientLogin method.  This method has one suboptimal aspect, it requires recording your Google Account user name and password in a file on your computer.  Obviously there's a security risk of that file leaking out and being seen by other people, so be careful.

import com.google.gdata.client.blogger.BloggerService;
...
String authorName = " -- ";
String userName = " -- ";
String userPasswd = " -- ";
String blogId = " -- ";
...
BloggerService service = new BloggerService("exampleCo-exampleApp-1");
service.setUserCredentials(userName, userPasswd);

The userName and userPassword are the same used for login at blogger.com.  The authorName is a human-friendly name string.

Getting the blogId for a Blogger blog

You get the blogId by logging into the blogger.com, and going to the blog dashboard.   The URL for the dashboard includes a blogId= parameter that's, well, the blogId for the given blog.   It looks like this:

http://www.blogger.com/blogger.g?blogID=____blogId___#overview

Simply copy the number out of your dashboard URL, and you're good to go.

It's also possible to write a little program to list the blogs (and their blogId's) owned by a particular google account:

package blogger;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.net.URL;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
import com.google.gdata.client.blogger.BloggerService;
import com.google.gdata.data.Feed;
import com.google.gdata.data.Entry;

public class ListBlogs {

static String authorName = " -- ";
static String userName = " -- ";
static String userPasswd = " -- ";

public static void main(String[] args)
throws ParserConfigurationException, SAXException, IOException,
AuthenticationException, ServiceException {
BloggerService service = new BloggerService("exampleCo-exampleApp-1");
service.setUserCredentials(userName, userPasswd);
// Request the feed
final URL feedUrl = new URL("http://www.blogger.com/feeds/default/blogs");
Feed feed = service.getFeed(feedUrl, Feed.class);
// Print the results
System.out.println(feed.getTitle().getPlainText());
for (int i = 0; i < feed.getEntries().size(); i++) {
Entry entry = feed.getEntries().get(i);
System.out.println(entry.getId() + "\t" + entry.getTitle().getPlainText());
}
}
}

Run this (after downloading the libraries) and it prints a table of identifier strings and blog names.  Unfortunately the identifier string is verbose, long, and the blogId we're interested in is encoded within the string.  What we need is to change that last line of code to read this way:

    System.out.println(entry.getId().split("blog-")[1] + "\t" + entry.getTitle().getPlainText());

A class (MetaData) to help you form correct URL's for the Blogger API

Using the Blogger API means constructing correct URL's.  The feedUrl above is one example, and the full list of URL's is documented on the protocol reference page.  The one we're especially interested in for creating blog posts is:

http://www.blogger.com/feeds/blogID/posts/default

I found it helpful to create a class to contain the details.  It's meant to contain methods to construct any of the URL's used with the Blogger API, at the moment it only supports creating the URL to post new blog posts.

import java.net.MalformedURLException;
import java.net.URL;

public class MetaData {
public static final String METAFEED_URL = "http://www.blogger.com/feeds/default/blogs";
public static final String FEED_URI_BASE = "http://www.blogger.com/feeds";
public static final String POSTS_FEED_URI_SUFFIX = "/posts/default";
public static final String COMMENTS_FEED_URI_SUFFIX = "/comments/default";

public static URL createPostFeedURL(String blogId) throws MalformedURLException {
return new URL(FEED_URI_BASE +"/"+ blogId + POSTS_FEED_URI_SUFFIX);
}
}

 

Creating a BlogEntry object

This method is used to create blog posts.  Simply call it with all the argument values given here.  It assumes the variables above have been initialized, specifically the "service" has been connected as above.

import com.google.gdata.client.blogger.BloggerService;
import com.google.gdata.data.Category;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Entry;
import com.google.gdata.data.Feed;
import com.google.gdata.data.IEntry;
import com.google.gdata.data.Person;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.blogger.BlogEntry;
import com.google.gdata.util.ServiceException;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
...
BloggerService service = null; // initialize as above
String blogId = null; // find this as discussed earlier
String authorName = null; // pretty human name
String userName = null; // blogger account name
...
public IEntry createPost(String title, String content,
LinkedList categories, Boolean isDraft,
DateTime published, DateTime edited) throws ServiceException, IOException {
// Create the entry to insert
BlogEntry entry = new BlogEntry();
entry.setTitle(new PlainTextConstruct(title));
entry.setContent(new PlainTextConstruct(content));
if (published != null) {
entry.setPublished(published);
}
if (edited != null) {
entry.setEdited(edited);
}
entry.getAuthors().add(new Person(authorName, null, userName));
entry.setDraft(isDraft);
entry.setCanEdit(true);

int count = 0;
for (String catname : categories) {
if (count++ > 19) continue;
Category category = new Category();
category.setScheme("http://www.blogger.com/atom/ns#");
category.setTerm(catname);
entry.getCategories().add(category);
}

// Ask the service to insert the new entry
URL postUrl = MetaData.createPostFeedURL(blogId);
return service.insert(postUrl, entry);
}

Let's walk through this carefully.

Entry objects come in many varieties and the javadoc makes this rather obtuse and obfuscated.  In any case it appears that BlogEntry is the correct Entry class to use to represent, well, a BlogEntry.  Create one of these, and then fill in its data values using the methods it exports.

The setPublished and setEdited methods are used to set the publishing and edited dates.  If you don't supply these dates, the date gets set to "NOW".  In my case I'm exporting blog posts from an existing Drupal blog and want the post in Blogger to have the same posting date.  You'll notice the published and edited objects are GData DateTime object.  In my case I'm receiving String's in an RFC-822 date format and generate that DateTime object using the provided factory method:

DateTime publ = DateTime.parseRfc822(entryPublished); 

The isDraft method determines whether the post is a draft post, in which case the public won't see it, or a real live post.

There's a loop setting categories on the post.  This corresponds to what the blogger dashboard calls a "Label".  You'll notice the setScheme method call.  I really don't understand why the Blogger client library makes you do this.  You'll see on the protocol reference page this is the required encoding in the XML for each category term.  Calling the setScheme method sets this scheme attribute in the XML, but what puzzles me is why the Blogger API client library makes you do this, and why they don't just do this under the covers for you.

Another tricky bit about setting up the categories is that line which does a "continue" if "count" is greater than 19.  What I found is that if you specify 20 or more categories Blogger throws up an error.  This line of code silently drops the excess category names.

Once you have the BlogEntry object configured correctly you call service.insert.  What happens inside insert is

  • we generate the correct URL for posting blog entries using the MetaData class
  • the BlogEntry object is serialized down to the correct XML,
  • the XML s sent (authenticated) to the postUrl

Conclusion

I hope this was useful.  The task of posting to a Blogger blog is pretty simple but Google's documentation leaves a bit to be desired.

Links:

http://code.google.com/apis/blogger/ - Blogger API home page

http://code.google.com/apis/gdata/javadoc/ - Javadoc for all of Google's Java client libraries

http://code.google.com/apis/blogger/docs/2.0/developers_guide_protocol.html - Protocol reference for the Blogger API

http://code.google.com/apis/blogger/docs/2.0/developers_guide_java.html#Authenticating - Authenticating using Java client

http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html