LoginSignup
15
15

More than 5 years have passed since last update.

Bing画像検索API v7で画像を収集する

Posted at

はじめに

Bingの画像検索APIを使って画像を大量に収集する を参考にBingで画像収集しようとしたところ、APIがv7になっていたので、少し修正することになりました。
また、url取得とダウンロードをわけて行うようしました。

Bingのアカウントの取得や、API Keyの取得などは上記の記事を参考に行ってください。大変お世話になりました。

python素人なので、コードがイマイチなところは目をつぶってください。

コード

画像URL収集用

プログラム中に指定した検索ワードでBing画像検索を行い、結果のURLリストをファイルに保存します。

bing_image_url_collector.py
# coding: utf-8
import requests
import os
import math
import configparser # for Python3
import urllib
import re
import datetime

import bing_util

def get_headers(api_key):
    return {"Ocp-Apim-Subscription-Key" : api_key}

def get_params(search_term, num_imgs_per_transaction, offset):
    return urllib.parse.urlencode({
        "q": search_term,
        "license": "All",
        "imageType": "photo",
        "count":num_imgs_per_transaction,
        "offset": offset * num_imgs_per_transaction,
        "mkt":"ja-JP"})

def get_search_results(search_url, headers, params):
    response = requests.get(search_url, headers=headers, params=params)
    response.raise_for_status()
    search_results = response.json()
    return search_results

def save_urls(results, filepath):    
    with open(filepath, mode='a') as f:
        for values in results:
            if values['encodingFormat'] == 'jpeg':
                print(values['contentUrl'], file=f)

def get_filename(path, fn, ext):
    return os.path.join(path, '%s.%s' % (fn, ext))

def gen_url_save_file(search_term, url_dir_path, total_count):
    ext = 'txt'
    fn = bing_util.search_term2file_name(search_term)
    filename = get_filename(url_dir_path, fn, ext)
    dt = datetime.datetime.now().strftime("%Y%m%d%H%M%S")

    if os.path.isfile(filename):
        fn = '%s_%s' % (fn, dt)
        filename = get_filename(url_dir_path, fn, ext)
    with open(filename, mode='w') as f:
        print("date=%s, search_term=%s, totalEstimatedMatches=%d" % (dt, search_term, total_count), file=f)
    return filename


if __name__ == '__main__':
    config = configparser.ConfigParser()
    config.read('authentication.ini')
    bing_api_key = config['auth']['bing_api_key']

    save_dir_path = './bing'
    bing_util.make_dir(save_dir_path)
    url_dir_path = os.path.join(save_dir_path, 'url')
    bing_util.make_dir(url_dir_path)

    num_imgs_required = 1000 # Number of images you want.
    num_imgs_per_transaction =150 # default 30, Max 150 images
    search_term = "emeperor penguin"

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

    headers = get_headers(bing_api_key)
    params = get_params(search_term, num_imgs_per_transaction, 0)

    first_search_results = get_search_results(search_url, headers, params)
    total_count = first_search_results["totalEstimatedMatches"]
    print("totalEstimatedMatches=%d" % total_count)

    filepath=gen_url_save_file(search_term, url_dir_path, total_count)

    print ("len=%d" % (len(first_search_results["value"])))
    save_urls(first_search_results["value"], filepath)

    if num_imgs_required > total_count :
        num_imgs_required = total_count

    offset_count = math.ceil(num_imgs_required / num_imgs_per_transaction)
    print('offset_count=%d' % offset_count)
    for offset in range(1, offset_count):
        params = get_params(search_term, num_imgs_per_transaction, offset)
        search_results = get_search_results(search_url, headers, params)

        print ("len=%d" % len(search_results["value"]))
        save_urls(search_results["value"], filepath)

URL収集と次の画像ダウンロードで共通する処理だけくくりだしたutil。

bing_util.py
import os
import re

def make_dir(path):
    if not os.path.isdir(path):
        os.mkdir(path)

def search_term2file_name(search_term):
    return re.sub('\s+', '_', search_term)

設定箇所

mainにある以下を編集します。

search_term
検索キーワードをいれます。多分日本語いけるはずですが、このキーワードを使ったファイルでURL取得リストを作るので、ちょっと注意したほうが良いかと思います。
save_dir_path
URL取得リストを保存するディレクトリを指定。ディレクトリがなければ作ります。
num_imgs_required
ほしい画像枚数。取得するURLリストの上限。ものすごくたくさん欲しくても、検索にマッチした画像がなければ、取れないのです。。。
num_imgs_per_transaction
Bingリクエスト一回あたりに取得する画像URL。Image Search APIのcountに渡す値です。countのデフォルトは30。

