徒然だみー

オオトリ様との日常

MENU

花の画像をスクレイピングしてみた!

昨日上げたブログを修正してみました!
見やすくなってると思います。

こんちゃ!だみーです

卒研のために現在の進捗とかまとめ解こうと思ってブログに書くことにしました。

今回はトルコギキョウという花の画像をスクレイピングして集めたのでまとめます。

環境

Google Chrom(バージョン:114.0.5735.199)

chromedriver(バージョン:114.0.5735.90)

python(Python 3.10.11)

全コード

とりあえず全コード貼っときます。

適切なchromedriverインストールすればすぐに使えると思います!

import requests

from selenium import webdriver

from selenium.webdriver.chrome.options import Options

import os

import time

import datetime

from selenium.common.exceptions import NoSuchElementException

from selenium.common.exceptions import ElementClickInterceptedException




tm_sum = time.time()  # 処理時間の計測用

dt_now = datetime.datetime.now()  # 現在日時

dt_date_str = dt_now.strftime('%Y/%m/%d %H:%M')

print(dt_date_str)



QUERY = '〇〇〇'  # 検索ワード

LIMIT_DL_NUM = 400               # ダウンロード数の上限

SAVE_DIR = '---------'  # 出力フォルダへのパス(フォルダがない場合は自動生成する)

FILE_NAME = 'trc'                       # ファイル名(ファイル名の後ろに0からの連番と拡張子が付く)

TIMEOUT = 60                     # 要素検索のタイムアウト(秒)

ACCESS_WAIT = 1                  # アクセスする間隔(秒)

RETRY_NUM = 3                    # リトライ回数(クリック、requests)

DRIVER_PATH = '-------'        # chromedriver.exeへのパス



# Chromeをヘッドレスモードで起動

options = Options()

options.add_argument('--headless')

options.add_argument('--no-sandbox')

options.add_argument('--disable-dev-shm-usage')

options.add_argument('--start-fullscreen')

options.add_argument('--disable-plugins')

options.add_argument('--disable-extensions')

driver = webdriver.Chrome(DRIVER_PATH, options=options)



# タイムアウト設定

driver.implicitly_wait(TIMEOUT)

tm_driver = time.time()

print('WebDriver起動完了', f'{tm_driver - tm_sum:.1f}s')



# Google画像検索ページ

url = f'https://www.google.com/search?q={QUERY}&tbm=isch'

driver.get(url)



tm_geturl = time.time()

print('Google画像検索ページ取得', f'{tm_geturl - tm_driver:.1f}s')



tmb_elems = driver.find_elements_by_css_selector('#islmp img')

tmb_alts = [tmb.get_attribute('alt') for tmb in tmb_elems]



count = len(tmb_alts) - tmb_alts.count('')

print(count)



while count < LIMIT_DL_NUM:

    # ページの一番下へスクロールして新しい画像を表示

    driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')

    time.sleep(4)



    # 画像取得

    tmb_elems = driver.find_elements_by_css_selector('#islmp img')

    tmb_alts = [tmb.get_attribute('alt') for tmb in tmb_elems]



    count = len(tmb_alts) - tmb_alts.count('')

    print(count)



# 画像をクリックすると表示される領域を取得

imgframe_elem = driver.find_element_by_id('islsp')



# 出力フォルダ作成

os.makedirs(SAVE_DIR, exist_ok=True)



# HTTPヘッダ作成

HTTP_HEADERS = {'User-Agent': driver.execute_script('return navigator.userAgent;')}

print(HTTP_HEADERS)



# ダウンロード対象の拡張子

IMG_EXTS = ('.jpg', '.jpeg', '.png', '.gif')



# 拡張子取得

def get_extension(url):

    url_lower = url.lower()

    for img_ext in IMG_EXTS:

        if img_ext in url_lower:

            extension = '.jpg' if img_ext == '.jpeg' else img_ext

            break

    else:

        extension = ''

    return extension



# urlの画像を取得しファイルへ書き込む

def download_image(url, path, loop):

    result = False

    for i in range(loop):

        try:

            r = requests.get(url, headers=HTTP_HEADERS, stream=True, timeout=10)

            r.raise_for_status()

            with open(path, 'wb') as f:

                f.write(r.content)

        except requests.exceptions.SSLError:

            print('***** SSL エラー')

            break  # リトライしない

        except requests.exceptions.RequestException as e:

            print(f'***** requests エラー({e}): {i + 1}/{RETRY_NUM}')

            time.sleep(1)

        else:

            result = True

            break  # try成功

    return result



tm_thumbnails = time.time()

print('画像取得', f'{tm_thumbnails - tm_geturl:.1f}s')



# ダウンロード

EXCLUSION_URL = 'https://lh3.googleusercontent.com/'  # 除外対象url

count = 0

url_list = 

