using google image search from within a cocoa app
I’ve been looking for a way to use Google’s image search from within a Cocoa application but found nothing ready to use. So I had to do it myself using regular expressions. After some research I found a .NET project here which got me started. I finally came up with a working solution. Only drawback: if Google decides to change anything in the search result’s HTML the regular expression has to be adapted.
Prerequisites
You will need to download and install the excellent RegexKit framework here (full version, NOT lite!).
The project was created with XCode 3.1.2 and requires Leopard as it uses some Obj-C 2.0 features. It can be downloaded here.
How Google Image Search works
When you go to http://images.google.com/ and type in a search query, a page with exactly 20 result images is shown. You’ll also notice a bunch of parameters added to the URL on the results page. Most important for us are the q and start parameters.
qdefines the search term. Soq=applefor example, searches for the term “apple”. Spaces must be replaced with “+”.-
startdefines the results’ zero-based starting index. So a query withstart=42would return 20 results, starting from index 42.
The Regular Expression
#define IMAGES_REGEX @"/imgres\x3Fimgurl=(?<imgurl>[^&>]*)[>&]{1}imgrefurl=(?<imgrefurl>[^&>]*)[>&]{1}usg=[^&>]*[>&]{1}h=(?<height>[^&>]*)[>&]{1}w=(?<width>[^&>]*)[>&]{1}sz=(?<sz>[^&>]*)[>&]{1}hl=(?<hl>[^&>]*)[>&]{1}start=(?<start>[^&>]*)[>&]{1}tbnid=(?<tbnid>[^&>]*)[>&]{1}tbnh=(?<tbnh>[^&>]*)[>&]{1}tbnw=(?<tbnw>[^&>]*)[>&]{1}prev=(?<prev>[^&>]*)[>&]{1}<img src=(?<tbnurl>[^ ]*)[ ]“
The expression basically looks for the interesting stuff in the HTML response which are the URLs and dimensions of the preview and the actual image. These are stored in named groups (recognizable by ?<groupname>) which can then be easily extracted into a NSString object.
Code
To build a search query in Obj-C there are 3 basic steps:
- Build the URL string
- Create an NSURLRequest and a NSURLConnection to receive the HTTP response
- Parse the HTTP response
1 and 2 are basic stuff (well, 2 not so much… read Apple’s URL Loading System guide). So lets assume the response has finished loading and we have it stored in an NSData object. First we have to convert this object to NSString in order to parse it:
NSString *result = [[NSString alloc] initWithData:receivedData
encoding:NSUTF8StringEncoding];
Then RegexKit is used to parse the string:
RKEnumerator *matchEnum = [result matchEnumeratorWithRegex:IMAGES_REGEX];
// loop over all results
while([matchEnum nextRanges] != NULL)
{
double width = 0.0;
double height = 0.0;
// image
NSString *imageUrl = [matchEnum stringWithReferenceFormat:@"${imgurl}"];
[matchEnum getCapturesWithReferences:@"${width:%lf}", &width, nil];
[matchEnum getCapturesWithReferences:@"${height:%lf}", &height, nil];
NSSize imageSize = NSMakeSize(width, height);
// thumbnail
NSString *tbnUrl = [matchEnum stringWithReferenceFormat:@"${tbnurl}"];
[matchEnum getCapturesWithReferences:@"${tbnw:%lf}", &width, nil];
[matchEnum getCapturesWithReferences:@"${tbnh:%lf}", &height, nil];
NSSize tbnSize = NSMakeSize(width, height);
MyImageObject *imgObj = [[MyImageObject alloc] init];
imgObj.thumbnailUrl = tbnUrl;
imgObj.imageUrl = imageUrl;
imgObj.thumbnailSize = tbnSize;
imgObj.imageSize = imageSize;
[self.imageObjects addObject:imgObj];
[imgObj release];
}
Here is what happens. The loop jumps from match to match. Essential data is extracted from the current match using the regex groups mentioned earlier. Then a MyImageObject instance storing this data is created and added into a NSMutableArray.
After this loop is done, we have successfully sent a search request to Google’s image search, received and parsed the response, and stored all essential data into custom objects, ready to be processed for display.
Displaying the Images
For nicely displaying the images I chose the new IKImageBrowserView from ImageKit. Basically I followed Apple’s Image Kit programming guide for IKImageBrowserView. Because we are dealing with image URLs, the only thing I changed was MyImageObject’s imageRepresentationType, which is now IKImageBrowserNSURLRepresentationType. Setting up the datasource was really easy and the result looks pleasing.
