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
- JSON http://api.bing.net/json.aspx?AppId= YOUR_APPID &Version=2.2&Market=en-US&Query=testign&Sources=web+spell&Web.Count=1
- XML http://api.bing.net/xml.aspx?AppId= YOUR_APPID &Version=2.2&Market=en-US&Query=testign&Sources=web+spell&Web.Count=1
- SOAP http://api.bing.net/search.wsdl?AppID=YourAppId&Version=2.2
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.
May 10, 2012 at 4:35 pm
Hi.Thank you for sharing this example. When I try to use your example inside a form it always return an exception EAccessViolation if you use a high count value (like 100). Can you help avoiding it?
May 10, 2012 at 6:10 pm
Send me a sample project with the issue.
May 10, 2012 at 7:03 pm
I’m just testing a way to interface Bing (and Google) search. You can find the sample in: http://dl.dropbox.com/u/2208225/Spider.zip
If you try with a count of 100 it always crashes (at least here). You can PM me at miguel dot enguica at me dot com . Thank you for your time!
May 11, 2012 at 11:35 am
The code access violation is caused because you are trying to get a value of a IXMLDOMNode which is nil, this is because sometimes the response of the bing service can contain an status error instead of seacrh result.
Check this code which add the nil validation for the IXMLDOMNode and also handle the status error returnes by the bing service.
May 11, 2012 at 2:23 pm
Hi Rodrigo. Once again thank you for your time! The exception error is gone, but when I perform a search asking for more than 50 results (ex: param web.count=51) the reply from the Bing server is always:
microsoft
1002
Parameter has invalid value.SearchRequest.Web.Count51http://msdn.microsoft.com/en-us/library/dd251042.aspxWhen you enter a web.count under 50 it all works. Is there any limitation from the Bing API to retrieve only the first 50 results?
May 11, 2012 at 2:30 pm
To retrieve more results you must play with the parameter web.offset as described in:
http://msdn.microsoft.com/en-us/library/dd250920
Once again, thank you!
May 11, 2012 at 2:41 pm
Yes the Web.Count defines the results by page, using the web.offset parameter you can access additional pages of results.
Pingback: Using the Bing search API (Windows Azure Marketplace version) from Delphi « The Road to Delphi – a Blog about programming