Bing Web Search APIで画像自動収集プログラムを作る

こんにちは。

これまでTwitterのツイート検索APIGoogle Custom Search APIを利用した画像自動収集プログラムをご紹介しました。

blog.wackwack.net

blog.wackwack.net

最終回となる今回は、Microsoftが提供するBing Web Search APIを利用した画像自動収集プログラムを紹介します。

サブスクリプションキーの取得

MicrosoftのBing Web Search APIは、Microsoft Azureと呼ばれるクラウドプラットフォームによって提供されます。

そしてBing Web Search APIを利用するには、サブスクリプションキーを取得する必要があります。

有効期限

最初に言っておくと、Bing Web Search API無料で使える期間は30日間です。

それ以降は有料プランへの切り替えが必要になります。なお、勝手に課金されることはありませんので、ご安心ください。

無料サブスクリプションキーの取得

それでは早速サブスクリプションキーを取得します。

まずはMicrosoft Azureの登録サイトにアクセスします。


アクセスしたら「Bing Search API v7」APIキーの取得」をクリックしましょう。
APIキーの取得


表示されたダイアログにて規約への同意と、利用する国・リージョンを選択して「次へ」をクリックします。
規約への同意


次に表示されるダイアログにて「Microsoft」「Facebook」「LinkedIn」「GitHub」のいずれかのアカウントでログインします。
ログイン

もしもアカウントを持っていない場合は、Azure無料アカウント登録よりMicrosoftアカウントを作成しましょう。


ログインできれば登録完了です。30日間有効のサブスクリプションキー(APIキー)が2つ発行されます。
サブスクリプションキー

画像自動収集プログラムの実装

私が実装したプログラム例を示します。実装はPython 3.6.1(Anaconda 4.4.0)で行っています。

GitHubにも掲載しています。
imagecrawler/bing_image_crawler.py at master · quotto/imagecrawler · GitHub

# -*- coding:utf-8 -*-
import math
import requests
import imgutil
import time

# image save path
path = "/path/to/save"
imgutil.mkdir(path)

url = "https://api.cognitive.microsoft.com/bing/v7.0/images/search"

# parameters
query = "キーワード"
count = 50      # 1リクエストあたりの最大取得件数 default:30 max:150
mkt = "ja-JP"   # 取得元の国コード

num_per = 10    # リクエスト回数(count * num_per=取得画像数)
offset = math.floor(count / num_per)    # ループ回数

subscriptionKey="xxxxxxxxxxxxxxxxxxx"    # Bing Search API Key

headers = {'Ocp-Apim-Subscription-Key':subscriptionKey}

for offset_num in range(offset):
    params = {'q':query,'count':count,'offset':offset_num*offset,'mkt':mkt}
    r = requests.get(url,headers=headers,params=params)
    data = r.json()
    for values in data['value']:
        image_url = values['contentUrl']
        try:
            imgutil.download_img(path,image_url)
        except Exception as e:
            print("failed to download image at {}".format(image_url))
            print(e)
    time.sleep(1)

imgutilの実装は以下を参照してください。

imagecrawler/imgutil.py at master · quotto/imagecrawler · GitHub

ポイント

簡単にポイントに触れておきます。

サブスクリプションキーの設定

Bing Web Search APIを利用するためにはhttpリクエストヘッダーの「Ocp-Apim-Subscription-Key」に取得したサブスクリプションキーを設定します。

パラメータ

Bing Web Search APIに設定するパラメータは以下のとおりです。

  • q:検索ワード
  • count:1リクエストあたりの取得件数
  • offset:データを取得開始するインデックス
  • mkt:結果の取得元となる国コード

リクエストと結果の取得

サブスクリプションキーとパラメータを設定したら、普通にhttpリクエストを実行するだけです。

結果はJSON形式で取得されます。キー「value」の配列として、各「contentURL」の値が画像のURLとなります。あとはこのURLから画像をダウンロードします。


以上、Microsoftの提供するBing Web Search APIのご紹介でした。

