After a couple painful hours of trial and error, I’d like to share some code that simplifies the process of accessing the MSN Live Search API from Ruby.
The code assumes that you have auto-generated the SOAP driver for the MSN Search web services in a directory called msn_search_driver, using the following command.
wsdl2ruby –wsdl http://soap.search.msn.com/webservices.asmx?wsdl –type client
msn_search.rb takes care of the nasty details for you, so that you can write simple code like this:
require 'msn_search'
MsnSearch.new.search("bogle").results.each {|r| puts r.url}
<RANT>
Although it’s possible in to call the SOAP driver directly, the API is hard to use because of complex, nested request and response types, poorly documented mandatory arguments, and unhelpful error messages on invalid requests. (These shortcomings are not unique to the MSN SOAP interface– I’ve experience similar shortcomings in many other SOAP apis. REST-based APIs tend to win hands down in terms of simplicity and debuggability.
Note that neither Google, Yahoo, nor MSN will allow to fetch beyond the first 1000 results in a search, independent of their daily hit limits. If you need to use a web index for an application that’s more than a toy, you’re probably much better off using something like Alexa Web Search Platform than any of the major search APIs. The downside to the Alexa platform is that C and Java are the only supported languages. The Alexa platform supports C, Java, and Ruby. Alexa provides Ruby-based tutorial on how to build an image search application using their index.
</RANT>
Now that I’ve got that rant off my chest, here’s the code.
msn_search.rb
($:).push('msn_search_driver')
require ‘defaultDriver.rb’
class MsnSearch
attr_accessor :driver
def initialize(appID = ‘INSERT_YOUR_APP_ID_HERE’)
@driver = MSNSearchPortType.new
@appID = appID
end
def search(query, offset=0, count=50, source=”Web”,
safeSearch=”Off”, culture=”en-US”)
s = Search.new
s.request = with_new(SearchRequest) do |r|
r.cultureInfo = culture
r.safeSearch = safeSearch
r.query = query
r.appID = @appID
r.requests = source_requests =
ArrayOfSourceRequestRequests.new
source_requests << with_new(SourceRequest) do |sr|
sr.offset = offset
sr.count = count
sr.source = source
sr.resultFields = “All”
end
end
@driver.search(s).response.responses.sourceResponse
end
def set_debug_mode(dev = STDOUT)
@driver.wiredump_dev = dev
end
protected
def with_new(klass)
instance = klass.new
yield instance
return instance
end
end