【手抜きUI】今更C#でBingの画像サーチしてみました（50枚以上対応）


ある所に、C言語でドライバとかメンテしていれば良かったおっさんがいました。彼は紆余曲折の末、IoT系の部署に放り込まれ、やれAWSだデータ収集だと新しい仕事でテンぱっています。

その流れで機械学習とか面白そうだと感じた彼は、とりあえず流行りのニューラルネット系でもいじってみようかなと考えました。この分野は画像処理が進んでますので、手始めに時崎狂三と鳶一折紙の区別でもさせてみようかと彼は思いました。が、どうも元データをどう集めるか結構面倒である事に気がついたのです。

あっちこっちググりまくると、今（2016/7月）だとBingのWebAPIをC#から扱うのがまぁ簡単そうに見えました。なので、実際にやってみたのがこれです。

# Bingについて

[本家の説明はここです](https://datamarket.azure.com/dataset/bing/search)
Azure系のWebWPIですね。要は。これを使うと検索が出来ますというもの。

# 利用するには

sadapon2008さんの[PHPからBing Search APIを使って画像を収集する](http://qiita.com/sadapon2008/items/38628ae8266495b3d3a3)という記事がとても良くまとまっていますので、是非ご一読下さい。注意点は以下。

* 無料では月5000回です。
* 1回50件までなので、100件欲しい場合は2回消費するシステムです。

ちなみに[Azure Marketplaceサービス自体はここ](https://datamarket.azure.com/home)、で今回使うBingは「データ」をクリックすると行きます。

# C#から利用するには

以下のリンクを見たり、ダウンロードしたりでやっていく感じと思います。

* [Bing Search APIデータ](https://datamarket.azure.com/dataset/bing/search)
* [C#のライブラリ](https://datamarket.azure.com/dataset/explore/getproxy/5ba839f1-12ce-4cce-bf57-a49d98d29a44)
* [Migrating Bing Search API Applications(docx)](http://go.microsoft.com/fwlink/?LinkID=248077)

Migrating Bing Search API Applicationsを見ると分かりますがC#のライブラリを使うと割と簡単に必要なデータが集められそうですが、以下の点に注意が必要です（これはdocxにも記述があります）

* ```using System;``` と```using System.Net;``` 特に後者は多分自分で追加が必要
*  System.Data.Services.Client を参照する事。

あと、私の環境だけかもしれませんが、VisualStudio2015では以下の箇所でエラーが出たので少しいじりました。

```csharp
// docの記述
 var imageQuery = bingContainer.Image(word, null, null, null, null, null); 

// こうすればパスる（null1個追加)
var imageQuery = bingContainer.Image(word, null, null, null, null, null, null);
```

これで動きますが・・

### 50件目以降問題

Bingでは1回の検索で50件までの応答が戻ります。50件でディープラーニングを名乗るのはさすがに恥ずかしいため（※）、もう少し欲しくなるじゃないですか。__でもその方法が分からない・・__
→※　まぁ学習済データを使って20件でディープラーニングしたとかいう話も聞いた事はありますが。。

そこでカスな私がググるとこんなのが。

[Bing search api returns only 50 results per request ](https://social.msdn.microsoft.com/Forums/en-US/5889d249-90b0-4250-9f00-5d48da0471f5/bing-search-api-returns-only-50-results-per-request-solved?forum=DataMarket)

↑にもありますが[こっちのADM_SCHEMA_GUIDE](http://www.bing.com/webmaster/content/developers/ADM_SCHEMA_GUIDE.docx)に記載があって、これに従った記述が必要です。具体的には以下ですね。以下は51件目からよこせと指示しているコードです。

```csharp
imageQuery = imageQuery.AddQueryOption("$skip", 50);
```

ちなみにこれは[Bing Search APIデータ](https://datamarket.azure.com/dataset/bing/search)からリンクされている[FAQ](http://go.microsoft.com/fwlink/?LinkID=252146)でも記載がありました。良く見ろって事ですねごめなさい。

# C#で検索コードを書いてみる。

という事で実際に書いてみました。さすがにライブラリを使えば非常に簡単でこんなので動きました。ほぼほぼサンプルのままですが。

```csharp
// ↓をやってます。
using Systemt;
using System.Net;

        private void CallBingSearchAPI(string word)
        {
            // 以下で定義されたメンバ変数を使って検索、結果を格納します。

            // 入力
            // 引数word が検索単語です。
            // 以下のコードで使っている定義です。呼び出す前に設定済です。
            //   private const string BING_SEARCH_API_URL = "https://api.datamarket.azure.com/Bing/Search/";
            //   private string m_BingKey = "プライマリ アカウント キー";
            //   private int m_count; // 何回目の検索であるかが設定されています。

            // 出力
            // 以下に成功失敗が入ります。
            //   private bool m_success;
            // 失敗の際には以下にエラー（例外メッセージ）が入ります。
            //   private string m_exceptionMeggase;
            // 成功の際には、以下の個数が入ります。
            //   private int m_numberOfItem;
            // 以下にタイトルと画像のURLをそれぞれ入れます。
            //   private string[] m_title = new string[50];
            //   private string[] m_mediaUri = new string[50];

            m_numberOfItem = 0;
            try
            {
                m_BingContainer = new Bing.BingSearchContainer(new Uri(BING_SEARCH_API_URL));
                m_BingContainer.Credentials = new NetworkCredential(m_BingKey, m_BingKey);

                var imageQuery = m_BingContainer.Image(word, null, null, null, null, null, null);
                imageQuery = imageQuery.AddQueryOption("$top", 50);
                imageQuery = imageQuery.AddQueryOption("$skip", m_count *50);
                var imageResults = imageQuery.Execute();
                foreach (Bing.ImageResult result in imageResults)
                {
                    m_title[m_numberOfItem] = result.Title;
                    m_mediaUri[m_numberOfItem] = result.MediaUrl;
                    m_numberOfItem++;
                }
                m_success = true;

            }
            catch (Exception e)
            {
                m_exceptionMeggase = e.Message;
                m_success = false;
            }
        }
```

これは実際に動かしたアプリから該当部分を抜粋して、コピペしたものです。

オプション系は一切使っていませんので、使う際はdocx系など見ながら設定すれば良いのかなと思っています。

コードではタイトル（ブラウザでBIng検索した際に出るアレです）と画像へのURLのみ取り出していますが、もちろん他の情報を取り出す事も出来ます。

# 動作確認用アプリケーション

上記の確認が行える動作確認用にアプリを、[これ](http://qiita.com/GDaigo/items/5b7d7cbf3429b271ccf5)の宣伝もかねて作ってみました。以下の機能を持ちます。

* 任意の単語で画像検索します。
* 検索結果の一覧をHTMLで表示します。クリックで画像を見ることも出来ます。
* 50件以上でも検索可能です。
* 過去の情報も持っていますので、例えば200件検索した後、51-100の一覧を再度出すことが出来ます（トランザクション消費はしません）。
* ただし検索単語を変更すると一覧は消えます。
* 検索結果をjsonファイルで提供します。jsonファイルは単語が変更されても残ります（同一単語で再検索すると上書きされます）
* 簡易的ではありますが、プロ生ちゃんが案内してくれます。

# 要件

* VisualStudio2015を使います。
* WIndows7,10 (共に64bit）、Windows8.1(32bit)で確認しています。
* 開発者がアホのため、ちょっと設定とか必要がです。それに付き合ってくれる人
* 出来立てなので生暖かい目で見守ってくれる人

# ダウンロード

[CuiHelper](http://qiita.com/GDaigo/items/5b7d7cbf3429b271ccf5)を使っていますので、まずはHTML系のコンテンツが必要です。

* [GitHubのここ](https://github.com/gdaigo82/cui_helper)にあるCuiHelperContentsをフォルダ毎落としてどこかに置いて下さい。
* 次にアプリ本体のVisualStudioプロジェクトを[GitHubのここ](https://github.com/gdaigo82/Bing_ImageSearch)から落として下さい。

# ビルド

1. BingImageSearch\BingImageSearch\Resourcesフォルダにあるsetting.jsonをエディタで開けます。

```json
{
    "parameters":{
        "ImagePath":"C:\\CuiHelperContents\\img\\",
        "ContentsPath": "C:\\CuiHelperContents\\",
        "PrimayKey" : "Please Your Primary Key"
     }
}
```
2.各項目を入力します。

 * ImagePathは落とした CuiHelperContents\\img\\ フォルダを上記例にならってフルパスで指定します（\は２つ続けて下さい）
 * ContentsPathも同様に落とした CuiHelperContents\\ フォルダをフルパスで指定します。
 * PrimaryKeyにプライマリ アカウント キーの文字列を入れて下さい。[ここ](https://datamarket.azure.com/dataset/explore/bing/search)の「プライマリ アカウント キー 表示する」をクリックすると出てきます。

3.後はVisualStudioでビルドすればBingImageSearch.exeが出来ます。

# 使い方

まず最初に謝罪。大変申し訳ございませんが、この確認で何回かトランザクションを消費してしまいます。ご理解の上、進んで戴ければ幸いです。

1. BingImageSearch.exeをダブルクリックで起動します。DebugモードならVisualStuduioの”デバッグの開始”でも良いです。
2. 暫くすると[Bing Search API](https://datamarket.azure.com/dataset/explore/bing/search)の画面に行こうとします。必要であればログインして下さい。もしかすると、ここでうまく立ち上がらないかもしれません。setting.jsonに問題がなければ、大変お手数ですが再度立ち上げてみて下さい。
3. ログインが終わりますと、Bing Search APIの画面が出ます。ここでトランザクションの残りを確認する事も出来ます。
4. __ここでトランザクションが消費されます。__画面上のTextBoxに検索したい単語を入れ、ComboBoxから「Search」を選択し、GOをクリックします。
![BingSearchFirst.png](https://qiita-image-store.s3.amazonaws.com/0/129334/9892d0f8-4ffe-3a09-3073-3139054633c9.png)
5. 暫くすると結果が以下のように表示されます。
![BingSearchResult.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/4f5e5cc5-0824-1a4b-a2d0-0969c7d7c9e7.png)
6. 青くURLが表示されていると思いますので、そこをクリックすると以下のように画像が出ます。
![BingSearchPic.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/70d0cf04-e2b5-f342-b2e4-5e2431f3b321.png)
7. 元の検索結果に戻りたい場合はComboBoxから「Back」を選んでGOをクリックして下さい。Backで最後に見た結果の画面に戻る事ができます（BACKではトランザクションは消費されません）
8. ちなみに検索結果のHTMLはCuiHelperContentsフォルダにbing(数字).htmlとして作成されますので、後から見ることが可能です。
9. 更にBingImageSearch.exeのあるフォルダにはResourcesフォルダがあるかと思います。その中には（検索単語）_（数字）.json という名前のJSONファイルが以下のような内容で作成されています。

```json
{"data":[
{"id":1, "url":"http://pronama.azurewebsites.net/wp-content/uploads/2013/08/sato.png"},
{"id":2, "url":"http://i1.wp.com/pronama.azurewebsites.net/wp-content/uploads/2015/09/2015-09-10_2048x1280_buildinsider_2.png?fit=1200%2C1200"},
{"id":3, "url":"http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-71-33-metablogapi/2021.image_5F00_2F586C08.png"},
  // :  以下続きます。
```

10.__ここでトランザクションが消費されます。__51枚目以降が欲しければ、ComboBoxからNextをサーチすれば、続いて検索します。必要ならば繰り返して下さい。__これは次にSearchを選択するまで続けることが出来ます。__
11.複数回繰り返した後、例えば60枚目の確認がしたい場合は下図のようにTextBoxに数値を入れてENTERキーを押します。
![BingSearchSelect.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/47d92651-7111-4620-c817-f8f29b550dcd.png)
12.このように出てきます(実際にはTOP画面が出ます。これはスクロールしたのをキャプチャしてます）この操作ではトランザクションは消費されません。
![Bing60list.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/496df2f4-d51b-4262-6b37-719b365e05c7.png)
13.__ここでトランザクションが消費されます。__別のキーワードで画像を検索する際には、TextBoxに再び検索したい単語を入れて「Seach」を選択してください。
14.後は上記の操作を必要なだけ繰り返す感じです。また、「HOME」を選択すると最初のAPIの画面に戻ります。

注意が必要な事として、例えば「プロ生ちゃん」で200枚検索した後に「くいなちゃん」で検索しますと、内部情報をクリアして、一覧画面（具体的にはhtml）を上書きしますので、プロ生ちゃんの一覧画面を見る事が出来なくなります。これに対してJSONファイルは検索単語を変更しても元のデータが消える事はありません。但し同じ単語で再検索した場合は上書きされます。

# Bing関係のコードは？

SearchApplication.cs辺りを見てもらいますと、BingのAPI使った処理が記述されています。今はJSONで結果出力していますが、CSVに変えるのとかもここで割合簡単に出来るかと思います。

# ライセンス

以下使わせて戴きました。素晴らしいソフトウェアを提供して下さり、ありがとうございます。

 * 本プロダクトは[MITライセンス](https://opensource.org/licenses/MIT)とします。詳細はGitHubを。
 * [DynamicJson](https://dynamicjson.codeplex.com/)を利用しています。ライセンスは[Microsoft Public License](https://dynamicjson.codeplex.com/license)です。
 * bootstrapを利用しています。[MITライセンス](https://github.com/twbs/bootstrap/blob/master/LICENSE)です。
 * jQueryを利用しています。[MITライセンス](https://jquery.org/license/)です。
 * プロ生ちゃんの画像は[「プロ生ちゃん利用ガイドライン」](http://pronama.azurewebsites.net/pronama/guideline/)に従った利用が必要ですので、ご注意ください。
 * アイコンは[このサイトのbird-green](http://www.veryicon.com/icons/system/trainee/bird-green.html)を採用しています。商用利用に制限がありますので、ご注意下さい。
 * BingSearchContainer.csは[マイクロソフト社のプロキシ クラス ライセンス条項](https://datamarket.azure.com/proxyclassterms)での利用が必要です。
 * コードではありませんが、Web側のフォントは[Google Fonts](https://www.google.com/fonts#AboutPlace:about)を使っています。

以上です。