無料枠が30日間と期限付きですが、TwitterGoogleと比べてAPIキーの取得や実装は非常に簡単です。

チャチャッと画像を集めたい場合はぜひ利用を検討してみてください。

Microsoft Azure実践ガイド (impress top gear)

Microsoft Azure実践ガイド (impress top gear)

Google Custom Search APIを使って画像の自動収集

こんにちは。

前回はTwitter search APIを使ってTwitterから画像データを自動収集する方法をまとめました。
blog.wackwack.net

今回はGoogleCustomSearch APIを使った画像自動収集プログラムを作成しましたので、必要な手順を紹介します。

カスタム検索エンジンの作成とAPIキーの発行

GoogleCustomSearch APIを使うには「カスタム検索エンジンの作成」APIキーの発行」が必要となります。

GoogleCustomSearch APIではAPIキーを使って認証を行い、作成したカスタムエンジンに対して検索リクエストを投げる仕組みになっています。

カスタム検索エンジンの作成

まずはGoogleカスタム検索にログインしましょう。

ログイン後に「Add」をクリックします。 Googleカスタム検索

必要項目を入力して「作成」をクリックします。

  • 検索するサイト:任意のURLを入力します。(この後の手順で設定は削除するので何でもOK)
  • 言語:日本語
  • 検索エンジンの名前:任意の名前を入力します。

Googleカスタム検索

作成完了後の画面左側から、作成したカスタム検索エンジンを選択して「設定」をクリックします。
Googleカスタム検索

設定画面で以下のとおり設定を行います。(この画面では「保存」ボタン等をクリックする必要はありません。)

  • 画像検索:「オン」に設定
  • 検索するサイト:「追加したサイトを重視して、ウェブ全体を検索する」を選択
  • サイト:表示されているサイトをチェックして「削除」する

Googleカスタム検索

ここまで完了したら設定画面の検索エンジンID」をクリックして、表示されるIDを確認します。この値をプログラム上で利用します。
Googleカスタム検索

Googleカスタム検索

APIキーの発行

続いてAPIキーを発行します。APIの利用状況(1日のクエリ発行数など)はこのキーで管理されます。

Custom Search JSON/Atom APIにアクセスし、画面中央の「GET A KEY」をクリックします。
APIキーの発行

表示されたダイアログで「Create My Project」を選択します。これから発行するAPIキーはこの「プロジェクト」の中で管理されます。
プロジェクトの作成

適当な名前を入力して「NEXT」をクリックします。
プロジェクトの作成

作成が完了するとAPIキーが表示されます。この値をプログラム上で利用します。
APIキー


なおこの手順で作成した「プロジェクト」と「APIキー」は、Google Cloud プラットフォームより確認できます。

ダッシュボード

ダッシュボード

画像自動収集プログラム

私が作成した実装例を示します。実装はPython 3.6.1(Anaconda 4.4.0)で行っています。
imagecrawler/google_image_crawler.py at master · quotto/imagecrawler · GitHub

# -*- coding:utf-8 -*-
import httplib2
import imgutil
from googleapiclient.discovery import build

# image save path
path = "/path/to/save"

# parameters
api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"   # APIキー
cse_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 検索エンジンID
query = "キーワード"
service = build("customsearch","v1",developerKey=api_key)

imgutil.mkdir(path)

page_limit = 10 # 検索ページ数
startIndex = 1
response = []
img_list = []

for page in range(0,page_limit):
    try:
        response.append(service.cse().list(
            q=query,            # 検索ワード
            cx=cse_key,         # 検索エンジンID
            lr="lang_ja",       # 言語
            num=10,             # 1回あたりの取得件数(max10)
            start=startIndex,   # 取得開始インデックス
            searchType="image"  # 検索タイプ
        ).execute())
        startIndex = response[page].get("queries").get("nextPage")[0].get("startIndex")
    except Exception as e:
        print(e)

for i in range(len(response)):
    if len(response[i]['items']) > 0:
        for j in range(len(response[i]['items'])):
            img_list.append(response[i]['items'][j]['link'])

