LoginSignup
6
5

More than 3 years have passed since last update.

機械学習のデータ集めのためのicrawlerを使いやすくした

Posted at

はじめに

機械学習の画像データ集めには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"を以下のように修正する必要がある。

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

実装

ということで、先に実装を以下に公開する。 

img_collection.py
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を作成する。これに検索ワードを記述し、下の例では、三種類の検索ワードを指定する形になり、いちいち三つのワードをそれぞれ実行しなくて済む。

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枚程度が残る感じになる。
とりあえず、これで猫の画像が大量に手に入る。
検索ワードごとにディレクトリを分けているが、一つのディレクトリに画像をまとめてやれば、重複する画像はファイル名が競合するので、基本的にマージされる。

neko.png

最後に

Webクローラーは、権利関係がデリケートなので、利用目的に応じて適切に取り扱うようにしましょう。

6
5
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
6
5