あと、Bing Search APIのkeyをこのスクリプトと同じディレクトリに、authentication.ini のファイル名で保存します。

authentication.ini
[auth]
bing_api_key = XXXXXXXXXXXXXXXXXXXXX

という感じに。

動きのざっくりした説明

一回のリクエストで取得できる結果がmax 150なので、たくさんほしいときは複数回投げます。複数回投げるときは、APIのパラメータのcountとoffsetを使います。
countとoffsetについては、参考にさせていただいた記事を御覧ください。わかりやすいので。

  1. 一回、検索パラメータを組み立てて、検索を投げる。レスポンスから、検索結果件数を取得。
  2. URL格納ファイルを作成。

    • ファイル名は "検索キーワード.txt"。ただし、検索キーワード中にスペースがある場合は"_"に変換。あと、出力ディレクトリに同じファイル名のファイルがあったら、"検索キーワード_YYYYMMDDhhmmss.txt"と日時をくっつけたファイル名にする。
    • 一行目に以下の情報を出力し、二行目以降に結果のURLリストを出力。

    date=YYYYMMDDhhmmss, search_term=検索キーワード, totalEstimatedMatches=検索結果件数

  3. 検索結果件数 < num_imgs_required の場合は、num_imgs_requiredに検索結果件数をセットし直す。

  4. num_imgs_requiredとnum_imgs_per_transactionから、何回検索を投げるかを決めて、必要回数だけ投げて、結果をファイルに格納。

いくつか補足

  • Image Searchのパラメーターでlicenseを指定できます。サンプルではAllにしているので、licenseを無視してガバガバ集めます。APIドキュメント これをPublicとかにしていると取得結果がすっごく少なくなる。king penguinだと150件以上あるのに、adelie penguinだと8件しかないとか。
  • 取得する画像のタイプをjpegに制限しています。他の画像種類が必要でしたら、save_urlsを直してください。

画像ダウンロード用

前項で取得したURLリストのファイルを使って、画像をダウンロードするスクリプトです。動かし方は、

$ python3 bing_image_downloader.py URL格納ファイル
bing_image_downloader.py
# coding: utf-8
import requests
import os
import sys
import re
import hashlib
import json

import bing_util

def get_target(line):
    matchObj=re.match(r"date=(\d{14}), search_term=([^,]+)", line)
    return [matchObj.group(1), matchObj.group(2)]

def download_image(url, timeout=10):
    response = requests.get(url, allow_redirects=True, timeout=timeout)
    if response.status_code != 200:
        error = Exception("HTTP status: %d" % response.status_code)
        raise error

    content_type = response.headers["content-type"]
    if 'image' not in content_type:
        error = Exception("Content-Type: %s" % content_type)
        raise error

    return response.content

def gen_image_md5(image_data):
    return hashlib.md5(image_data).hexdigest()

def save_image_file(img_save_dir, content):
    filename = "%s.jpg" % (gen_image_md5(content))
    filepath = os.path.join(img_save_dir, filename)
    with open(filepath, "wb") as fout:
        fout.write(content)
    return filename

if __name__ == '__main__':

    filepath = sys.argv[1]
    save_dir_path = './bing'
    bing_util.make_dir(save_dir_path)

    file = open(filepath)
    lines = file.readlines()
    file.close()

    [target_date, search_term] =get_target(lines[0].rstrip())
    print (search_term)
    img_save_dir=os.path.join(save_dir_path, '%s_%s' % (bing_util.search_term2file_name(search_term), target_date))

    bing_util.make_dir(img_save_dir)

    correspondence_table = {}

    for url in lines[1:]:
        try:
            url = url.rstrip()
            content = download_image(url)
            filename = save_image_file(img_save_dir, content)
            correspondence_table[url] = filename
            print ("filename:%s" % filename)

        except KeyboardInterrupt:
            break
        except Exception as err:
            print("%s : %s" % (err, url))

    with open(os.path.join(img_save_dir, 'corr_table.json'), mode='w') as f:
        json.dump(correspondence_table, f)

動きのざっくりした説明

  1. URL格納ファイルを開いて、一行目の情報をとってきます。
  2. 画像をためるディレクトリを作成します。save_dir_path/URLファイル中の検索キーワード_URLファイル中のdate/ です。
  3. URLにリクエストを投げて画像を2.にためていきますが、同じ画像が別URLで置かれている場合があるので、画像をhash値にしてファイル名としています。
  4. 最後にURLと画像ファイル名の対応をcorr_table.jsonというファイル名で、画像保存ディレクトリに書き出します。

おわりに

大量に集めてきた画像を前に途方に暮れている現状。。。この先が大変。

15
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15