はじめに
Bingの画像検索APIを使って画像を大量に収集する を参考にBingで画像収集しようとしたところ、APIがv7になっていたので、少し修正することになりました。
また、url取得とダウンロードをわけて行うようしました。
Bingのアカウントの取得や、API Keyの取得などは上記の記事を参考に行ってください。大変お世話になりました。
python素人なので、コードがイマイチなところは目をつぶってください。
コード
画像URL収集用
プログラム中に指定した検索ワードでBing画像検索を行い、結果のURLリストをファイルに保存します。
# 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。
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 のファイル名で保存します。
[auth]
bing_api_key = XXXXXXXXXXXXXXXXXXXXX
という感じに。
動きのざっくりした説明
一回のリクエストで取得できる結果がmax 150なので、たくさんほしいときは複数回投げます。複数回投げるときは、APIのパラメータのcountとoffsetを使います。
countとoffsetについては、参考にさせていただいた記事を御覧ください。わかりやすいので。
-
一回、検索パラメータを組み立てて、検索を投げる。レスポンスから、検索結果件数を取得。
-
URL格納ファイルを作成。
- ファイル名は "検索キーワード.txt"。ただし、検索キーワード中にスペースがある場合は"_"に変換。あと、出力ディレクトリに同じファイル名のファイルがあったら、"検索キーワード_YYYYMMDDhhmmss.txt"と日時をくっつけたファイル名にする。
- 一行目に以下の情報を出力し、二行目以降に結果のURLリストを出力。
date=YYYYMMDDhhmmss, search_term=検索キーワード, totalEstimatedMatches=検索結果件数
-
検索結果件数 < num_imgs_required の場合は、num_imgs_requiredに検索結果件数をセットし直す。
-
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格納ファイル
# 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)
動きのざっくりした説明
- URL格納ファイルを開いて、一行目の情報をとってきます。
- 画像をためるディレクトリを作成します。
save_dir_path/URLファイル中の検索キーワード_URLファイル中のdate/
です。 - URLにリクエストを投げて画像を2.にためていきますが、同じ画像が別URLで置かれている場合があるので、画像をhash値にしてファイル名としています。
- 最後にURLと画像ファイル名の対応をcorr_table.jsonというファイル名で、画像保存ディレクトリに書き出します。
おわりに
大量に集めてきた画像を前に途方に暮れている現状。。。この先が大変。