Here’s an example of implementing an API with many different endpoints. It’s the Google AJAX Search API which lets you access all of Google’s search engines programmatically! A few notes:
- In the Javascript API Google provides “branding” functions to make sure search results are properly attributed. There doesn’t seem to be a corresponding AJAX call — that is, it’s probably implemented directly in the Javascript — but I’d still like to provide a corresponding function. It would be nice if API providers actually gave a branding end-point
- The code doesn’t support (yet) multi-page results: coming soon
- The clever bit is in
_item_path, which describes how to pull WORK result objects out of the AJAX result - all this code is actually available right now, via SVN: the instructions are here. This library is standalone (and is in fact the basis for many of the other projects I have on Google code)
- The Google API requires a
_http_referer: the URL of the site that’s using the results - The Google API does not require an API key, but you can pass one (in the constructor or in individual search calls) under the key
api_key. You can use the same API key that you’ve created for Google Maps.
Here’s the Google API class: quite simple. I’ll probably extend each individual search function to provide all the known parameters by name, rather than passing in a **ad catch-all.
class Google(bm_api.API):
_base_query = {
"v" : "1.0",
}
_item_path = "responseData.results"
_meta_path = "responseData.cursor"
_convert2work = bm_work.JSON2WORK()
def __init__(self, _http_referer, **ad):
bm_api.API.__init__(self, _http_referer = _http_referer, **ad)
def WebSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/web"
self.SearchOn(q = q, **ad)
def LocalSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/local"
self.SearchOn(q = q, **ad)
def VideoSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/video"
self.SearchOn(q = q, **ad)
def BlogSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/blogs"
self.SearchOn(q = q, **ad)
def NewsSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/news"
self.SearchOn(q = q, **ad)
def BookSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/books"
self.SearchOn(q = q, **ad)
def ImageSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/images"
self.SearchOn(q = q, **ad)
def PatentSearch(self, q, **ad):
self._uri_base = "http://ajax.googleapis.com/ajax/services/search/patentNew"
self.SearchOn(q = q, **ad)
Here’s how you use it:
api_key = os.environ["GMAPS_APIKEY"]
referer = "http://code.davidjanes.com"
query = "Paris Hilton"
api = Google(key = api_key, _http_referer = referer)
api.VideoSearch(query)
for item in api.IterItems():
pprint.pprint(item)
Here’s an example of a results, searching for “Paris Hilton” in Videos. I tried searching in Patents without luck.
{'@Index': 0,
'@Page': 1,
u'GsearchResultClass': u'GvideoSearch',
u'content': u"Paris Hilton's new video clip for 'Nothing In This World'",
u'duration': u'204',
u'playUrl': u'http://www.youtube.com/v/...',
u'published': u'Thu, 12 Oct 2006 09:33:23 PDT',
u'publisher': u'www.youtube.com',
u'rating': u'4.52872',
u'tbHeight': u'240',
u'tbUrl': u'http://0.gvt0.com/vi/Ki2M3-2W-cQ/0.jpg',
u'tbWidth': u'320',
u'title': u'Paris Hilton - Nothing In This World',
u'titleNoFormatting': u'Paris Hilton - Nothing In This World',
u'url': u'http://www.google.com/url?q=...',
u'videoType': u'YouTube'}
[...] scraping it (by pretending HTML is XHTML and treating it as WORK objects), geocoding it (using our WORK Google API) and mapping it (using this [...]
Loving your WORK prototypes – I’ve been mucking about with the some of the same ideas in Ruby, albeit with a different approach.
Thanks Kieran! I’ve been working with this concept for a couple of weeks now and I think I’m getting the hang of the paradigm. In particular (I don’t have this work quite yet) what I’m hoping you do is specify a chain like this:
* specify a search, say “bistro in toronto”
* pass that as a Meta WORK to GoogleSearchLocal
* this will generate a list of WORK Items
We then write a mapper that converts a single GoogleSearchLocal WORK item into a single VirtualEarthDisplay WORK Item.
* we rewrite the WORK items using that mapper
* we pass those new items to VirtualEarthDisplay
We now have a mashup that’s entirely statically specified that looks up stuff in Google Local and displays on Virtual Earth.
You can then imagine hooking up events to VirtualEarth to change the Meta Item that starts the whole process, so as the map is moved the search evolves!