50
73

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 5 years have passed since last update.

Pythonで、Yahoo画像検索を利用した大量画像スクレイピング

Last updated at Posted at 2019-07-04

背景

https://karaage.hatenadiary.jp/entry/2017/08/23/073000
このページにあった、Yahoo画像検索結果から画像を自動でダウンロードしてくれるスクリプトが、2018年6月に使えなくなったことが確認されたようで、それを見た私が少し改変して使えるようにしたコードを発掘し、せっかくなので共有しておこうと思った次第。

機械学習用の画像データセットをサクッと集めたいなあ・・・と思っていたので、一度に大量の画像を取得できるようにもしてます。

動作環境

2019.07.05 動作確認済み。
動作環境:macOS10.14.5, python3.7.3

Python コード

# coding: utf-8

import os
import sys
import traceback
from mimetypes import guess_extension
from time import time, sleep
from urllib.request import urlopen, Request
from urllib.parse import quote
from bs4 import BeautifulSoup

MY_EMAIL_ADDR = ''

class Fetcher:
    def __init__(self, ua=''):
        self.ua = ua

    def fetch_img_direct(self, url):
        """
        yahoo画像検索画面に表示されている画像のbyte情報を抽出します。
        引数:
            url: yahoo画像検索画面のurlです。

        返り値:
            img_b_content: ウェブサイトリソースのbyteコードのリストです。
            mime: CONTENT_TYPEにより指定されている拡張子です。
        """
        req = Request(url, headers={'User-Agent': self.ua})
        try:
            with urlopen(req, timeout=3) as p:
                page_b_content = p.read()
                structured_page = BeautifulSoup(page_b_content.decode('UTF-8'), 'html.parser')
                img_link_elems = structured_page.find_all('img')
                img_urls = [e.get('src') for e in img_link_elems if e.get('src').startswith('http')]
                img_urls = list(set(img_urls)) #なぜset化しているのかは不明
        except:
            sys.stderr.write('Error in fetching {}\n'.format(url))
            sys.stderr.write(traceback.format_exc())
            return None, None

        img_b_content = []
        mime = []
        for i, img_url in enumerate(img_urls):
            req1 = Request(img_url, headers={'User-Agent': self.ua})
            try:
                with urlopen(req1, timeout=3) as p:
                    img_b_content.append(p.read())
                    mime.append(p.getheader('Content-Type'))
            except:
                sys.stderr.write('Error in fetching {}\n'.format(img_url))
                sys.stderr.write(traceback.format_exc())
                continue

        return img_b_content, mime

fetcher = Fetcher(MY_EMAIL_ADDR)



def url_brancher(word):
    """
    yahoo画像検索画面のurlを、検索条件の組み合わせの数だけ取得します。

    引数:
        word : 検索語です。

    返り値:
        urllist : yahoo画像検索画面のurlのリストです。
    """
    constant = "https://search.yahoo.co.jp/image/search?p={}&n=60".format(quote(word))

    values = [\
    ["", "small"],\
    ["", "red"],\
    ["", "face"]\
    ]
    """
    values = [\
    ["", "small", "medium", "large", "wallpaper", "widewallpaper"],\
    ["", "red", "orange", "yellow", "green", "teal", "blue", "purple", "pink", "white", "gray", "black", "brown"],\
    ["", "face", "photo", "clipart", "lineart"]\
    ]
    """
    urllist = []

    for i in range(len(values[0])):
        for j in range(len(values[1])):
            for k in range(len(values[2])):
                urllist.append(constant + "&dim={}".format(values[0][i]) + "&imc={}".format(values[1][j]) + "&ctype={}".format(values[2][k]))
    return urllist



def main(word):
    """
    Fetchにより取得された情報をもとに画像ファイルを保存します。

    引数:
    word: 検索語です。

    返り値:
    """
    data_dir = 'data/'
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)

    yahoo_url_list = url_brancher(word)

    for i, yahoo_url in enumerate(yahoo_url_list):
        sleep(0.1)
        img, mime = fetcher.fetch_img_direct(yahoo_url)
        if not mime or not img:
            print('Error in fetching {}\n'.format(yahoo_url))
            continue

        for j, img_url in enumerate(img):
            ext = guess_extension(mime[j].split(';')[0])
            if ext in ('.jpe', '.jpeg'):
                ext = '.jpg'
            if not ext:
                print('Error in fetching {}\n'.format(img_url))
                continue

            result_file = os.path.join(data_dir, str(i) + str(j)+ ext)
            with open(result_file, mode='wb') as f:
                f.write(img_url)
            print('fetched', str(i) + str(j) + ext)



if __name__ == '__main__':
    word = input("検索ワードを入力してください : ")
    main(word)

url_brancher の values をコメントアウト部分に変更すれば、yahoo画像検索の組み合わせ(6 * 13 * 5) * 60 = 23,400 個の画像ファイルが、一度の実行で取得可能です。画像の重複を許して保存するようになってます。重複させたくない場合は、img_b_content の set を作って if not img_b_content in set とかにすれば実現できるはず。

一年前くらいに書いたコードなので細かいところはあまり覚えてませんが、動くは動くので使いたい方がいらっしゃれば・・・

追記

2019. 07 .07

結構反響があったので見直しました。

「#なぜset化しているのかは不明」と書いてますが、img_url の重複を消すためです。全然不明じゃありませんでした。

50
73
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
50
73

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?