I use Bing for the search box on bililite.com, and it's worked well; simple API, no need to create a custom search engine as with Google. Unfortunately, Microsoft is losing almost half a million dollars an hour on Bing, and they want me to make up the difference. Well, not me alone, but they are going to start charging for using their web services. Fortunately, they are (as of now) providing a free tier of up to 5,000 queries a month, which is far more than I need.
So I have to sign up for Azure Marketplace (Azure is Microsoft's cloud service) and Subscribe to the Bing Web Search API and create an application key. Then I need to convert my old requests into the new format. Luckily, Microsoft provides a migration guide (as a Word document!), and that includes sample code in PHP. The biggest difference is the need for HTTP authentication. The code from Microsoft works, as long I leave out the proxy line in the context parameters (I guess they only tested their code on local servers) and file_get_contents works on URLs, which is enabled on my service with Nearly Free Speech. I imagine setting the header similarly with cUrl would also work.
The other big difference is that they no longer return the total number of results if not all of them were returned. Now they return a parameter __next
(note two underlines) that contains the URL for getting more results if they are available. Since I'm only showing a limited list, I just need to test for the existence of that parameter to indicate that more results are available.
So the updated code is:
<?$accountKey = 'your Azure key here'; $q = $_GET['q']; if (get_magic_quotes_gpc()) $q = stripslashes($q); // note that $q is unsafe!
?><form method="get" action=""> <input name="q" type="text" value="<?=htmlentities($q)?>" /> <input type="submit" value="Search" /> </form>
<?function sitesearch ($query, $site, $accountKey, $count=NULL){ // code from http://go.microsoft.com/fwlink/?LinkID=248077 $ServiceRootURL = 'https://api.datamarket.azure.com/Bing/Search/'; $WebSearchURL = $ServiceRootURL . 'Web?$format=json&Query='; $context = stream_context_create(array( 'http' => array( 'request_fulluri' => true, 'header' => "Authorization: Basic " . base64_encode($accountKey . ":" . $accountKey) ) )); $request = $WebSearchURL . urlencode("'$query site:$site'"); // note the extra single quotes if ($count) $request .= "&\$top=$count"; // note the dollar sign before $top--it's not a variable! return json_decode(file_get_contents($request, 0, $context)); } function showresponse ($q, $next, $results){ $count = count($results); if ($count==0){ $resultwords = 'No results'; }else if ($next){ $resultwords = "More than $count results"; }else if ($count == 1){ $resultwords = '1 result'; }else{ $resultwords = "$count results"; } echo "<p>$resultwords found for <strong>".htmlentities($q)."</strong></p>\n"; if ($count == 0) return; echo "<dl>\n"; foreach ($results as $result) echo "<dt><a href='{$result->Url}'>{$result->Title}</a></dt>\n<dd>{$result->Description}</dd>\n"; echo "</dl>\n"; } if ($q){ // get search results $result = sitesearch ($q, $_SERVER['HTTP_HOST'], $accountKey, 10); showresponse($q, $result->d->{__next}, $result->d->results); $searchURL = 'http://www.bing.com/search?q='.urlencode("$q site:{$_SERVER['HTTP_HOST']}"); echo "<p><a href='$searchURL'>See all results</a><p>\n"; }
?>
And you can see it in action on the Bililite home page. Note that as of today (June 5, 2012) my domain registrar is in flux and Bing doesn't have many pages for bililite.com. Try the alias of bililite.nfshost.com for better search results.
Now to wait for the inevitable, when Microsoft cans the whole thing altogether. TNSTAAFL.
AK says:
Hi Danny-
I’m a fledgling computer programmer and I must now learn the new Bing API….I had it working for a while under the OLD API and now feel the same frustration you do regarding the change.
Anyway, I am happy to have found your code, and with a LAMP server I built I am trying to get it to run.
My PHP complains about the last line:
[Thu Dec 06 08:44:54 2012] [error] [client 192.168.2.19] PHP Parse error: syntax error, unexpected ‘<' in /var/www/b3.php on line 53
What I did was to copy n paste into a b3.php file and I'm wondering where I might need to fix some code.
December 6, 2012, 7:46 amSuggestions?
Danny says:
@AK:
December 6, 2012, 8:49 amThat was a copy/paste error. The last line should be
?>
.I have corrected it. Thanks for looking so closely!
–Danny
AK says:
Excellent, very cool.
I’ve made that PHP syntax adjustment and now I am at the core of the matter….
No matter what I try, I keep getting the error:
PHP Warning: file_get_contents(https://api.datamarket.azure.com/Bing/Search/Web?$format=json&Query=%27sushi%27&$top=10): failed to open stream: HTTP request failed! HTTP/1.1 401 The authorization type you provided is not supported. Only Basic and OAuth are supported\r\n in /var/www/b3.php on line 31
Not sure what’s causing that error, yet.
December 8, 2012, 1:15 pmOffer some guidance if you can, I’m still working it on this end.
Danny says:
@AK:
That’s the error that Bing gives when it’s not getting the username/password. I can’t solve your problem, but I would suggest:
1. Try the URL directly in the browser (https://api.datamarket.azure.com/Bing/Search/Web?$format=json&Query=%27sushi%27&$top=10) and enter your account key directly (it’s both the username and the password) and see if it works.
2. Use the Microsoft sample code and go from there (http://go.microsoft.com/fwlink/?LinkID=248077 and search for the PHP sample code).
3. Use cURL rather than file_get_contents. Look at the manual page (http://us.php.net/manual/en/function.curl-setopt.php) and search for “https:” for sample code; mostly it’s a matter of
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ) ;
curl_setopt($curl, CURLOPT_USERPWD, "username:password");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
Hope this helps.
December 9, 2012, 7:45 am–Danny
Martin says:
How would you do a bing search from the shell ?
August 14, 2013, 3:13 pmDanny says:
@Martin:
August 14, 2013, 3:28 pmThat’s not the subject of this post, but I suppose
an appropriate use of cURL from the command line would work.
–Danny