Pythonによる画像スクレイピング
本記事では、Pythonを使った画像スクレイピングの実装、およびコードの解説をしていきます。
コピペで動くサンプルコードもあるので、是非参考にしてみてください。
本記事でわかること
- WebDriverの簡単な使い方
- 画像スクレイピングの実装方法
画像スクレピングは、クリック1つでフォルダ内が天国や地獄へと変化する、魔法のようなプログラムです。
今回、サンプルとして扱うプログラムでは、読者さんの目の保養を兼ねて、「レムりん」の画像を大量に集めることにします。
※本当は、僕が最近「リゼロ」を観始めたことが要因です()
サンプルプログラム実行後、フォルダがこのようになることを覚悟しておいてください。
では、さっそく本編へ移りましょう!
動作環境
・Windows 10
・Python 3.7.7
スクレイピングの準備
スクレピングを実装するにあたって、事前に準備しておくことがいくつかあるので、先にこちらを行ってください。
本来は、ここに準備の内容も記載していたのですが、思いのほか記事が長くなってしまったので、別の記事にまとめています。
3分程度で準備できるので、Selenium・WebDriverの準備手順を参考にしてみてください。
既に準備できている方は、読み飛ばしてもらってかまいません。
Pythonによる実装コード・解説
まずは、スクレイピングの流れを確認し、その後に実装コードを見ていきます。
スクレイピングの流れ
今回行うスクレイピングは、以下のような流れで行います。
スクレイピングの流れ
- Yahoo画像へアクセス
- 画面最下部まで自動でスクロール
- 表示されたすべての画像URLを取得
- URL先から画像をダウンロード
Yahoo画像において厄介な存在である「無限スクロール」には、「自動スクロール」で対応しています。
無限スクロールと言っても、割と限界があるようなので、自動スクロールで対応できました。
スクレイピング実装コード
実装コードを以下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# 必要なライブラリのインポート import os from time import sleep import requests from selenium import webdriver from selenium.webdriver.chrome.options import Options # 1枚の画像を保存する関数 def save_img(url, file_path): r = requests.get(url, stream=True) if r.status_code == 200: with open(file_path, "wb") as f: f.write(r.content) # 複数の画像のダウンロードを行う関数 def download_imgs(img_urls, save_dir): for i, url in enumerate(img_urls): file_name = f"{i}.png" # 画像ファイル名 save_img_path = os.path.join(save_dir, file_name) # 保存パス save_img(url, save_img_path) # 画像の保存 if (i + 1) % 10 == 0 or (i + 1) == len(img_urls): print(f"{i + 1} / {len(img_urls)} done") word = "レム" # 検索するワード save_dir = "./images/Rem" # スクレイピングした画像を保存するディレクトリパス # ディレクトリが存在しなければ作成する if not os.path.exists(save_dir): os.makedirs(save_dir) # Webdriverの設定 options = Options() options.add_argument('--headless') # UI無しで操作する driver = webdriver.Chrome("./chromedriver.exe", options=options) # WebDriverのパスを設定 # yahoo画像へアクセス url = "https://search.yahoo.co.jp/image/search?p={}" driver.get(url.format(word)) # 指定したURLへアクセス urls = [] # 画像URLを格納するリスト # 止まるまでスクロールする while True: prev_html = driver.page_source # スクロール前のソースコード driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 最下部までスクロール sleep(1.0) # 1秒待機 current_html = driver.page_source # スクロール後のソースコード # スクロールの前後で変化が無ければループを抜ける if prev_html != current_html: prev_html = current_html else: # 「もっと見る」ボタンがあればクリック try: button = driver.find_element_by_class_name("sw-Button") button.click() except: break # 画像タグをすべて取得 elements = driver.find_elements_by_tag_name("img") # すべての画像URLを抜き出す for elem in elements: url = elem.get_attribute("src") if url not in urls: urls.append(url) # urlをリストに追加する driver.close() # driverをクローズする download_imgs(urls, save_dir) # 画像をダウンロードする |
コードの解説
まあまあ長いので、ブロック単位で区切りながら解説します。
一応、ある程度多めにコメントを入れているので、要点だけ絞って解説していきます。
1~7行目
この部分では、必要なライブラリをインポートしています。
1 2 3 4 5 6 7 |
# 必要なライブラリのインポート import os from time import sleep import requests from selenium import webdriver from selenium.webdriver.chrome.options import Options |
下の2行が、事前に準備しておいたSeleniumのライブラリです。
31~36行目
この部分では、検索するワードと、画像の保存先の設定・作成を行っています。
31 32 33 34 35 36 |
word = "レム" # 検索するワード save_dir = "./images/Rem" # スクレイピングした画像を保存するディレクトリパス # ディレクトリが存在しなければ作成する if not os.path.exists(save_dir): os.makedirs(save_dir) |
自分でプログラムを実行する際は、 word = "検索したい画像" と save_dir = "保存先パス" を各自で設定してください。
記事冒頭で述べたように、今回は「レム」で画像を検索します。
38~45行目
この部分では、WebDriverの初期設定と、Yahoo画像へのアクセスまでを行っています。
38 39 40 41 42 43 44 45 |
# Webdriverの設定 options = Options() options.add_argument('--headless') # UI無しで操作する driver = webdriver.Chrome("./chromedriver.exe", options=options) # WebDriverのパスを設定 # yahoo画像へアクセス url = "https://search.yahoo.co.jp/image/search?p={}" driver.get(url.format(word)) # 指定したURLへアクセス |
40行目の、 options.add_argument('--headless') を記述することによって、UI無しでブラウザ操作を行うことができます。
気になる方は、この部分をコメントアウトして、動作の違いを確認してみてください。
また、41行目の、 driver = webdriver.Chrome("./chromedriver.exe", options=options) では、第一引数に、ダウンロードしたWebDriverの実行ファイルのパスを記述します。
解凍したフォルダの中にあるファイルのパスです。
Yahoo画像では、 https://search.yahoo.co.jp/image/search?p= の最後尾に、検索ワードを付け加えることで、画像ページへアクセスできます。
今回の例では、 https://search.yahoo.co.jp/image/search?p=レム へとアクセスすることになりますね。
ちなみに、実際に上記のURLへアクセスしてみると以下のようなページへ遷移することが確認できます。
47~77行目
この部分では、画面のスクロール、画像URLの取得までを行います。
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
urls = [] # 画像URLを格納するリスト # 止まるまでスクロールする while True: prev_html = driver.page_source # スクロール前のソースコード driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 最下部までスクロール sleep(1.0) # 1秒待機 current_html = driver.page_source # スクロール後のソースコード # スクロールの前後で変化が無ければループを抜ける if prev_html != current_html: prev_html = current_html else: # 「もっと見る」ボタンがあればクリック try: button = driver.find_element_by_class_name("sw-Button") button.click() except: break # 画像タグをすべて取得 elements = driver.find_elements_by_tag_name("img") # すべての画像URLを抜き出す for elem in elements: url = elem.get_attribute("src") if url not in urls: urls.append(url) # urlをリストに追加する driver.close() # driverをクローズする |
50行目から始まる、whileループによって、自動スクロールを行っています。
ちなみに、自動スクロールは、以下の手順を繰り返すことで実現しています。
- 表示されているページの一番下までスクロール
- スクロールの前後でソースコードが変化していなければ、「もっと表示する」ボタンの有無をチェック
- ボタンがあればクリック、無ければループを終了
ループ終了後、68行目の、 elements = driver.find_elements_by_tag_name("img") によって、すべての画像タグを取得します。
幸い、Yahoo画像では、検索結果以外のimgタグが無いようなので、過不足なく、お目当てのimgタグを取得できます。
後は、forループによって、imgタグの中から、src属性のみを抜き出すだけです。
ちなみに、src属性は、画像のURL情報が入っている属性値です。
最後に、WebDriverをクローズして、URL取得処理はおしまいです。
78行目
この部分では、URLのリストと保存先パスを引数として、ダウンロード用の関数を呼び出しています。
78 |
download_imgs(urls, save_dir) # 画像をダウンロードする |
あとは、この関数の中身を確認したらおしまいです!
download_imgs(19~28行目)
この関数では、引数で受け取った複数の画像URLの処理を行います。
19 20 21 22 23 24 25 26 27 28 |
# 複数の画像のダウンロードを行う関数 def download_imgs(img_urls, save_dir): for i, url in enumerate(img_urls): file_name = f"{i}.png" # 画像ファイル名 save_img_path = os.path.join(save_dir, file_name) # 保存パス save_img(url, save_img_path) # 画像の保存 if (i + 1) % 10 == 0 or (i + 1) == len(img_urls): print(f"{i + 1} / {len(img_urls)} done") |
実際に1枚1枚の画像を保存する関数は、 save_img という関数で実装しているので、最後にそれを確認していきましょう。
save_img(10~16行目)
この関数では、受け取ったURL先の画像を保存します。
10 11 12 13 14 15 16 |
# 一般的な画像を保存する関数 def save_img(url, file_path): r = requests.get(url, stream=True) if r.status_code == 200: with open(file_path, "wb") as f: f.write(r.content) |
これは普通に、URL先の画像を保存しているだけです。
ここは特に説明することがないので、これだけにします。
これでコード解説はおしまいです!
プログラム実行例
最後に、サンプルコードを実行した場合の動きを確認して終わりにします。
事前に、GIF画像として用意しておいたので、こちらをご覧ください。
こんな感じで、実行ボタン1つクリックするだけで、次々と画像が保存されていきます。
まとめ
以上が、画像スクレイピングの実装と解説です。
コピペして使う場合は、検索ワードと保存先のパス、WebDriverのパスだけを各自で変更してください。
最後に、Yahoo画像は、スクレイピングを明示的に禁止しているわけではないのですが、無目的に乱用することは問題になり得るので、そこは自己責任でお願いします。
では、今回はここまでとします。
お疲れさまでした。