1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Bing Image Search APIを使ってみた

Posted at

画像認識のモデル作成用に大量に取得する必要があったため、Bing Image Search APIを呼び出して画像を一括で取得してみました。
Bingの画像検索APIを使って画像を大量に収集するに載っていたPythonのコードを少し修正して使わせてもらいました。
試した環境は Windows 10 / Python 3.8.2 です。

コード

bing-api.py
# -*- coding: utf-8 -*-
import http.client
import json
import re
import requests
import os
import math
import pickle
import urllib
import hashlib
import sha3


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


def make_correspondence_table(correspondence_table, original_url, hashed_url):
    """Create reference table of hash value and original URL.
    """
    correspondence_table[original_url] = hashed_url


def make_img_path(save_dir_path, url):
    """Hash the image url and create the path

    Args:
        save_dir_path (str): Path to save image dir.
        url (str): An url of image.

    Returns:
        Path of hashed image URL.
    """
    save_img_path = os.path.join(save_dir_path, 'imgs')
    make_dir(save_img_path)

    file_extension = os.path.splitext(url)[-1]
    file_extension_changed = re.sub('\?.*(\S+)', '', file_extension)
    if file_extension_changed.lower() in ('.jpg', '.jpeg', '.gif', '.png', '.bmp'):
        encoded_url = url.encode('utf-8') # required encoding for hashed
        hashed_url = hashlib.sha3_256(encoded_url).hexdigest()
        full_path = os.path.join(save_img_path, hashed_url + file_extension_changed.lower())

        make_correspondence_table(correspondence_table, url, hashed_url)

        return full_path
    else:
        raise ValueError('Not applicable file extension')


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

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

    return response.content


def save_image(filename, image):
    with open(filename, "wb") as fout:
        fout.write(image)


if __name__ == "__main__":
    save_dir_path = "save/directory/path"
    make_dir(save_dir_path)

    num_imgs_required = 20 # Number of images you want. The number to be divisible by 'num_imgs_per_transaction'
    num_imgs_per_transaction = 10 # default 30, Max 150
    offset_count = math.floor(num_imgs_required / num_imgs_per_transaction)

    url_list = []
    correspondence_table = {}

    headers = {
        # Request headers
        'Content-Type': 'multipart/form-data',
        'Ocp-Apim-Subscription-Key': 'api-key-value', # API key
    }

    for offset in range(offset_count):

        params = urllib.parse.urlencode({
            # Request parameters
            'q': 'xxxxxx',
            'mkt': 'ja-JP',
            'count': num_imgs_per_transaction,
            'offset': offset * num_imgs_per_transaction # increment offset by 'num_imgs_per_transaction' (for example 0, 150, 300)
        })

        try:
            conn = http.client.HTTPSConnection('api.cognitive.microsoft.com')
            conn.request("GET", "/bing/v7.0/images/search?%s" % params, "{body}", headers)
            response = conn.getresponse()
            data = response.read()

            save_res_path = os.path.join(save_dir_path, 'pickle_files')
            make_dir(save_res_path)
            with open(os.path.join(save_res_path, '{}.pickle'.format(offset)), mode='wb') as f:
                pickle.dump(data, f)

            conn.close()
        except Exception as err:
            print("[Errno {0}] {1}".format(err.errno, err.strerror))

        else:
            decode_res = data.decode('utf-8')
            data = json.loads(decode_res)

            pattern = r"(http.+)" # extract an URL of image

            for values in data['value']:
                unquoted_url = urllib.parse.unquote(values['contentUrl'])
                img_url = re.search(pattern, unquoted_url)
                if img_url:
                    url_list.append(img_url.group(1))

    for url in url_list:
        try:
            img_path = make_img_path(save_dir_path, url)
            image = download_image(url)
            save_image(img_path, image)
            print('saved image... {}'.format(url))
        except KeyboardInterrupt:
            break
        except Exception as err:
            print("%s" % (err))

    correspondence_table_path = os.path.join(save_dir_path, 'corr_table')
    make_dir(correspondence_table_path)

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

前提

  • Bing Image Search APIのサブスクリプションが済んでいること
  • Pythonがインストール済みであること
  • 取得した画像ファイルの保管先としてパラメーターsave_dir_pathで指定するディレクトリのパスが作成済みであること

実行時に指定する内容

  • headersOcp-Apim-Subscription-Key に取得したAPI keyを指定
  • 検索したいクエリーを params中の qで指定(日本語も可)
  • 取得したい画像枚数を num_imgs_required で指定
  • 1トランザクションで取得する画像数は params中のcountで指定(defaultは35枚で、Maxは150枚)
  • 落としてきた画像を保存するpathを save_dir_path で指定

実行方法

bing-api.pyを置いているディレクトリパスにて以下のコマンドを実行

py bing-api.py

元の記事のコードからの主な変更点

  • APIのバージョンアップに対応して、エンドポイントやメソッドを変更
変更前
conn.request("POST", "/bing/v5.0/images/search?%s" % params, "{body}", headers)
変更後
conn.request("GET", "/bing/v7.0/images/search?%s" % params, "{body}", headers)
  • 保管対象となる画像を拡大。元の記事のコードだと「.jpg」「.jpeg」「.gif」「.png」「.bmp」のいずれかで終わるURLの画像を保管する作りになっていましたが、 https://xxxx.jp/image/assessment/2019/10/xxxxx.jpg?ts=12345678 のようにファイル拡張子の後にクエリ文字列がくっつく画像もそこそこあったので、クエリ文字列がついてても保管できるようにしました。
変更前
    file_extension = os.path.splitext(url)[-1]
    if file_extension.lower() in ('.jpg', '.jpeg', '.gif', '.png', '.bmp'):
        encoded_url = url.encode('utf-8') # required encoding for hashed
        hashed_url = hashlib.sha3_256(encoded_url).hexdigest()
        full_path = os.path.join(save_img_path, hashed_url + file_extension.lower())

        make_correspondence_table(correspondence_table, url, hashed_url)

        return full_path
変更後
    file_extension = os.path.splitext(url)[-1]
    # 次の行でクエリ文字列が含まれていた場合には削除するようにしています
    file_extension_changed = re.sub('\?.*(\S+)', '', file_extension)
    if file_extension_changed.lower() in ('.jpg', '.jpeg', '.gif', '.png', '.bmp'):
        encoded_url = url.encode('utf-8') # required encoding for hashed
        hashed_url = hashlib.sha3_256(encoded_url).hexdigest()
        full_path = os.path.join(save_img_path, hashed_url + file_extension_changed.lower())

        make_correspondence_table(correspondence_table, url, hashed_url)

        return full_path
1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?