for tmb_elem, tmb_alt in zip(tmb_elems, tmb_alts):



    if tmb_alt == '':

        continue



    print(f'{count}: {tmb_alt}')



    for i in range(RETRY_NUM):

        try:

            # 画像をクリック

            tmb_elem.click()

        except ElementClickInterceptedException:

            print(f'***** click エラー: {i + 1}/{RETRY_NUM}')

            driver.execute_script('arguments[0].scrollIntoView(true);', tmb_elem)

            time.sleep(1)

        else:

            break  # try成功

    else:

        print('***** キャンセル')

        continue  # リトライ失敗



    # アクセス負荷軽減用のウェイト

    time.sleep(ACCESS_WAIT)



    alt = tmb_alt.replace("'", "\\'")

    try:

        img_elem = imgframe_elem.find_element_by_css_selector(f'img[alt=\'{alt}\']')

    except NoSuchElementException:

        print('***** img要素検索エラー')

        print('***** キャンセル')

        continue



    # url取得

    tmb_url = tmb_elem.get_attribute('src')  # サムネイル画像のsrc属性値

 

    for i in range(RETRY_NUM):

        url = img_elem.get_attribute('src')

        if EXCLUSION_URL in url:

            print('***** 除外対象url')

            url = ''

            break

        elif url == tmb_url:  # src属性値が遷移するまでリトライ

            print(f'***** urlチェック: {i + 1}/{RETRY_NUM}')

            #print(f'***** {url}')

            time.sleep(1)

            url = ''

        else:

            break



    if url == '':

        print('***** キャンセル')

        continue



    # 画像を取得しファイルへ保存

    ext = get_extension(url)

    if ext == '':

        print(f'***** urlに拡張子が含まれていないのでキャンセル')

        print(f'{url}')

        continue



    filename = f'{FILE_NAME}{count}{ext}'

    path = SAVE_DIR + '/' + filename

    result = download_image(url, path, RETRY_NUM)

    if result == False:

        print('***** キャンセル')

        continue

    url_list.append(f'{filename}: {url}')



    # ダウンロード数の更新と終了判定

    count += 1



    if count >= LIMIT_DL_NUM:



        break



tm_end = time.time()

print('ダウンロード', f'{tm_end - tm_thumbnails:.1f}s')

print('------------------------------------')

total = tm_end - tm_sum

total_str = f'トータル時間: {total:.1f}s({total/60:.2f}min)'

count_str = f'ダウンロード数: {count}'

print(total_str)

print(count_str)



# urlをファイルへ保存

path = SAVE_DIR + '/' + '_url.txt'

with open(path, 'w', encoding='utf-8') as f:

    f.write(dt_date_str + '\n')

    f.write(total_str + '\n')

    f.write(count_str + '\n')

    f.write('\n'.join(url_list))



driver.quit()

ここから詳しくコードを解説します。

使用ライブラリ宣言

import requests

from selenium import webdriver

from selenium.webdriver.chrome.options import Options

import os

import time

import datetime

from selenium.common.exceptions import NoSuchElementException

from selenium.common.exceptions import ElementClickInterceptedException

今回使用したライブラリは大まかに5つ。

requestsはメソッドを操作しやすくしたもの
import requests

webスクレイピングやwebAPIを使いwebからデータを取得したいときに使う。

selenium
from selenium import webdriver

from selenium.webdriver.chrome.options import Options

from selenium.common.exceptions import NoSuchElementException

from selenium.common.exceptions import ElementClickInterceptedException

seleniumはブラウザを自動的に操作するライブラリです。

seleniumの使い方は以下のサイトにわかりやすくまとまっていたので参考にしてください。

【Python】Seleniumとは?Webブラウザ操作の自動化方法を徹底解説

os
import os

osに存在している様々な機能を利用するためのモジュールです。

主にファイルやディレクトリのを操作する際に用います。

time
import time

timeはプログラム内で現在時刻を確認したり処理を一時停止できるモジュールです。

datetime
import datetime

datetimeは日付をプログラムから取得でき、日付や時間の表示を可能にするモジュールです。

プログラムの実行状況を記録

tm_sum = time.time()  # 処理時間の計測用

dt_now = datetime.datetime.now()  # 現在日時

dt_date_str = dt_now.strftime('%Y/%m/%d %H:%M')

print(dt_date_str)

プログラムの処理時間と現在日時を表示します。

検索内容の指定

QUERY = '〇〇〇'  # 検索ワード

LIMIT_DL_NUM = 400               # ダウンロード数の上限

SAVE_DIR = '---------'  # 出力フォルダへのパス(フォルダがない場合は自動生成する)

FILE_NAME = 'trc'                       # ファイル名(ファイル名の後ろに0からの連番と拡張子が付く)

TIMEOUT = 60                     # 要素検索のタイムアウト(秒)

ACCESS_WAIT = 1                  # アクセスする間隔(秒)

RETRY_NUM = 3                    # リトライ回数(クリック、requests)