for i in range(len(img_list)):
    http = httplib2.Http(".cache")
    url = img_list[i]
    try:
        imgutil.download_img(path,url)
    except Exception as e:
        print("failed to download image at {}".format(url))
        print(e)
        continue

imgutil.download_imgの実装内容は以下を参照。
imagecrawler/imgutil.py at master · quotto/imagecrawler · GitHub

ポイント

かいつまんでポイントを示します。

google-api-python-client

このプログラムではgoogle-api-python-clientという、超便利なライブラリを利用しています。このライブラリを使うことでGoogle Custom Search APIの利用に関わる煩雑な処理を隠蔽することができます。

パラメータを指定しgoogle-api-python-clientのbuildメソッドでserviceインスタンスを作成します。「api_key」には先程取得したAPIキーを設定します。

パラメータ

Googleへのリクエスト部分のパラメータは以下のとおり。

  • q:検索キーワード
  • cs:取得した検索エンジンID
  • lr:言語
  • num:取得結果数(最大10)
  • start:取得対象の開始インデックス
  • searchType:画像を取得するためには”image”を指定

ループ処理

Google Custom Search APIでは1回のリクエストで取得できる件数は最大10件です。11件目以降を取得するにはパラメータ「start」に開始インデックスを指定します。次のインデックスは返却結果内の「startIndex」に格納されています。

画像URLの取得

リクエスト結果には画像のURLが格納されています。「items」要素からURLを取得し、httpリクエストを発行します。

100件以上は取得できない

最後に、Google Custom Search APIには注意点があります。

それはひとつのキーワードで取得可能な画像数は最大100件ということです。


つまり、

「num=10で100回ループ回せば1,000件の画像が取れるよね?」

に対する答えはNoです。


startIndexが100枚目以降になった場合、Google Custom Search APIはHTTPエラーを返します。

num=10の指定で10回リクエストを投げようが、100回リクエストを投げようが、取得できる画像データは100件までです。


もっとたくさんの画像が欲しい場合はBingやTwitterを使うしかありませんな。

データを集める技術 (Informatics &IDEA)

データを集める技術 (Informatics &IDEA)

Twitterのツイート検索APIを使って画像を自動収集する

こんにちは。

9月頃から趣味で機械学習の勉強をしています。機械学習に取り組む上での最重要作業のひとつとして「テストデータの収集」があります。例えば画像を使って物体認識させたいなら、大量の画像データが必要です。

ではそれらの画像を一つ一つ手作業で集めるのかと言えば、Noです。各検索プロバイダのAPIを使うことで手軽に大量*1の画像データを集めることができます。

ということで、Twitterのツイート検索APIを用いて画像を自動収集するプログラムを作成したので紹介します。

Twitterアプリケーションの登録

TwitterAPIを利用するにはアプリケーション登録を行い、Oauth認証用のキーを取得する必要があります。

まずはTwitter Application Managementへアクセスし、自分のTwitterアカウントでログインします。
Twitter Application Manager

次に右上の「Create New App」をクリックします。 f:id:WorldWorldWorld:20171224102650p
(私の画面では登録済みのアプリケーションが表示されていますが、初利用時にはこの画面は空白です。)

登録するアプリケーションの入力フォームが出ます。
Create an application

「Name」「Description」「Website」は必須入力項目です。適当な内容で入力します。

  • Name:アプリケーションの登録名
  • Description:アプリケーションの説明文
  • Website:アプリケーションの問い合わせ先が分かるようなURLを入力します。私の場合はとりあえず当ブログのURLを登録しています。
  • CallbackURL:画像収集用にAPIを利用する場合は入力不要です。

入力したらページ最下部の「Create your application」をクリックします。 Create your twitter application

これでアプリケーションの登録が完了しました。
f:id:WorldWorldWorld:20171224104225p:plain

続いてAPIの利用に必要となるAccess Tokenの発行を行います。表示されたページのタブ「Keys And Access Tokens」から「Create my access token」をクリックします。
f:id:WorldWorldWorld:20171224104509p:plain

