Friday, March 18, 2011

Bing Web Search in C#

image

This blog post contains a C# wrapper of the Bing API (Microsoft’s internet search engine). In my previous post I presented a similar wrapper for Google’s Custom Search API, successor to the Web Search API.  I was frankly less than impressed with Google custom search API as it appears to be engineered to favor specific themes and/or domains.

To use use this code below you must first create a Live ID and request an App ID.  This page provided more details.

How to use:

If you have a .NET 4.0 WPF application, like this:

<Window x:Class="WpfApplication6.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="myDataGrid" />
    </Grid>
</Window>

Then all you need to do to populate the window with internet search results about Albert Einstein is to called the asynchronous search method on BingSearch .

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();

        this.Loaded += (s, e) => {
            BingSearch search = new BingSearch() {
                AppId = "<Your App ID>"
            };
            search.SearchCompleted += (a, b) => {
                this.myDataGrid.ItemsSource = b.Response.Web.Results;
            };
            search.SearchAsync("Albert Einstein");               
        };
    }
}

Below is the source code to BingSearch, a class that provides both a synchronous and an asynchronous method to search the internet using Bing.

public class BingSearch {
    public BingSearch() {
        this.Count = 50;
        this.Offset = 0;
        this.Sources = "Web";
        this.Adult = BingSafeLevel.Moderate;
        this.Options = "DisableLocationDetection";
        this.WebOptions = "DisableHostCollapsing+DisableQueryAlterations";
    }
    //
    // PROPERTIES
    //
    public string AppId { get; set; }
    public string Sources { get; set; }
    public int Count { get; set; }
    public int Offset { get; set; }
    public BingSafeLevel Adult { get; set; }
    public string Options { get; set; }
    public string WebOptions { get; set; }
    //
    // EVENTS
    //
    public event EventHandler<BingSearchEventArgs> SearchCompleted;
    //
    // METHODs
    //
    protected void OnSearchCompleted(BingSearchEventArgs e) {
        if (this.SearchCompleted != null) {
            this.SearchCompleted(this, e);
        }
    }
    public BingSearchResponse Search(string search) {
        //
        UriBuilder builder = this.BuilderQuery(search);

        // Submit Request
        WebClient w = new WebClient();
        string result = w.DownloadString(builder.Uri);
        if (string.IsNullOrWhiteSpace(result)) { return null; }

        // Desearealize from JSON to .NET objects
        Byte[] bytes = Encoding.Unicode.GetBytes(result);
        MemoryStream memoryStream = new MemoryStream(bytes);
        DataContractJsonSerializer dataContractJsonSerializer =
new DataContractJsonSerializer(typeof(BingWebResponse));
        BingWebResponse bingWebResponse =
dataContractJsonSerializer.ReadObject(memoryStream) as BingWebResponse;
        memoryStream.Close();

        return bingWebResponse.SearchResponse;
    }
    public void SearchAsync(string search) {
        // Get URL
        UriBuilder builder = this.BuilderQuery(search);

        // Submit Request
        WebClient w = new WebClient();
        w.DownloadStringCompleted += (a, b) => {
            // Check for errors
            if (b == null) { return; }
            if (b.Error != null) { return; }
            if (string.IsNullOrWhiteSpace(b.Result)) { return; }

            // Desearealize from JSON to .NET objects
            Byte[] bytes = Encoding.Unicode.GetBytes(b.Result);
            MemoryStream memoryStream = new MemoryStream(bytes);
            DataContractJsonSerializer dataContractJsonSerializer =
new DataContractJsonSerializer(typeof(BingWebResponse));
            BingWebResponse bingWebResponse =
dataContractJsonSerializer.ReadObject(memoryStream) as BingWebResponse;
            memoryStream.Close();

            // Raise Event
            this.OnSearchCompleted(
                new BingSearchEventArgs() {
                    Response = bingWebResponse.SearchResponse
                }
            );
        };
        w.DownloadStringAsync(builder.Uri);
    }
    private UriBuilder BuilderQuery(string search) {
        // Build Query
        string query = string.Empty;
        query += string.Format("AppId={0}", this.AppId);
        query += string.Format("&Query={0}", search);
        query += string.Format("&Sources={0}", this.Sources);
        query += string.Format("&Adult={0}", this.Adult);
        query += string.Format("&Options={0}", this.Options);
        query += string.Format("&Web.Count={0}", this.Count);
        query += string.Format("&Web.Offset={0}", this.Offset);
        query += string.Format("&Web.Options={0}", this.WebOptions);
        query += string.Format("&JsonType={0}", "raw");

        // Construct URL
        UriBuilder builder = new UriBuilder() {
            Scheme = Uri.UriSchemeHttp,
            Host = "api.bing.net",
            Path = "json.aspx",
            Query = query
        };

        // Return URL
        return builder;
    }
}

public enum BingSafeLevel {
    Off, Moderate, Strict
}

public class BingSearchEventArgs : EventArgs {
    public BingSearchResponse Response { get; set; }
}

[DataContract]
public class BingWebResponse {
    [DataMember(Name = "SearchResponse")]
    public BingSearchResponse SearchResponse { get; set; }
}

[DataContract]
public class BingSearchResponse {
    [DataMember(Name = "Version")]
    public string Version { get; set; }
    [DataMember(Name = "Query")]
    public BingQuery Query { get; set; }
    [DataMember(Name = "Web")]
    public BingWeb Web { get; set; }
}

[DataContract]
public class BingQuery {
    [DataMember(Name = "SearchTerms")]
    public string SearchTerms { get; set; }
}

[DataContract]
public class BingWeb {
    [DataMember(Name = "Total")]
    public int Total { get; set; }
    [DataMember(Name = "Offset")]
    public int Offset { get; set; }
    [DataMember(Name = "Results")]
    public List<BingResult> Results { get; set; }
}

[DataContract]
public class BingResult {
    [DataMember(Name = "Title")]
    public string Title { get; set; }
    [DataMember(Name = "Description")]
    public string Description { get; set; }
    [DataMember(Name = "Url")]
    public string Url { get; set; }
    [DataMember(Name = "DisplayUrl")]
    public string DisplayUrl { get; set; }
    [DataMember(Name = "DateTime")]
    public string DateTimeOriginal { get; set; }
    // Convert Date
    public DateTime? DateTimeConverted {
        get {
            if (string.IsNullOrWhiteSpace(this.DateTimeOriginal)) { return null; }
            DateTime d;
            if (!DateTime.TryParse(this.DateTimeOriginal, out d)) { return null; }
            return d;
        }
    }
}

6 comments:

  1. Hi

    Thank you for your great article.
    I am using Bing Search API , which I like more than Google API , however I am still confused about what are the good ways to aggregate all results in one array?? API gives us the ability to navigate between results based on pages, and offsets , however there is no clear way to aggregate all results even thousands in one array or list.

    hope you can help

    ReplyDelete
  2. @anonymous. I think you are restricted to making multiple requests and aggregating responses as they arrive.

    ReplyDelete
  3. With havin so much content do you ever run into any problems of plagorism or copyright violation? My website has a lot of completely unique content I've either authored myself or outsourced but it seems a lot of it is popping it up all over the web without my agreement. Do you know any solutions to help reduce content from being ripped off? I'd genuinely appreciate it. Reply

    ReplyDelete
  4. BlueHost is ultimately the best web-hosting company with plans for all of your hosting needs.

    ReplyDelete