はじめに
Azure Cognitive Services のカテゴリの一つに Web 検索があります。Web 検索では、Microsoft 社の Web 検索エンジンである Bing の検索を API 経由で利用できる Bing Search API がありましたが、これが、2020 年 10 月 30 日以降、利用できなくなったようです。
これ以前に、Azure Cognitive Services 内で Bing Search をインスタンス化している場合は、以降 3 年間は利用できるようですが、Azure Cognitive Services の Bing Search を新規に作成、利用することはできなくなりました。
今後、新規に Bing Search API を利用するには、Azure マーケットプレイスにある "Bing Search v7" を利用します。公式ページでも新規利用時のサービス有効化方法として、以下のページが案内されています。
これまでとの変更点としては、Bing Search API のエンドポイントが、以下のように変更されました。ドメインの cognitive 部分が、bing に変更されています。
.NET から、Bing Search API を呼び出す場合は、SDK(クライアント ライブラリ) が利用できました。WebSearch を行いたい場合は、"Microsoft.Azure.CognitiveServices.Search.WebSearch" というライブラリが NuGet 経由で入手可能で、これを使って非常に簡単に、Bing Search API を利用できたのですが、2020 年 11 月 21 日時点では、新しいエンドポイントに対して、このライブラリが正しく動作しません。
現状では、Bing Search API クライアント ライブラリの最終更新日は、2018/03/22 となっており、今回の移行にあわせたクライアント ライブラリの更新が行われていないようでした。
よって、今回は、Bing Search API をクライアント ライブラリを使わず、利用する方法を説明したいと思います。HttpClient で、直接 API をコールします。今回は、.NET 5 を使用しましたが、.NET Core 3.1 でも動作すると思います。
Bing Search v7 の有効化
Azure ポータルの [リソースの作成] から、"bing search" を検索し、[Bing Search v7] を選択して、Bing Search のインスタンスを作成します。
価格レベルについては、以下のサイトに記載があります。無料の Free も選択できて、1,000 トランザクション/月まで利用が可能です。
Bing Search のインスタンスが作成されたら、[キーとエンドポイント] を選択し、[キー 1] または、[キー 2] の文字列をコピーしておきます。このキーは、後ほど、API 呼び出しの際に、HTTP ヘッダーに入れて使用します。
BingClient クライアント ライブラリの実装
以降で、.NET Core コンソール アプリ(.NET 5)で、実装を行っていきます。
まず、BingClient クラスと、検索結果を格納する各クラスを定義します。
public class BingClient
{
// Web ページ検索結果
public class WebPage
{
public string Name;
public string Url;
public string Snippet;
public DateTimeOffset DateLastCrawled;
}
// 画像検索結果
public class Image
{
public string Name;
public string ThumbnailUrl;
public string ContentUrl;
public DateTimeOffset DatePublished;
}
// 動画検索結果
public class Video
{
public string Name;
public string ThumbnailUrl;
public string ContentUrl;
public string Description;
public string Publisher;
public string Creator;
public long? ViewCount;
public DateTimeOffset DatePublished;
}
}
次に、BingClient クラスのプライベートフィールドとコンストラクタを実装します。コンストラクタでは、Bing Search API のサブスクリプション キーを受け取りフィールドに保存しておきます。
また、HTTP 要求のために、HttpClient のインスタンスもフィールドに保存しておきます。
public class BingClient
{
....
private static readonly HttpClient _httpClient = new HttpClient();
private readonly string _endpoint = "https://api.bing.microsoft.com/v7.0";
private readonly string _subscriptionKey = null;
public BingClient(string subscriptionKey)
{
this._subscriptionKey = subscriptionKey;
}
}
次に、BingClient クラスに各検索を実行するメソッドを実装していきます。レスポンスは、JSON で返ってきますので、Json.NET で処理します。コードを実装する前に、Json.NET(Newtonsoft.Json) を NuGet パッケージでインストールしておいてください。
先に、Search メソッドで、各検索要求の共通部分を実装しておきます。
public class BingClient
{
....
private async Task<dynamic> Search(string url, string subscriptionKey)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(url));
request.Headers.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
HttpResponseMessage response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
string responseString = await response.Content.ReadAsStringAsync();
dynamic json = JsonConvert.DeserializeObject<dynamic>(responseString);
return json;
}
}
次に、Web ページ検索を実行する SearchWeb メソッドを実装します。引数に、検索キーワード、マーケットコード、検索結果数をとります。
マーケットコードは、Market and language codes used by Bing Web Search API に定義があります。
また、検索結果の各プロパティは、最小限のものを入れていますので、必要に応じて、Web Search API v7 response objects
の "WebPage" を参照して、クラス、処理を拡張してください。
public class BingClient
{
....
public async Task<IEnumerable<WebPage>> SearchWeb(string keyword, string market = "ja-JP", int resultCount = 10)
{
string endpoint = $"{_endpoint}/search";
string url = $"{endpoint}?q={Uri.EscapeDataString(keyword)}&mkt={market}&responseFilter=Webpages&count={resultCount}";
dynamic json = await this.Search(url, _subscriptionKey);
var webPages = (json.webPages.value as IEnumerable<dynamic>).Select(page =>
new WebPage()
{
Name = page.name,
Url = page.url,
Snippet = page.snippet,
DateLastCrawled = DateTime.SpecifyKind(page.dateLastCrawled.Value, DateTimeKind.Utc),
});
return webPages;
}
}
同様に、画像検索、動画検索も実装していきます。
画像検索の結果の各プロパティの定義は、Image Search APIs v7 response objects の Image に、動画検索の結果の各プロパティは、Video Search APIs v7 response objects
の Video を参照してください。
public class BingClient
{
....
public async Task<IEnumerable<Image>> SearchImage(string keyword, string market = "ja-JP", int resultCount = 10)
{
string endpoint = $"{_endpoint}/images/search";
string url = $"{endpoint}?q={Uri.EscapeDataString(keyword)}&mkt={market}&count={resultCount}";
dynamic json = await this.Search(url, _subscriptionKey);
var images = (json.value as IEnumerable<dynamic>).Select(img =>
new Image()
{
Name = img.name,
ThumbnailUrl = img.thumbnailUrl,
ContentUrl = img.contentUrl,
DatePublished = DateTime.SpecifyKind(img.datePublished.Value, DateTimeKind.Utc),
});
return images;
}
public async Task<IEnumerable<Video>> SearchVideo(string keyword, string market = "ja-JP", int resultCount = 10)
{
string endpoint = $"{_endpoint}/videos/search";
string url = $"{endpoint}?q={Uri.EscapeDataString(keyword)}&mkt={market}&count={resultCount}";
dynamic json = await this.Search(url, _subscriptionKey);
var videos = (json.value as IEnumerable<dynamic>).Select(vdo =>
new Video()
{
Name = vdo.name,
ThumbnailUrl = vdo.thumbnailUrl,
ContentUrl = vdo.contentUrl,
Description = vdo.description,
Publisher = vdo.publisher?[0]?.name,
Creator = vdo.creator?.name,
ViewCount = vdo.viewCount,
DatePublished = DateTime.SpecifyKind(vdo.datePublished.Value, DateTimeKind.Utc),
});
return videos;
}
BingClient クライアント ライブラリの利用
実装した BingClient クライアント ライブラリを利用します。
"yourKey" には、先に Azure ポータルで有効化した Bing Search v7 のキーを設定してください。
class Program
{
static async Task Main(string[] args)
{
string subscriptionKey = "<yourKey>";
string keyword = "猫";
BingClient bingClient = new BingClient(subscriptionKey);
var webPages = await bingClient.SearchWeb(keyword);
webPages.ToList().ForEach(w =>
{
Console.WriteLine($"ページ タイトル: {w.Name}");
Console.WriteLine($"概要: {w.Snippet}");
Console.WriteLine($"URL: {w.Url}");
Console.WriteLine();
});
var images = await bingClient.SearchImage(keyword);
images.ToList().ForEach(img =>
{
Console.WriteLine($"画像タイトル: {img.Name}");
Console.WriteLine($"URL: {img.ContentUrl}");
Console.WriteLine();
});
await Task.Delay(1000);
var videos = await bingClient.SearchVideo(keyword);
videos.ToList().ForEach(v =>
{
Console.WriteLine($"動画タイトル: {v.Name}");
Console.WriteLine($"概要: {v.Description}");
Console.WriteLine($"サムネイル URL: {v.ThumbnailUrl}");
Console.WriteLine($"URL: {v.ContentUrl}");
Console.WriteLine($"視聴数: {v.ViewCount}");
Console.WriteLine();
});
var news = await bingClient.SearchNews(keyword);
news.ToList().ForEach(n =>
{
Console.WriteLine($"ニュース タイトル: {n.Name}");
Console.WriteLine($"概要: {n.Description}");
Console.WriteLine($"サムネイル URL: {n.ThumbnailUrl}");
Console.WriteLine($"URL: {n.Url}");
Console.WriteLine();
});
Console.ReadKey();
}
}
完全なコードは、以下の GitHub リポジトリに登録してあります。