はじめに
機械学習の画像データ集めにはPythonライブラリのicrawlerが便利で、公式の例では以下のように物凄く簡単にインストールと実装ができる。
#インストール
pip install icrawler
or
conda install -c hellock icrawler
#実行例(Googleで"cat"で検索し、100枚の画像を取得)
from icrawler.builtin import GoogleImageCrawler
google_crawler = GoogleImageCrawler(storage={'root_dir': 'your_image_dir'})
google_crawler.crawl(keyword='cat', max_num=100)
このように、たった三行で画像を取得できる。
※現在2020/05/03は、Googleのparser変更により、GoogleImageCrawlerは機能しない。
#使いやすく改良
以下の2点を入れて、より使いやすくした
①. 複数の検索ワードを(外部)指定したい
②. 重複する画像がでないでほしい
①については、外部のテキストファイルに検索ワードを行ごとに記述し、これを読み込む形で実装。
②については、ファイル名に画像のURLを記載することで、同じ画像を保存する際に自動でスキップするようにした。
※ただし、長すぎるURLの場合はファイルの書き込みのエラーが出てしまうので、これを解決するために、icrawlerライブラリの"filesystem.py"を以下のように修正する必要がある。
class FileSystem(BaseStorage):
"""Use filesystem as storage backend.
The id is filename and data is stored as text files or binary files.
"""
def __init__(self, root_dir):
self.root_dir = root_dir
def write(self, id, data):
filepath = osp.join(self.root_dir, id)
folder = osp.dirname(filepath)
if not osp.isdir(folder):
try:
os.makedirs(folder)
except OSError:
pass
mode = 'w' if isinstance(data, six.string_types) else 'wb'
# with open(filepath, mode) as fout:
# fout.write(data)
try:
with open(filepath, mode) as fout:
fout.write(data)
except FileNotFoundError:
pass
#実装
ということで、先に実装を以下に公開する。
import base64
from icrawler import ImageDownloader
from six.moves.urllib.parse import urlparse
from icrawler.builtin import BaiduImageCrawler
from icrawler.builtin import BingImageCrawler
from icrawler.builtin import GoogleImageCrawler
import argparse, os
parser = argparse.ArgumentParser(description='img_collection')
parser.add_argument('--output_dir', default="",type=str, help='')
parser.add_argument('--N', default=10, type=int, help='')
parser.add_argument('--engine', choices=['baidu',"bing","google"],default="bing",type=str, help='')
args = parser.parse_args()
class Base64NameDownloader(ImageDownloader):
def get_filename(self, task, default_ext):
url_path = urlparse(task['file_url'])[2]
if '.' in url_path:
extension = url_path.split('.')[-1]
if extension.lower() not in [
'jpg', 'jpeg', 'png', 'bmp', 'tiff', 'gif', 'ppm', 'pgm'
]:
extension = default_ext
else:
extension = default_ext
# works for python 3
filename = base64.b64encode(url_path.encode()).decode()
return '{}.{}'.format(filename, extension)
def get_crawler(args, dir_name):
if args.engine == "baidu":
crawler = BaiduImageCrawler(downloader_cls=Base64NameDownloader,storage={'root_dir': dir_name })
elif args.engine == "bing":
crawler = BingImageCrawler(downloader_cls=Base64NameDownloader,storage={'root_dir': dir_name })
elif args.engine == "google": # dont work
crawler = GoogleImageCrawler(storage={'root_dir': dir_name })
return crawler
if __name__=="__main__":
# read ini file.
with open('./setting.txt', mode='r', encoding = "utf_8") as f:
read_data = list(f)
print("SELECTED ENGINE : "+args.engine)
for i in range(len(read_data)):
print("SEARCH WORD : "+read_data[i].replace('\n', ''))
print("NUM IMAGES : "+str(args.N))
dir_name = os.path.join(args.output_dir, read_data[i].replace('\n', '').replace(' ', '_'))
#init crawler
crawler = get_crawler(args, dir_name)
crawler.crawl(keyword=read_data[i], max_num=args.N)
img_collection.pyと同じ階層にsetting.txtを作成する。これに検索ワードを記述し、下の例では、三種類の検索ワードを指定する形になり、いちいち三つのワードをそれぞれ実行しなくて済む。
猫 cat
猫 大人
猫 子供
#使い方
setting.txtに検索ワードを好きなだけ記述し、以下を実行
--N : 取得画像上限(Max1000。実際は通信やページの有無により1000枚も取得できない)
--output_dir : 出力先のディレクトリパス
--engine : 検索エンジン。bing, baiduから選択。Googleは現在動かない。
python img_collection.py --N 1000 --output_dir D:\hogehoge\WebCrawler\out --engine bing
#結果
取得枚数1000枚で指定しても、実際には通信のエラーなどで600枚程度が残る感じになる。
とりあえず、これで猫の画像が大量に手に入る。
検索ワードごとにディレクトリを分けているが、一つのディレクトリに画像をまとめてやれば、重複する画像はファイル名が競合するので、基本的にマージされる。
#最後に
Webクローラーは、権利関係がデリケートなので、利用目的に応じて適切に取り扱うようにしましょう。