スクレイピング初心者がpythonでかわいい猫ちゃん画像をコマンド一発でネットから収集してみた

f:id:karaage:20170814144955j:plain:w640

注:この猫ちゃん画像は昔私が撮影した猫ちゃんで、ネットから落とした画像ではありません

追記:現在このスクリプトは使用できないようです

 Yahoo画像検索の仕様が変わったらしく、2018/08/12 の時点でこのスクリプトは使用できないようです。仕様が変わると適用できなくなるのは、スクレイピングのスクリプトの宿命ですね。今のところ対応の予定はありません。申し訳ございません。

ネットから画像収集しようとしたら意外に大変だった

 最近、以下で書いているように機械学習に興味津々だったりします。

 機械学習やディープラーニングを試していると、やっぱり試したくなるのが、チュートリアルで使用しているデータセット以外のデータにも同じ手法が適用できるかどうかですね。

 とりあえず画像をネットから収集しようかなと思い、googleの画像検索の結果を一つ一つ右クリックで落とし始めたのですが、どう考えても非人間的過ぎます。たしかこういう作業を自動化するのをスクレイピングと言って、pythonのサンプルがたくさんあった記憶があります。

 ネットで調べたら沢山例が出てきました。「これは楽勝だわー」と思いコピペして実行…動かん…。よくあることよくあること、と次へ行くと登録が沢山必要…パス。次は有料サービス…と、いっこうに動きません。

 コピペしてコマンド一発で画像がダウンロードできるスクリプトを探しているだけなのに…最終的にはできたのですが、結構大変だったので、メモしておきます。

pythonで好きな画像をネットからダウンロードする方法

 以下の3ステップでOKです。コピペと実行は一瞬なので、ほとんど環境構築が全てですね。

  • pythonの環境構築
  • プログラムのコピペ
  • プログラム実行

pythonの環境構築

 色々調べたら、スクレイピングするときは日本語とかの関係でpyhthon3が良いらしいので、python3に科学技術関係の代表的なパッケージが付属している anaconda3 の仮想環境を構築します。詳細は、下記記事参照下さい。

 上記記事でpyenvのインストールまで実施したら、具体的には以下コマンドで仮想環境を構築します。

$ pyenv install anaconda3-4.4.0 
$ pyenv global anaconda3-4.4.0
$ conda create -n scraping python
$ pyenv global anaconda3-4.4.0/envs/scraping

 後は、beautiful soupというスクレイピングに使われるライブラリを以下コマンドでインストールしましょう

$ pip install bs4

 これで環境構築は完了です。

画像をダウンロードするソフトのコード

 結局探し回った結果、自分が一番簡単に使えたのは以下のnknytkさんのGitHubのリポジトリの中のソフトでした。

 上記のリポジトリ自体は、ディープラーニングによる顔分類のツールで、そこに付属しているpythonのダウンローダを使わせてもらっているという形です。最初ライセンスが不明だったのと、jpegの拡張子がjpeになっちゃうのが気になったのでPull Requestしつつ、それとなく再配布に関して聞いてみたら、Pull Requestマージいただくと共に、快くMITライセンスにしていただけました、めちゃ感謝です!

 nknytkさんのソフトをベースに、今回簡単に画像をダウンロードすることに特化した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(self, url):
        req = Request(url, headers={'User-Agent': self.ua})
        try:
            with urlopen(req, timeout=3) as p:
                b_content = p.read()
                mime = p.getheader('Content-Type')
        except:
            sys.stderr.write('Error in fetching {}\n'.format(url))
            sys.stderr.write(traceback.format_exc())
            return None, None
        return b_content, mime

fetcher = Fetcher(MY_EMAIL_ADDR)

def fetch_and_save_img(word):
    data_dir = 'data/'
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)

    for i, img_url in enumerate(img_url_list(word)):
        sleep(0.1)
        img, mime = fetcher.fetch(img_url)
        if not mime or not img:
            continue
        ext = guess_extension(mime.split(';')[0])
        if ext in ('.jpe', '.jpeg'):
            ext = '.jpg'
        if not ext:
            continue
        result_file = os.path.join(data_dir, str(i) + ext)
        with open(result_file, mode='wb') as f:
            f.write(img)
        print('fetched', img_url)


def img_url_list(word):
    """
    using yahoo (this script can't use at google)
    """
    url = 'http://image.search.yahoo.co.jp/search?n=60&p={}&search.x=1'.format(quote(word))
    byte_content, _ = fetcher.fetch(url)
    structured_page = BeautifulSoup(byte_content.decode('UTF-8'), 'html.parser')
    img_link_elems = structured_page.find_all('a', attrs={'target': 'imagewin'})
    img_urls = [e.get('href') for e in img_link_elems if e.get('href').startswith('http')]
    img_urls = list(set(img_urls))
    return img_urls

if __name__ == '__main__':
    word = sys.argv[1]
    fetch_and_save_img(word)

 上記のプログラムの、以下のMY_EMAIL_ADDRには、以下のように自分のアドレスを入れましょう。これはUser Agentとして使われます。ツールを使ってダウンロードするときのマナーなので、必ず守るようにしましょう。ちなみに下記のアドレスは、架空のアドレスなのでメール送らないようにね。

MY_EMAIL_ADDR = 'karaage@karaage.net'

ソフトの使い方

 使い方は、上記のファイルをget_images_yahoo.pyという名前で保存して、例えば以下コマンド実行すれば、かわいい猫ちゃんの画像を一気にダウンロードできます

$ python get_images_yahoo.py "かわいい猫"

 簡単ですね!今回は手軽なYahooの画像検索からダウンロードする方法です。Googleの場合は、コマンドで一気に大量に画像をダウンロードするには有料サービスへの登録が必要なのですが、nknytkさんはGoogle Chromeのextensionを組み合わせることで、ある程度自動でダウンロードできるツールを開発されていました。私は試していないのでここでは紹介しませんが、興味ある方は以下READMEなどを参考に試してみるとよいかなと思います。

まとめ

 初めてpythonでスクレイピング的なことをしてみました。意外にネット情報がそのまま使えなくて苦戦しました。どうも、サービス側の方の仕様が変更したり、スクレイピング対策をしているのが原因みたいのようです。Googleとか、ちゃっかり有料化しているの全然知りませんでした。スクレイピングは、かなり奥深そうです。

 ただ、特にスクレイピング自体に興味があるわけじゃないので、あんまり深みにハマらないように気をつけたいなと思います。最後に、せっかく(?)なので、かわいい猫ちゃん画像を申し訳程度に載っけておきます(笑)。ネットから落としたやつだと、ちょっと問題ありそうなので、昔私が撮影した猫ちゃん写真です。

f:id:karaage:20170814145131j:plain:w640

f:id:karaage:20170814145132j:plain:w640

関連記事