Google画像検索から画像スクレイピング（Python3 + Selenium）

### はじめに

Deep Learning(TensorFlow)で画像分類をするために大量の画像データを収集する必要があったので、Google画像検索からスクレイピングしました。

Deep Learning(TensorFlow)に興味のある方は、是非 この記事でスクレイピングした画像データを使って画像分類を試してみてください。

https://qiita.com/yoshihara0512/items/524f73dd90ac0ab1bcab

### 実行環境

本記事に掲載しているコードはJupyter Notebook(Python3)で実行しました。
```
- OS：Windows 11
- 言語：Python3
- Webドライバ：Selenium/Chrome
```

### フォルダ構成

実行ユーザーのホームディレクトリに任意のフォルダを作成して以下のフォルダ/ファイルを配置しました。

```
├─download …①  
├─chromedriver.exe …②  
└─piture_scraping.ipynb …③    
```

① スクレイピングした画像データを格納するフォルダ  
② 「準備」の手順でダウンロードしたWebドライバ  
③ 本記事のコードを実行したJupyter Notebookのファイル  


### 準備

以下のサイトからChrome Driverをダウンロード
```
https://chromedriver.chromium.org/downloads
```

Anaconda Promptを起動してSeleniumをインストール
```Python
pip3 install selenium
```

### 画像スクレイピング
以下のコードをJupyter Notebookで実行します。

#### ・まずはライブラリをインポート
かなり試行錯誤しながらやったので不要な物が含まれていたらスミマセン、、、
```Python
import requests
from time import sleep
from selenium import webdriver
from selenium.webdriver.chrome import service as fs
from bs4 import BeautifulSoup
import os
import time
```

#### ・ドライバーを指定してChromeブラウザを開く
実行状態が分かりやすいようにあえてヘッドレス(バックグランド)にせず画面描画して実行してます。
大量にスクレイピングする際は上手く実行できているのを確認した後にヘッドレスに切り替えた方が良いです。
以下のコードを実行するとChromeブラウザが起動してGoogle画像検索が表示されます。
```Python
CHROMEDRIVER = "./chromedriver.exe"
chrome_service = fs.Service(executable_path=CHROMEDRIVER)
browser = webdriver.Chrome(service=chrome_service)

# Google画像検索のURLを指定
url = "https://www.google.co.jp/imghp?hl=ja"
browser.get(url)
```

#### ・検索キーワードを入力
以下のコードを実行するとJupyter Notebookで検索キーワードの入力を求められます。
入力するとChromeブラウザで検索キーワードが指定され、画像が表示されます。

```Python
# 検索キーワードを入力するHTML要素を取得
selector = "body > div.L3eUgb > div.o3j99.ikrT4e.om7nvf > form > div:nth-child(1) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input"
# ※セレクタが不明な場合はブラウザの開発者ツールから対象の要素を選択して右クリックメニューから「検証」
# 　Elementタブで対象の要素を選択して右クリックメニューから「Copy」-「Copy Selector」

from selenium.webdriver.common.by import By
kw_search = browser.find_element(By.CSS_SELECTOR, selector)

# 検索キーワードを指定
from selenium.webdriver.common.keys import Keys
actor_name = input("画像検索のキーワードを入力してください。：")

# ブラウザに検索キーワードを投入
kw_search.send_keys(str(actor_name))
kw_search.send_keys(Keys.ENTER)
```
Jupyter Notebookでは、以下の様な感じで入力を求められます。
![SnapCrab_NoName_2023-2-27_9-56-32_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193303/62454962-c627-9bef-c718-8e2cbb51c654.png)

#### ・画像URLの一覧を作成
このブロックは以下の流れで処理を実行しています。
① Chromeブラウザの画面スクロール
② img(画像)タグを取得
③ ファビコンなど関係ない画像はスキップ
④ 必要な画像のURLを配列変数(img_urls)に格納

以下のコードを実行するとChromeブラウザで画面がスクロールされます。
画面スクロールしないと初期表示の20件しかデータが取得できません。
(理由が分からず少しハマりました、、、)
今回は100件の画像データを取得していますが、増減する場合はimg_limit変数に指定する値を変えてください。

```Python
# 画像検索したページのURLを取得する
cur_url = browser.current_url
res = requests.get(cur_url)
time.sleep(3)

# 取得する画像の枚数を設定
img_limit = 100
img_count = 0

#画面のスクロールを行う。
try:

    img_urls = []
    # 画面を5回スクロール(スクロールしないと20枚しか取得できない)
    for i in range(5):
        # 画面をスクロール
        browser.execute_script("window.scrollTo(0,document.body.scrollHeight);")
        # BeautifulSoupを読み込み
        soup = BeautifulSoup(browser.page_source , "html.parser")

        # imgタグのHTML要素を全て取得する
        for image in soup.find_all("img"):
            # 画像の取得上限を超えたら終了
            if img_count >= img_limit:
                break
            
            try:
                url = image.get("data-src")

                if url is None:
                    url = image.get("src")

                if url is not None:
                    # ファビコンやpng、svgなどの場合はスキップ
                    if ('favicon' in url) or ('.png' in url) or ('.svg' in url) or ('data:image/' in url):
                        continue
                    if url in img_urls:
                        continue
                    img_urls.append(url)
                    img_count += 1
            except:
                print("画像のURL取得に失敗しました。")
        sleep(2)
        
except Exception:
    print("画面スクロールに失敗しました。")
```

#### ・画像データをダウンロード
最後に配列変数に格納したURLから画像をダウンロードします。
後からDeep Learning(TensorFlow)で画像分類し易い様にフォルダに連番をふっています。
以下のコードを実行すると「download」フォルダに画像が保存されます。


```Python
# 保存先のディレクトリ
dl_dir = "download"

# ダウンロードディレクトリが存在しない場合は作成
if not os.path.exists(dl_dir):
    os.mkdir(dl_dir)

# フォルダ数のカウント(連番用)
dir_count = 0
files = os.listdir(dl_dir)
for f in files:
    path = os.path.join(dl_dir, f)
    if os.path.isdir(path):
        dir_count += 1
        
# 連番を付与して画像検索キーワードと同名のディレクトリ名を生成
save_dir = dl_dir + "/" + str(dir_count) + "_" + actor_name

# サブディレクトリが存在しない場合は作成
if not os.path.exists(save_dir):
    os.mkdir(save_dir)
    
# 取得した画像のデータを保存する    
file_number = 1
for elem_url in img_urls:
    try:

        r = requests.get(elem_url)
        with open(save_dir + "/" + str(actor_name) + "_" + str(file_number) + ".jpg","wb") as fp:
            fp.write(r.content)
        file_number += 1
        sleep(0.1)
    except:
        pass

```

#### ・ブラウザを閉じる
最後にブラウザを閉じて完了です。

```Python
browser.quit()
```


### スクレイピングの結果を確認
「download」フォルダを開いて画像が保存されていれば成功です。
何かアニメキャラ？の様な画像も混じってますね。。。
Deep Learningの学習データとして使用する場合は不要なデータを削除する必要がありそうです。
![SnapCrab_NoName_2023-2-27_9-58-59_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3193303/84dc72e5-1fa0-5944-43be-e836eb0b734d.png)