The Bing Search API 2.0 now is available in the Windows Azure Marketplace, check the updated article here.
The Bing Search API provides a very flexible set of methods to submit queries and retrieve results from the Bing Engine. In this article I will show how you can use this API from Delphi.
Getting Started
Before to start read the terms of use of the Bing API which states “…The API is intended to deliver relevant results (collectively, “Bing results”) for queries submitted to Microsoft’s Bing service and other related Microsoft services (collectively, “Bing services”) for rendering within a customer-facing or end-user-facing website (“Website”) or application“..(So it’s ok use this API from a desktop application)
To use the Bing API you must get an Application ID from the Bing Developer Center and include this AppId in the API calls.
You can send a request to the Bing Search API and get the response in one of these protocols JSON (JavaScript Object Notation), XML (Extended Markup Language) or SOAP (Simple Object Access Protocol).
The protocol is identified in the used URI, check these samples URLs
Before to choose the appropriate response protocol you must evaluate the kind of application which are you developing, in this article I choose the XML protocol due that can be implemented im most of the delphi versions without use third party libraries (Note : the only limitation using the XML response is which the length of the requested URL is limited by the maximum URL length.)
Building the URL
When you uses the Bing XML interface, you must make a HTTP GET using a URL like this
http://api.bing.net/xml.aspx?AppId= YOUR_APPID &Version=2.2&Market=en-US&Query=testign&Sources=web+spell&Web.Count=1&xmltype=elementbased
- Appid : is the Application ID
- Version : Version of the API to use (recommended is 2.2)
- Market : prefered language of the search results
- Query : the sentence to search
- Web.Count : the number of results to get
- xmltype : this field control control the flavor of XML interface. If ElementBased enumeration is specified, each field will be rendered as a separated tag. If AttributeBased enumeration is specified, all simple type fields will be rendered as attributes instead of elements. The default value is ElementBased.
- Sources : indicate the types of data to retrieve (see Working with SourceTypes) (you can use multiple values in this files separated by an +)
the values of this field can be
- Image (Bing, Version 2.0)
- News (Bing, Version 2.0)
- Phonebook (Bing, Version 2.0)
- RelatedSearch(Bing, Version 2.0)
- Spell(Bing, Version 2.0)
- Translation (Bing, Version 2.2)
- Video (Bing, Version 2.x)
- Web (Bing, Version 2)
Depending of how you construct your URL the XML returned can vary (see Using XML (Bing, Version2) for a sample output XML).
The code
After of build the URL you must make a GET request to access the Bing API. you can use your favorite component like WinInet, Indy and so on. In my case I choose the IXMLHTTPRequest interface to make GET request because I can use the same object to parse the XML result.
Check the next snippets using the AttributeBased and elementbased values in the xmltype field and how the parse procedure changes depending of this value.
{$APPTYPE CONSOLE}
uses
MSXML,
SysUtils,
ActiveX,
ComObj,
Variants;
//Demo of Attribute Based XML request
procedure GetBingInfoXML(const SearchKey : string;NumberOfResults:integer=1);
const
//this AppId if for demo only please be nice and use your own , it's easy get one from here http://msdn.microsoft.com/en-us/library/ff512386.aspx
ApplicationID= '73C8F474CA4D1202AD60747126813B731199ECEA';
URI='http://api.bing.net/xml.aspx?AppId=%s&Version=2.2&Market=en-US&Query=%s&Sources=web&web.count=%d&xmltype=AttributeBased';
COMPLETED=4;
OK =200;
var
XMLHTTPRequest : IXMLHTTPRequest;
XMLDOMDocument : IXMLDOMDocument;
XMLDOMNode : IXMLDOMNode;
XMLDOMNodeList : IXMLDOMNodeList;
LIndex : Integer;
begin
XMLHTTPRequest := CreateOleObject('MSXML2.XMLHTTP') As IXMLHTTPRequest;
try
XMLHTTPRequest.open('GET', Format(URI,[ApplicationID, SearchKey, NumberOfResults]), False, EmptyParam, EmptyParam);
XMLHTTPRequest.send('');
if (XMLHTTPRequest.readyState = COMPLETED) and (XMLHTTPRequest.status = OK) then
begin
XMLDOMDocument := XMLHTTPRequest.responseXML As IXMLDOMDocument2;
XMLDOMNode := XMLDOMDocument.selectSingleNode('//web:Web');
Writeln(Format('Total found %s',[String(XMLDOMNode.attributes.getNamedItem('Total').Text)]));
Writeln;
XMLDOMNodeList := XMLDOMNode.selectNodes('//web:WebResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Title %s',[String(XMLDOMNode.attributes.getNamedItem('Title').Text)]));
Writeln(Format('Description %s',[String(XMLDOMNode.attributes.getNamedItem('Description').Text)]));
Writeln(Format('Url %s',[String(XMLDOMNode.attributes.getNamedItem('Url').Text)]));
Writeln(Format('CacheUrl %s',[String(XMLDOMNode.attributes.getNamedItem('CacheUrl').Text)]));
Writeln(Format('DisplayUrl %s',[String(XMLDOMNode.attributes.getNamedItem('DisplayUrl').Text)]));
Writeln(Format('DateTime %s',[String(XMLDOMNode.attributes.getNamedItem('DateTime').Text)]));
Writeln;
end;
end;
finally
XMLHTTPRequest := nil;
end;
end;
begin
try
CoInitialize(nil);
try
GetBingInfoXML('delphi programming blogs', 5);
finally
CoUninitialize;
end;
except
on E:EOleException do
Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
{$APPTYPE CONSOLE}
uses
MSXML,
SysUtils,
ActiveX,
ComObj,
Variants;
//Demo of element based XML request
procedure GetBingInfoXML(const SearchKey : string;NumberOfResults:integer=1);
const
//this AppId if for demo only please be nice and use your own , it's easy get one from here http://msdn.microsoft.com/en-us/library/ff512386.aspx
ApplicationID= '73C8F474CA4D1202AD60747126813B731199ECEA';
URI='http://api.bing.net/xml.aspx?AppId=%s&Version=2.2&Market=en-US&Query=%s&Sources=web&web.count=%d&xmltype=elementbased';
COMPLETED=4;
OK =200;
var
XMLHTTPRequest : IXMLHTTPRequest;
XMLDOMDocument : IXMLDOMDocument;
XMLDOMNode : IXMLDOMNode;
XMLDOMNodeList : IXMLDOMNodeList;
LIndex : Integer;
begin
XMLHTTPRequest := CreateOleObject('MSXML2.XMLHTTP') As IXMLHTTPRequest;
try
XMLHTTPRequest.open('GET', Format(URI,[ApplicationID, SearchKey, NumberOfResults]), False, EmptyParam, EmptyParam);
XMLHTTPRequest.send('');
if (XMLHTTPRequest.readyState = COMPLETED) and (XMLHTTPRequest.status = OK) then
begin
XMLDOMDocument := XMLHTTPRequest.responseXML As IXMLDOMDocument2;
XMLDOMNode := XMLDOMDocument.selectSingleNode('//web:Total');
Writeln(Format('Total found %s',[String(XMLDOMNode.text)]));
Writeln;
XMLDOMNodeList := XMLDOMNode.selectNodes('//web:WebResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Title %s',[String(XMLDOMNode.selectSingleNode('./web:Title').Text)]));
Writeln(Format('Description %s',[String(XMLDOMNode.selectSingleNode('./web:Description').Text)]));
Writeln(Format('Url %s',[String(XMLDOMNode.selectSingleNode('./web:Url').Text)]));
Writeln(Format('CacheUrl %s',[String(XMLDOMNode.selectSingleNode('./web:CacheUrl').Text)]));
Writeln(Format('DisplayUrl %s',[String(XMLDOMNode.selectSingleNode('./web:DisplayUrl').Text)]));
Writeln(Format('DateTime %s',[String(XMLDOMNode.selectSingleNode('./web:DateTime').Text)]));
Writeln;
end;
end;
finally
XMLHTTPRequest := nil;
end;
end;
begin
try
CoInitialize(nil);
try
GetBingInfoXML('delphi programming blogs', 5);
finally
CoUninitialize;
end;
except
on E:EOleException do
Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
And calling any of these methods in this way
GetBingInfoXML('delphi programming blogs', 5);
Will produce this output
Total found 7190000
Title DelphiFeeds.com - All Delphi blogs in one place - Delphi community ...
Description Andy's Blog - I get some reports about problems with IDE Fix Pack 4.7. Those ... About Delphi Programming - in TTreeView :: Say you want to display customer-order-item data ...
Url http://www.delphifeeds.com/
CacheUrl http://cc.bingj.com/cache.aspx?q=delphi+programming+blogs&d=46646399
49310488&mkt=en-US&w=c537195d,718a1a7b
DisplayUrl www.delphifeeds.com
DateTime 2012-03-30T13:36:00Z
Title Delphi Programming Blog
Description Recently TMS Software released a FTP Uploader which I tried and liked because of its simplicity, but I was not satisfied with it because it did not reset itself to ...
Url http://williamwmiller.wordpress.com/
CacheUrl http://cc.bingj.com/cache.aspx?q=delphi+programming+blogs&d=4754451987498697&mkt=en-US&w=368ed92b,7b91d282
DisplayUrl williamwmiller.wordpress.com
DateTime 2011-12-29T03:45:00Z
Title RTTI « The Road to Delphi - a Blog about programming
Description Here 's a sample code of how you can dump the declaration of a TRttiType using the Rtti. Supports classes, records and interfaces. Delphi //Author ...
Url https://theroadtodelphi.wordpress.com/category/delphi/rtti/
CacheUrl http://cc.bingj.com/cache.aspx?q=delphi+programming+blogs&d=4925950032087641&mkt=en-US&w=c2546964,98458a
DisplayUrl theroadtodelphi.wordpress.com/category/delphi/rtti
DateTime 2012-02-22T11:03:00Z
Title 2011 September « The Road to Delphi - a Blog about programming
Description Maybe you've seen articles about how use the FireMonkey Styles, and how you can set almost every aspect of a visual control, today I will go a step...
Url https://theroadtodelphi.wordpress.com/2011/09/
CacheUrl http://cc.bingj.com/cache.aspx?q=delphi+programming+blogs&d=5062478449739337&mkt=en-US&w=d5622f5e,eeaafc08
DisplayUrl theroadtodelphi.wordpress.com/2011/09
DateTime 2012-03-05T17:17:00Z
Title Delphi Programming Blog
Description Here's how to place a check box into a DBGrid. Create visually moreattractive user interfaces for editing boolean fields inside a DBGrid. ...
Url http://delphi4all.blogfa.com/
CacheUrl http://cc.bingj.com/cache.aspx?q=delphi+programming+blogs&d=4525654810627103&mkt=en-US&w=63fcb281,3d45a160
DisplayUrl delphi4all.blogfa.com
DateTime 2012-03-25T01:10:00Z
As you probably note , the above source code only parses the result when the response includes a web search result, so now we are to expand this code to support and parse any result returned by the Bing API (Web search, Images, Videos, etc.)
Check the next full sample
{$APPTYPE CONSOLE}
{$R *.res}
uses
MSXML,
SysUtils,
ActiveX,
ComObj,
Variants;
type
TBingApiSearchSource=(Image, News, RelatedSearch, Spell, Video, Web);
TBingApiSearchSources = Set of TBingApiSearchSource;
const
BingApiSearchSourcesStr : Array [TBingApiSearchSource] of string = ('Image','News','RelatedSearch','spell','Video','Web');
procedure GetBingInfoExtXML(const SearchKey : string;const NumberOfResults:integer=1;Source : TBingApiSearchSources=[Web]);
const
//this AppId if for demo only please be nice and use your own , it's easy get one from here http://msdn.microsoft.com/en-us/library/ff512386.aspx
ApplicationID= '73C8F474CA4D1202AD60747126813B731199ECEA';
URI='http://api.bing.net/xml.aspx?AppId=%s&Version=2.2&Market=en-US&Query=%s&Sources=%s&web.count=%d&xmltype=AttributeBased';
COMPLETED=4;
OK =200;
var
XMLHTTPRequest : IXMLHTTPRequest;
XMLDOMDocument : IXMLDOMDocument;
XMLDOMNode : IXMLDOMNode;
XMLDOMNodeList : IXMLDOMNodeList;
LIndex : Integer;
LSource : TBingApiSearchSource;
Sources : string;
begin
XMLHTTPRequest := CreateOleObject('MSXML2.XMLHTTP') As IXMLHTTPRequest;
try
Sources:='';
for LSource in Source do
Sources:=Sources+BingApiSearchSourcesStr[LSource]+'+';
Delete(Sources,Length(Sources),1);
XMLHTTPRequest.open('GET', Format(URI,[ApplicationID, SearchKey, Sources, NumberOfResults]), False, EmptyParam, EmptyParam);
XMLHTTPRequest.send('');
if (XMLHTTPRequest.readyState = COMPLETED) and (XMLHTTPRequest.status = OK) then
begin
XMLDOMDocument := XMLHTTPRequest.responseXML As IXMLDOMDocument2;
if Web in Source then
begin
XMLDOMNode := XMLDOMDocument.selectSingleNode('//web:Web');
Writeln(Format('WebSearch results total found %s',[String(XMLDOMNode.attributes.getNamedItem('Total').Text)]));
Writeln;
XMLDOMNodeList := XMLDOMNode.selectNodes('//web:WebResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Title %s',[String(XMLDOMNode.attributes.getNamedItem('Title').Text)]));
Writeln(Format('Description %s',[String(XMLDOMNode.attributes.getNamedItem('Description').Text)]));
Writeln(Format('Url %s',[String(XMLDOMNode.attributes.getNamedItem('Url').Text)]));
Writeln(Format('CacheUrl %s',[String(XMLDOMNode.attributes.getNamedItem('CacheUrl').Text)]));
Writeln(Format('DisplayUrl %s',[String(XMLDOMNode.attributes.getNamedItem('DisplayUrl').Text)]));
Writeln(Format('DateTime %s',[String(XMLDOMNode.attributes.getNamedItem('DateTime').Text)]));
Writeln;
end;
end;
if Image in Source then
begin
XMLDOMNode := XMLDOMDocument.selectSingleNode('//mms:Image');
Writeln(Format('Images results total found %s',[String(XMLDOMNode.attributes.getNamedItem('Total').Text)]));
Writeln;
XMLDOMNodeList := XMLDOMNode.selectNodes('//mms:ImageResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Title %s',[String(XMLDOMNode.attributes.getNamedItem('Title').Text)]));
Writeln(Format('MediaUrl %s',[String(XMLDOMNode.attributes.getNamedItem('MediaUrl').Text)]));
Writeln(Format('Url %s',[String(XMLDOMNode.attributes.getNamedItem('Url').Text)]));
Writeln(Format('DisplayUrl %s',[String(XMLDOMNode.attributes.getNamedItem('DisplayUrl').Text)]));
Writeln(Format('Width %s',[String(XMLDOMNode.attributes.getNamedItem('Width').Text)]));
Writeln(Format('Height %s',[String(XMLDOMNode.attributes.getNamedItem('Height').Text)]));
Writeln(Format('FileSize %s',[String(XMLDOMNode.attributes.getNamedItem('FileSize').Text)]));
Writeln(Format('ContentType %s',[String(XMLDOMNode.attributes.getNamedItem('ContentType').Text)]));
XMLDOMNode:=XMLDOMNode.selectSingleNode('//mms:Thumbnail');
if XMLDOMNode<>nil then
begin
Writeln(' Thumbnail Info');
Writeln(Format(' Url %s',[String(XMLDOMNode.attributes.getNamedItem('Url').Text)]));
Writeln(Format(' ContentType %s',[String(XMLDOMNode.attributes.getNamedItem('ContentType').Text)]));
Writeln(Format(' Width %s',[String(XMLDOMNode.attributes.getNamedItem('Width').Text)]));
Writeln(Format(' Height %s',[String(XMLDOMNode.attributes.getNamedItem('Height').Text)]));
Writeln(Format(' FileSize %s',[String(XMLDOMNode.attributes.getNamedItem('FileSize').Text)]));
end;
Writeln;
end;
end;
if Video in Source then
begin
XMLDOMNode := XMLDOMDocument.selectSingleNode('//mms:Video');
Writeln(Format('Video results total found %s',[String(XMLDOMNode.attributes.getNamedItem('Total').Text)]));
Writeln;
XMLDOMNodeList := XMLDOMNode.selectNodes('//mms:VideoResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Title %s',[String(XMLDOMNode.attributes.getNamedItem('Title').Text)]));
Writeln(Format('PlayUrl %s',[String(XMLDOMNode.attributes.getNamedItem('PlayUrl').Text)]));
Writeln(Format('SourceTitle %s',[String(XMLDOMNode.attributes.getNamedItem('SourceTitle').Text)]));
Writeln(Format('RunTime %s',[String(XMLDOMNode.attributes.getNamedItem('RunTime').Text)]));
Writeln(Format('Click Through Page Url %s',[String(XMLDOMNode.attributes.getNamedItem('ClickThroughPageUrl').Text)]));
XMLDOMNode:=XMLDOMNode.selectSingleNode('//mms:StaticThumbnail');
if XMLDOMNode<>nil then
begin
Writeln(' Static Thumbnail Info');
Writeln(Format(' Url %s',[String(XMLDOMNode.attributes.getNamedItem('Url').Text)]));
Writeln(Format(' ContentType %s',[String(XMLDOMNode.attributes.getNamedItem('ContentType').Text)]));
Writeln(Format(' Width %s',[String(XMLDOMNode.attributes.getNamedItem('Width').Text)]));
Writeln(Format(' Height %s',[String(XMLDOMNode.attributes.getNamedItem('Height').Text)]));
Writeln(Format(' FileSize %s',[String(XMLDOMNode.attributes.getNamedItem('FileSize').Text)]));
end;
Writeln;
end;
end;
if RelatedSearch in Source then
begin
Writeln('Related Searchs');
Writeln;
XMLDOMNodeList := XMLDOMDocument.selectNodes('//rs:RelatedSearchResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Title %s',[String(XMLDOMNode.attributes.getNamedItem('Title').Text)]));
Writeln(Format('Url %s',[String(XMLDOMNode.attributes.getNamedItem('Url').Text)]));
end;
end;
if News in Source then
begin
XMLDOMNode := XMLDOMDocument.selectSingleNode('//news:News');
Writeln(Format('News results total found %s',[String(XMLDOMNode.attributes.getNamedItem('Total').Text)]));
Writeln;
XMLDOMNodeList := XMLDOMNode.selectNodes('//news:NewsResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Title %s',[String(XMLDOMNode.attributes.getNamedItem('Title').Text)]));
Writeln(Format('Url %s',[String(XMLDOMNode.attributes.getNamedItem('Url').Text)]));
Writeln(Format('Source %s',[String(XMLDOMNode.attributes.getNamedItem('Source').Text)]));
Writeln(Format('Snippet %s',[String(XMLDOMNode.attributes.getNamedItem('Snippet').Text)]));
Writeln(Format('Date %s',[String(XMLDOMNode.attributes.getNamedItem('Date').Text)]));
Writeln(Format('BreakingNews %s',[String(XMLDOMNode.attributes.getNamedItem('BreakingNews').Text)]));
Writeln;
end;
end;
if Spell in Source then
begin
XMLDOMNode := XMLDOMDocument.selectSingleNode('//spl:Spell');
Writeln(Format('Spell results total found %s',[String(XMLDOMNode.attributes.getNamedItem('Total').Text)]));
Writeln;
XMLDOMNodeList := XMLDOMNode.selectNodes('//spl:SpellResult');
for LIndex:=0 to XMLDOMNodeList.length-1 do
begin
XMLDOMNode:=XMLDOMNodeList.item[LIndex];
Writeln(Format('Value %s',[String(XMLDOMNode.attributes.getNamedItem('Value').Text)]));
Writeln;
end;
end;
end;
finally
XMLHTTPRequest := nil;
end;
end;
begin
try
CoInitialize(nil);
try
GetBingInfoExtXML('Delphi programming', 1, [Web, Video, Image]);
finally
CoUninitialize;
end;
except
on E:EOleException do
Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.
-33.636934
-70.679350