PythonでSelenium使ってスクレイピング

最近Pythonにハマりまくってますuphyです。

さくっと何かを検証したいときとか、ちょっとした自動化したいとき、Pythonはほんとに便利ですね。 先人の方々が優秀なライブラリを開発してくださっているので、だいたいなんでも迅速にできちゃいます。

今回は、Google画像検索を例にスクレイピングを説明します。 Googleはこういったスクレイピングは禁止しているので、あくまでこういった感じにできるだろういう感じです。 動くかわからないですし、動かさないでください。

スクレイピング環境 on Python

Selenium WebDriverを使ってください。

ブラウザを操作してくれるライブラリです。 Python, Java,その他もろもろの言語で使えます。 Javaで使っていましたが、ブラウザ操作って対話的なので、対話的に実行しやすいスクリプト言語との親和性が圧倒的に高いと感じています。

Seleniumを使わずに、requestsとか使ってHTTPごりごり話して、クッキー処理して、、、とかやってもいいですけど、めんどいです。 たしかにブラウザなしで実行できるってすごく素敵ですけど労力かかまくります。 Webページってしょっちゅう変わっちゃうものなんで、プログラムの寿命は必然的に短くなっちゃうものです。 そんな短命なものに時間かけたくないですよね。

それから、Jupyter Notebook使うととても良いです。 スクレイピングってtry and errorなところがとても大きいです。 Jupyter Notebookは、プログラム書いて実行して失敗して原因調べてちょっと書き換えて失敗したとこから実行して・・ってのがものすごくやりやすくて、相性抜群です。

Selenium WebDriverのインストール

今回はChromeを使います。 macOSです。

$ brew install chromedriver
$ pip install selenium

スクレイピング

ブラウザプロセス起動

ブラウザを起動します。 今回はChromeで実行します。

from selenium import webdriver

browser = webdriver.Chrome()

検索

query = 'ここに画像のキーワード'
browser.get('https://www.google.co.jp/search?q=%s&tbm=isch' % query)

元画像のURL収集

Googleの画像検索って、確かinfinite scrollな感じだったと思うので、上で表示されたブラウザ上でスクロールして画像をたくさん表示してからの方が取得できるかも。 下のプログラムは、あくまで今表示されているDOMに対してしか行わないですからね。

import time

image_urls = set()
for thumbnail in browser.find_elements_by_css_selector('img.rg_ic'):
    thumbnail.click()
    time.sleep(1)
    for img in browser.find_elements_by_css_selector('img.irc_mi'):
        image_urls.add(img.get_attribute('src'))

収集したURLのダウンロード

こういうのサクッとかけちゃうPythonすごくいいんだけど、イテレーションの途中で失敗すると悲しいことになるので、例外の全キャッチと、失敗したURLの保存をやっておいたほうがいいかも。

import requests

index = 0
failed = []
for image_url in image_urls:
    if image_url == None:
        continue
    try:
        response = requests.get(image_url, stream=True)
        if response.status_code == 200:
            contenttype = response.headers['content-type']
            if contenttype == None:
                extension = '.bin'
            if contenttype.find('jpeg') != -1:
                extension = '.jpg'
            elif contenttype.find('png') != -1:
                extension = '.png'
            elif contenttype.find('gif') != -1:
                extension = '.gif'
            else:
                extension = '.bin'
            filename = 'images/%04d%s' % (index, extension)
            index += 1
            with open(filename, 'wb') as file:
                for chunk in response.iter_content(chunk_size=1024):
                    file.write(chunk)
            print('Downloaded: %s' % image_url)
    except:
        print('Failed: %s' % image_url)
        failed.append(image_url)

重複ファイル削除

ファイルのハッシュを比較して、重複したファイルを列挙します。

import hashlib
def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

hashes = set()
from os import listdir
from os.path import join
for file in listdir('./images'):
    file = join('images', file)
    h = md5(file)
    if h in hashes:
        print('Duplicated image: %s' % file)
        continue
    hashes.add(h)

以上です!Pythonたのしー!!