これで準備は完了です。先程のページに表示されている「Consumer Key」「Consumer Secret」「Access Token」「Access Token Secret」をプログラム内で利用します。
f:id:WorldWorldWorld:20171224104655p:plain

ツイッター画像収集プログラム

今回はPython3.6.1(Anaconda 4.4.0)で作成しました。

なおTwitterのsearch APIは画像のみを対象に検索することはできません。キーワードで検索した全ツイートから、画像を含むものを抽出します。


こちらが検索プログラム本体です。「Consumer Key」「Consumer Secret」「Access Token」「Access Token Secret」にアプリケーション登録で取得した値を設定します。

https://github.com/quotto/imagecrawler/blob/master/twitter_image_crawler.py

# -*- coding:utf-8 -*-

import requests
import requests_oauthlib
import json
import math
import imgutil

# image save path
directory = "/path/to/img"
imgutil.mkdir(directory)

url = "https://api.twitter.com/1.1/search/tweets.json"

# parameters
query = "キーワード"
lang = "ja"
result_type="recent" # 最新のツイートを取得
count = 100 # 1回あたりの最大取得ツイート数(最大100)
max_id = ''

total_count = 1000 # 取得ツイートのトータル
offset = math.floor(total_count/count) # ループ回数

# oauthの設定
consumer_key = "xxxxxxxxxx"
consumer_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
access_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
access_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
oauth = requests_oauthlib.OAuth1(consumer_key,consumer_secret,access_token,access_secret)

for i in range(offset):
    params = {'q':query,'lang':lang,'result_type':result_type,'count':count,'max_id':max_id}
    r = requests.get(url=url,params=params,auth=oauth)
    json_data = r.json()
    for data in json_data['statuses']:
        # 最後のidを格納
        max_id = str(data['id']-1)
        if 'media' not in data['entities']:
            continue
        else:
            for media in data['entities']['media']:
                if media['type'] == 'photo':
                    image_url = media['media_url']
                    try:
                        imgutil.download_img(directory,image_url)
                    except Exception as e:
                        print("failed to download image at {}".format(image_url))
                        print(e)
                        continue

注意点として、Twitterのツイート検索APIでは一回あたりに取得できる最大ツイート件数が100件です。そのため、100件を超えて検索したい場合は2回目以降のリクエストパラメータ「max_id」に、「前回検索で取得した最後のid - 1」を設定しています。これにより次の検索ではmax_id以下のidを持つツイートを検索します。


他のプログラムで再利用できる部分はimgutil.pyにまとめました。URLをハッシュ化したものをファイル名として端末内に保存します。

https://github.com/quotto/imagecrawler/blob/master/imgutil.py

# -*- coding:utf-8 -*-

import os
import requests
import urllib
import hashlib
import sha3
import os

def mkdir(path):
    if not os.path.exists(path):
        os.makedirs(path)

# 引数fをファイル名と拡張子(.は含まない)に分割する
def split_filename(f):
    split_name = os.path.splitext(f)
    file_name =split_name[0]
    extension = split_name[-1].replace(".","")
    return file_name,extension

def download_img(path,url):
    mkdir(path)
    _,extension  = split_filename(url)
    if extension.lower() in ('jpg','jpeg','gif','png','bmp'):
        encode_url = urllib.parse.unquote(url).encode('utf-8')
        hashed_name = hashlib.sha3_256(encode_url).hexdigest()
        full_path = os.path.join(path,hashed_name + '.' + extension.lower())

        r = requests.get(url)
        if r.status_code == requests.codes.ok:
            with open(full_path,'wb') as f:
                f.write(r.content)
            print('saved image...{}'.format(url))
        else:
            print("HttpError:{0}  at{1}".format(r.status_code,image_url))

パラメータや取得データの詳細は公式のAPIドキュメントを参照してください。
Standard search API — Twitter Developers


以上、Twitterのツイート検索APIを用いて画像収集するプログラムのご紹介でした。

次はGoogleとBingを使っての検索プログラムを作成してみたいと思います。

