画像認識のモデル作成用に大量に取得する必要があったため、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
で指定するディレクトリのパスが作成済みであること
実行時に指定する内容
-
headers
のOcp-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