見出し画像

Google Colabで画像収集(Selenium版)

Pythonを使ってGoogleの画像検索の画像を収集する方法について「さすをブログ」を参考に学習用のプログラムを組んでいたのですが、Google Colabで実行できなくなったので、修正版をこちらに展開します。

原因1:Google ColabのOSアップデートの影響

2023年の1月にGoogle ColabのOSがUbuntu 18.04 LTSからUbuntu 20.04 LTSに変更になったのが原因のようで、この影響で、seleniumが利用するDriver(chromium-browser)が使えなくなってしまいました。seleniumとはブラウザの操作を命令により実行するためのライブラリになります。ブラウザに相当するDriverがないとエラーになります。そこで外部からこれをインストールする設定が必要なります。インストールするにはGoogle Colab上で次のコードを実行します。

 #Google  Colab仕様変更対応==ここから==
%%shell
# Ubuntu no longer distributes chromium-browser outside of snap
#
# Proposed solution: https://askubuntu.com/questions/1204571/how-to-install-chromium-without-snap

# Add debian buster
cat > /etc/apt/sources.list.d/debian.list <<'EOF'
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster.gpg] http://deb.debian.org/debian buster main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-buster-updates.gpg] http://deb.debian.org/debian buster-updates main
deb [arch=amd64 signed-by=/usr/share/keyrings/debian-security-buster.gpg] http://deb.debian.org/debian-security buster/updates main
EOF

# Add keys
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys DCC9EFBF77E11517
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 648ACFD622F3D138
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 112695A0E562B32A

apt-key export 77E11517 | gpg --dearmour -o /usr/share/keyrings/debian-buster.gpg
apt-key export 22F3D138 | gpg --dearmour -o /usr/share/keyrings/debian-buster-updates.gpg
apt-key export E562B32A | gpg --dearmour -o /usr/share/keyrings/debian-security-buster.gpg

# Prefer debian repo for chromium* packages only
# Note the double-blank lines between entries
cat > /etc/apt/preferences.d/chromium.pref << 'EOF'
Package: *
Pin: release a=eoan
Pin-Priority: 500


Package: *
Pin: origin "deb.debian.org"
Pin-Priority: 300


Package: chromium*
Pin: origin "deb.debian.org"
Pin-Priority: 700
EOF

# Install chromium and chromium-driver
apt-get update
apt-get install chromium chromium-driver

# Install selenium
pip install selenium
 #Google  Colab仕様変更対応==ここまで==

ここまでで、chromium-driverのインストールとseleniumのインストールを行うことができます。

原因2:seleniumのバージョンの影響

pip install selenium

でインストールされるseleniumのバージョンは「selenium-4.14.0」になります。seleniumはバージョン3系と4系でコマンド体系が大きく変わっており、selenium3系で作ったプログラムは4系では動かなくなっています。3系のselemiumをインストールするには

pip install selenium==3.141.0

とします。これで先の「さすをブログ」のコードが動くようになったのですが、2023年6月ごろからLibのインストールが上手く行かなくなったようです。

pip install urllib3==1.26.16

ライブラリのバージョンを落とせばうまくいくようなんですが、せっかくなのでselenium4系で書き換えてみました。

動作コード

import requests
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os
import time
import datetime
from selenium.common.exceptions import NoSuchElementException, ElementClickInterceptedException, StaleElementReferenceException
from selenium.webdriver.common.by import By

tm_start = 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 = 100
SAVE_DIR = 'img/dog_'
FILE_NAME = 'dog_'
TIMEOUT = 60
ACCESS_WAIT = 1
RETRY_NUM = 3
DRIVER_PATH = '/usr/bin/chromedriver'

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')
service = Service(executable_path=r'/usr/bin/chromedriver') ##selenium 4.10.0への変更による##
driver=webdriver.Chrome(service=service,options=options)   ##selenium 4.10.0への変更による##

wait = WebDriverWait(driver, TIMEOUT)

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

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 = wait.until(EC.presence_of_all_elements_located((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(1)

    tmb_elems = wait.until(EC.presence_of_all_elements_located((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 = wait.until(EC.presence_of_element_located((By.ID, 'islsp')))

os.makedirs(SAVE_DIR, exist_ok=True)
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

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
    return result

tm_thumbnails = time.time()
print('サムネイル画像取得', f'{tm_thumbnails - tm_geturl:.1f}s')

EXCLUSION_URL = 'https://lh3.googleusercontent.com/'
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 _ in range(RETRY_NUM):
        try:
            tmb_elem.click()
            break
        except (ElementClickInterceptedException, StaleElementReferenceException):
            driver.execute_script('arguments[0].scrollIntoView(true);', tmb_elem)
            time.sleep(1)

    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

    tmb_url = tmb_elem.get_attribute('src')
    for _ in range(RETRY_NUM):
        url = img_elem.get_attribute('src')
        if EXCLUSION_URL in url or url == tmb_url:
            time.sleep(1)
            continue
        break

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

    ext = get_extension(url)
    if not ext:
        print(f'***** urlに拡張子が含まれていないのでキャンセル')
        print(f'{url}')
        continue

    filename = f'{FILE_NAME}{count}{ext}'
    path = os.path.join(SAVE_DIR, filename)
    if download_image(url, path, RETRY_NUM):
        url_list.append(f'{filename}: {url}')
        count += 1
    else:
        print('***** キャンセル')

    if count >= LIMIT_DL_NUM:
        break

tm_end = time.time()
print('ダウンロード', f'{tm_end - tm_thumbnails:.1f}s')
print('------------------------------------')
total = tm_end - tm_start
total_str = f'トータル時間: {total:.1f}s({total/60:.2f}min)'
count_str = f'ダウンロード数: {count}'
print(total_str)
print(count_str)

path = os.path.join(SAVE_DIR, 'url', '_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()

最後のURLをテキストに保存するところでエラーがでますが、これは元ファイルがないからみたいで、ファイル作成のコードをいれるか、からファイルを作っておくと動くと思います。

FileNotFoundError: [Errno 2] No such file or directory: 'img/dog_/url/_url.txt'

Google Colabの共有ファイルと注意事項

ここまでのコードは次のGoogle Colabの共有ファイルからアクセスでできます。

注意点ですが、

  1. Google自体がスクレイピング(画像収集)を禁止している点、

  2. 「犬 フリー」で検索しても著作権フリーの画像が出てくるわけではない

という2点です。検索エンジン自体がスクレイピングでデータを集めているので、禁止と言われてもなんだかという気もしますが、実験や学習以外で利用する場合は、注意や確認が必要なるかと思います。

この記事が気に入ったらサポートをしてみませんか?