DRIVER_PATH = '-------'        # chromedriver.exeへのパス

検索キーワードや画像のダウンロード枚数の指定をします。

出力フォルダへのパスと、chromedriver.exeのパスはご自身の環境のパスを指定してください。

chromeの起動

options = Options()

options.add_argument('--headless')

options.add_argument('--no-sandbox')

options.add_argument('--disable-dev-shm-usage')

options.add_argument('--start-fullscreen')

options.add_argument('--disable-plugins')

options.add_argument('--disable-extensions')

driver = webdriver.Chrome(DRIVER_PATH, options=options)

ここではoptionchromeの起動条件を追加していきchromeを起動します。今回はヘッドレスモードで起動します。(ここは個人の好みで調節してください。)

起動条件などは以下のサイトでまとめてくれています。

【Python3】SeleniumのChrome起動オプションについて | せなブログ



残りのプログラムコードの大まかな処理の流れ

while count < LIMIT_DL_NUM:
    # ページの一番下へスクロールして新しい画像を表示
    driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
    time.sleep(4)


    # 画像取得
    tmb_elems = driver.find_elements_by_css_selector('#islmp img')
    tmb_alts = [tmb.get_attribute('alt') for tmb in tmb_elems]


    count = len(tmb_alts) - tmb_alts.count('')
    print(count)

ページをスクロールして画像を取得する。

# ダウンロード対象の拡張子
IMG_EXTS = ('.jpg', '.jpeg', '.png', '.gif')

ダウンロードする画像の拡張子を指定する。

# 拡張子取得
def get_extension(url):
    url_lower = url.lower()
    for img_ext in IMG_EXTS:
        if img_ext in url_lower:
            extension = '.jpg' if img_ext == '.jpeg' else img_ext
            break
    else:
        extension = ''
    return extension

web上の拡張子を取得する。

# ダウンロード
EXCLUSION_URL = 'https://lh3.googleusercontent.com/'  # 除外対象url
count = 0
url_list = []
for tmb_elem, tmb_alt in zip(tmb_elems, tmb_alts):


    if tmb_alt == '':
        continue


    print(f'{count}: {tmb_alt}')


    for i in range(RETRY_NUM):
        try:
            # 画像をクリック
            tmb_elem.click()
        except ElementClickInterceptedException:
            print(f'***** click エラー: {i + 1}/{RETRY_NUM}')
            driver.execute_script('arguments[0].scrollIntoView(true);', tmb_elem)
            time.sleep(1)
        else:
            break  # try成功
    else:
        print('***** キャンセル')
        continue  # リトライ失敗


    # アクセス負荷軽減用のウェイト
    time.sleep(ACCESS_WAIT)


    alt = tmb_alt.replace("'", "\\'")
    try:
        img_elem = imgframe_elem.find_element_by_css_selector(f'img[alt=\'{alt}\']')
    except NoSuchElementException:
        print('***** img要素検索エラー')
        print('***** キャンセル')
        continue


    # url取得
    tmb_url = tmb_elem.get_attribute('src')  # サムネイル画像のsrc属性値
 
    for i in range(RETRY_NUM):
        url = img_elem.get_attribute('src')
        if EXCLUSION_URL in url:
            print('***** 除外対象url')
            url = ''
            break
        elif url == tmb_url:  # src属性値が遷移するまでリトライ
            print(f'***** urlチェック: {i + 1}/{RETRY_NUM}')
            #print(f'***** {url}')
            time.sleep(1)
            url = ''
        else:
            break


    if url == '':
        print('***** キャンセル')
        continue


    # 画像を取得しファイルへ保存
    ext = get_extension(url)
    if ext == '':
        print(f'***** urlに拡張子が含まれていないのでキャンセル')
        print(f'{url}')
        continue


    filename = f'{FILE_NAME}{count}{ext}'
    path = SAVE_DIR + '/' + filename
    result = download_image(url, path, RETRY_NUM)
    if result == False:
        print('***** キャンセル')
        continue
    url_list.append(f'{filename}: {url}')


    # ダウンロード数の更新と終了判定
    count += 1


    if count >= LIMIT_DL_NUM:


        break

画像をクリックし、URLを取得してリスト化する。

URLから画像を保存する。この際に、後で扱いやすいようにファイル名を整える。


まとめ

今回はgoogle chromeを使って画像スクレイピングを行った。

500枚ほど画像が欲しかったのだが、どうしても400枚画像を読み込んだところで処理がフリーズしていまった。

断念して今回は400枚の画像をダウンロードした。

今後より多くの画像を保存できるようにプログラムを変える必要がある。




また、画像判別のモデルを作る予定なので画像の水増しが必要だ。



今回ダウンロードした画像をクラスタリングしてどのくらい分類できるか調べるのが今後の課題だ。


進捗あったらまた書きます。

んじゃ!!