Twitter API ポケットリファレンス (POCKET REFERENCE)

Twitter API ポケットリファレンス (POCKET REFERENCE)

*1:ここで言う大量とは「趣味で取り組む程度には」くらいの意味

Python2における文字列「文字型」と「ユニコード型」についてザックリとまとめた

最近『meya.ai』をいじっていて、Pythonになんちゃって入門しました。

quotto.hatenablog.com

初心者として戸惑ったのが文字列の扱い。

print 'Hello,World'
print u'Hello,World'

「u」ってなんですかね。 ということで、ザックリとまとめてみます。

※独学入門の初心者のため、間違えているところ多数の可能性があります。

注意

私はPython2.7を使っています。 軽く調べたところPython3.x系では全然違うようなので注意してください。

「文字型」と「ユニコード型」

Python2.xにおいては、文字列を表す型に「文字型」(str)と「ユニコード型」(unicode)の2種類が存在します。

「u」が付いていないのが「文字型」、付いているのが「ユニコード型」です。

print type('abcd') #=> <type 'str'>
print type(u'abcd') #=> <type 'unicode'>

何が違うの?

ザックリ言うとこういうこと。

# -*- coding:utf-8 -*-

#これはAsciiなのでどちらも一緒。
print len('abcd') #=> 4
print len(u'abcd') #=> 4

#これはマルチバイト(UTF-8)なので、文字型はバイト数になる。
print len('あいうえ') #=> 12
print len(u'あいうえ') #=> 4

要するにPython2.xでは文字型と言いつつもそれはバイトやでってことです。

で、どっち使えばいいの?

単純に「文字列処理ができるユニコード型を使っておけばいいじゃん」とは思うのですが、やはり注意が必要です。

最たる例が標準出力とファイル出力です。 以下のtest.pyというプログラムを用意します。

# -*- coding:utf-8 -*-

#標準出力
text = u'あいうえお'
print text

#ファイル出力
f = open('test.txt','w')
f.write(text)
f.close()

これをターミナル上で実行すると、ファイル出力がエラーとなります。

$ python test.py
あいうえお
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    f.write(text)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)

なぜか。

実はprint実行時にはPythonが「出力先の文字コードUTF-8だね!」と判断して、UTF-8の文字型に変換してくれているんですね。 標準出力の文字コードはsysオブジェクトに値が格納されておりそいつを基に判定するのだと思います。

# -*- coding:utf-8 -*-
import sys

print sys.stdout.encoding #=> UTF-8

#だからこの値がasciiだとprintもエラーになる。
import codecs
sys.stdout = codecs.getwriter('ascii')(sys.stdout)
print 'あいうえお'
#=>UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)

一方でf.writeでは「文字コードの指定ないからようわからん。asciiでいいやろ。」と考えasciiに変換しようとするのですが、当然日本語には対応できずエラーとなります。

そのためファイル出力を行うにあたっては、

  1. あらかじめユニコード型を文字型にエンコードしておく。
  2. ファイルオープン時にエンコードを指定する。

のどちらかを実施する必要があります。

# -*- coding:utf-8  -*-
import codecs
text = u'あいうえお'

#文字型へエンコード
f = open('test1.txt','w')
f.write(text.encode('utf-8'))
f.close

#エンコードを指定してファイルを開く(要codecsオブジェクト)
f = codecs.open('test2.txt','w','utf-8')
f.write(text)
f.close
$ python test.py
$ cat test1.txt
あいうえお
$ cat test2.txt
あいうえお

結論:入門するならPython3

Python3だとunicode型が無くなり、「文字型とバイト型」というわかりやすい構成になっているようです。 私は何故か2.7で入門してしまいましたが、「u」とかいちいち付けるの面倒なんでこれから始める人は3.x系がおすすめです。

参考にさせていただきました。

私の記事よりも圧倒的に細かく・詳しく書いてあります。
Pythonの日本語処理
http://www.kabipan.com/computer/python/unicode.html
Python2のstr/unicodeとencode/decode