PythonでWebサイトから画像をスクレイピングする方法

PythonとSeleniumを使用してWebサイトから簡単に画像をスクレイピングする方法を学び、機械学習、市場分析、コンテンツキュレーションのためのデータ収集を強化しましょう。
7 min read
How to Scrape Images in Python blog image

このチュートリアルでは以下について説明します。

  • Webサイトからの画像スクレイピングが有益である理由
  • Seleniumを使用してPythonでWebサイトから画像をスクレイピングする方法

さっそく始めましょう!

Webサイトから画像をスクレイピングする理由

Webスクレイピングで可能なのは、テキストデータを抽出することだけではありません。画像などのマルチメディアファイルを含め、あらゆるタイプのデータを対象にできます。特に、Webサイトからの画像スクレイピングは、いくつかのシーンで役立ちます。以下がその例です。

  • 機械学習とAIモデルのトレーニング用画像を取得する:オンラインでダウンロードした画像を使用してモデルをトレーニングし、その精度と効果を高めます。
  • 競合他社のビジュアルコミュニケーションへの取り組み方を調査する:競合他社が主要メッセージをオーディエンスに伝えるために使用している画像を自社マーケティングチームに提供することで、トレンドと戦略を理解します。
  • オンラインプロバイダーから視覚的に魅力的な画像を自動的に取得する:高品質な画像を使用して、Webサイトやソーシャルメディアプラットフォームでのエンゲージメントを高め、オーディエンスの注目を集めて、維持します。

Python画像スクレイピング:詳細ステップガイド

Webページから画像をスクレイピングするには、次の操作を実行する必要があります。

  1. ターゲットサイトに接続する
  2. 対象となるページ上の画像HTMLノードをすべて選択する
  3. それぞれから画像URLを抽出する
  4. これらのURLに紐づけられた画像ファイルをダウンロードする

このタスクに適したターゲットサイトは、インターネット上で最も人気の高い画像プロバイダーの1つであるUnsplashです。「壁紙」というキーワードで無料画像を検索した場合のダッシュボードは次のようになります。

無料の壁紙画像を検索中

ご覧のとおり、ユーザーが下にスクロールすると、ページに新しい画像が読み込まれます。言い換えれば、スクレイピングにブラウザ自動化ツールを必要とするインタラクティブなサイトです。

このページのURLは以下の通りです。

https://unsplash.com/s/photos/wallpaper?license=free

では、PythonでこのWebサイトから画像をスクレイピングする方法を見てみましょう!

ステップ1:はじめに

このチュートリアルに沿って進むには、Python3がコンピューターにインストールされていることを確認してください。インストールされていない場合は、インストーラーをダウンロードし、アイコンをダブルクリックした後、プロンプトの指示に従ってください。

以下のコマンドを使用してPythonスクレイピング画像プロジェクトを初期化します。

mkdir image-scraper
cd image-scraper
python -m venv env

これで画像スクレイパーフォルダーが作成され、その中にPython仮想環境が追加されます。

お好みのPython統合開発環境 (IDE) でプロジェクトフォルダーを開いてください。PyCharm Community EditionまたはPython拡張機能を入れたVisual Studio CodeがあればOKです。

プロジェクトフォルダーにscraper.pyファイルを作成し、以下のように初期化します。

print('Hello, World!')

現時点では、このファイルは「Hello, World!」と表示するだけのシンプルなスクリプトですが、間もなく画像スクレイピングのロジックが組み込まれます。

IDEの実行 (Run) ボタンを押すか、次のコマンドを実行して、スクリプトが機能することを確認してください。

python scraper.py

ターミナルに次のメッセージが表示されるはずです。

Hello, World!

ここまで順調です!これでPythonプロジェクトが作成されました。次のステップでは、Webサイトから画像をスクレイピングするために必要なロジックを実装します。

ステップ2:Seleniumをインストールする

Seleniumは、静的コンテンツと動的コンテンツの両方を含むWebサイトを処理できるため、画像のスクレイピングに最適なライブラリです。ブラウザ自動化ツールとして、JavaScriptの実行が必要なページでもレンダリングが可能です。Selenium Webスクレイピングのガイドで詳細をご覧ください。

BeautiFoulSoupのようなHTMLパーサーと比較すると、Seleniumはより多くのサイトを対象とし、より多くの使用事例をカバーできます。例えば、新しい画像を読み込む際にユーザーの操作を必要とする画像プロバイダーにも対応しています。このガイドのターゲットサイトであるUnsplashがまさにそれに当たります。

Seleniumをインストールする前に、Python仮想環境をアクティベートする必要があります。Windowsでは、次のコマンドでこれを実行してください。

env\Scripts\activate

macOSおよびLinuxでは、代わりに以下を実行してください。

source env/bin/activate

環境ターミナルで、次のpipコマンドを使用してSelenium WebDriverパッケージをインストールします。

pip install selenium

インストールにはしばらく時間がかかるため、気長に待ちましょう。

完了です!Pythonでの画像スクレイピングに必要なものがすべて揃いました。

ステップ3:ターゲットサイトに接続する
scraper.pyに以下の行を追加して、SeleniumとChromeインスタンスを制御するために必要なクラスをインポートする

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.chrome.options import Options

これで、ヘッドレスChrome WebDriverインスタンスを次のコードで初期化できるようになりました。

# to run Chrome in headless mode
options = Options()
options.add_argument("--headless") # comment while developing

# initialize a Chrome WerbDriver instance
# with the specified options
driver = webdriver.Chrome(
    service=ChromeService(),
    options=options
)

SeleniumにGUIでChromeウィンドウを起動させたい場合は、--headlessオプションをコメントアウトします。これにより、ページ上でスクリプトが実行する内容をリアルタイムで追うことができるので、デバッグに役立ちます。本番環境では、リソースを節約するために--headlessオプションを有効にしておきましょう。

スクリプトの最後に次の行を追加して、ブラウザウィンドウを閉じることをお忘れなく。

# close the browser and free up its resources
driver.quit() 

一部のページでは、ユーザーのデバイスの画面サイズによって画像の表示が異なります。レスポンシブコンテンツに関する問題を回避するには、以下を使用してChromeウィンドウを最大化してください。

driver.maximize_window()

get()メソッドを使用して、次のようにSelenium経由でターゲットページに接続するようChromeに指示することができます。

url = "https://unsplash.com/s/photos/wallpaper?license=free"
driver.get(url)

すべてをまとめると、次のようになります。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.chrome.options import Options

# to run Chrome in headless mode
options = Options()
options.add_argument("--headless")

# initialize a Chrome WerbDriver instance
# with the specified options
driver = webdriver.Chrome(
    service=ChromeService(),
    options=options
)

# to avoid issues with responsive content
driver.maximize_window()

# the URL of the target page
url = "https://unsplash.com/s/photos/wallpaper?license=free"
# visit the target page in the controlled browser
driver.get(url)

# close the browser and free up its resources
driver.quit()

画像スクレイピングスクリプトをヘッド付きモードで起動します。これにより、Chromeを閉じる前に、次のページが一部のセクションだけ表示されます。

一部だけ表示されるページ

「Chromeは自動テストソフトウェアにより制御されています」というメッセージは、SeleniumがChromeウィンドウ上で想定通りに動作していることを意味します。

できました!ページから画像を抽出する方法については、そのページのHTMLコードをご覧ください。

ステップ4:対象サイトを調査する

Python画像スクレイピングのロジックを掘り下げる前に、対象ページのHTMLソースコードを調査する必要があります。こうすることでのみ、効果的なノード選択ロジックの定義方法を理解し、必要なデータを抽出する方法を把握できます。

従って、ブラウザで対象サイトにアクセスし、画像を右クリックして[調査]オプションを選択し、開発ツールを開きます。

DevToolsで画像を調査しています

ここで、いくつかの興味深い事実に気が付くでしょう。

まず、画像はHTML要素に含まれています。つまり、対象の画像ノードを選択するCSSセレクターは次の通りです:

[data-test="photo-grid-masonry-img"]

次に、画像要素には従来のsrc属性とsrcset属性の両方があります。後者の属性に詳しくない方のために説明すると、srcsetは、ブラウザがレスポンシブブレイクポイントに基づいて適切な画像を選択するのを助けるヒントとともに、複数のソース画像を指定します。

具体的には、srcset属性の値は次の形式になります:

<image_source_1_url> <image_source_1_size>, <image_source_1_url> <image_source_2_size>, ...

場所:

  • などは、さまざまなサイズの画像のURLです。
  • などは各画像ソースのサイズです。指定できる値は、ピクセル幅 (200wなど) またはピクセル比 (1.5xなど) です。

画像に両方の属性があるというこのシナリオは、現代のレスポンシブサイトではかなり一般的です。srcsetにはより高品質の画像のURLが含まれている可能性があるため、srcの画像URLを直接指定するのは最善の方法ではありません。

上記のHTMLから、すべての画像URLが絶対URLであることもわかります。そのため、WebサイトのベースURLをそれらに連結させる必要はありません。

次のステップでは、Seleniumを使用してPythonで最適な画像を抽出する方法を説明します。

ステップ5:すべての画像のURLを取得する

findElements()メソッドを使用して、ページ上の欲しいHTML画像ノードをすべて選択してください。

image_html_nodes = driver.find_elements(By.CSS_SELECTOR, "[data-test=\"photo-grid-masonry-img\"]")  

この命令が機能するには、次のインポートが必要です。

from selenium.webdriver.common.by import By

次に、画像要素から抽出されたURLを含むリストを初期化します。

image_urls = []

image_html_nodes内のノードを反復処理し、srcのURLまたはsrcsetから最大サイズの画像のURLを収集して (存在する場合)、image_urlsに追加します。

for image_html_node in image_html_nodes:
  try:
    # use the URL in the "src" as the default behavior
    image_url = image_html_node.get_attribute("src")

    # extract the URL of the largest image from "srcset",
    # if this attribute exists
    srcset =  image_html_node.get_attribute("srcset")
    if srcset is not None:
      # get the last element from the "srcset" value
      srcset_last_element = srcset.split(", ")[-1]
      # get the first element of the value,
      # which is the image URL
      image_url = srcset_last_element.split(" ")[0]

    # add the image URL to the list
    image_urls.append(image_url)
  except StaleElementReferenceException as e:
    continue

Unsplashはかなり動的なサイトなので、このループを実行するまでに、一部の画像がページから削除されている場合があります。このエラーを防ぐには、StaleElementReferenceExceptionをキャッチしてください。

繰り返しになりますが、次のインポートを追加するのをお忘れなく。

from selenium.common.exceptions import StaleElementReferenceException

これで、スクレイピングされた画像URLを次を使用してプリントできるようになりました。

print(image_urls)

現在のscraper.pyファイルには以下が含まれているはずです。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.common.exceptions import StaleElementReferenceException

# to run Chrome in headless mode
options = Options()
options.add_argument("--headless")

# initialize a Chrome WerbDriver instance
# with the specified options
driver = webdriver.Chrome(
    service=ChromeService(),
    options=options
)

# to avoid issues with responsive content
driver.maximize_window()

# the URL of the target page
url = "https://unsplash.com/s/photos/wallpaper?license=free"
# visit the target page in the controlled browser
driver.get(url)

# select the node images on the page
image_html_nodes = driver.find_elements(By.CSS_SELECTOR, "[data-test=\"photo-grid-masonry-img\"]")

# where to store the scraped image url
image_urls = []

# extract the URLs from each image
for image_html_node in image_html_nodes:
  try:
    # use the URL in the "src" as the default behavior
    image_url = image_html_node.get_attribute("src")

    # extract the URL of the largest image from "srcset",
    # if this attribute exists
    srcset =  image_html_node.get_attribute("srcset")
    if srcset is not None:
      # get the last element from the "srcset" value
      srcset_last_element = srcset.split(", ")[-1]
      # get the first element of the value,
      # which is the image URL
      image_url = srcset_last_element.split(" ")[0]

    # add the image URL to the list
    image_urls.append(image_url)
  except StaleElementReferenceException as e:
    continue

# log in the terminal the scraped data
print(image_urls)

# close the browser and free up its resources
driver.quit()

スクリプトを実行して画像をスクレイピングすると、次のような出力が得られます。

[
'https://images.unsplash.com/photo-1707343843598-39755549ac9a?w=2000&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxzZWFyY2h8MXx8d2FsbHBhcGVyfGVufDB8fDB8fHwy', 

# omitted for brevity...

'https://images.unsplash.com/photo-1507090960745-b32f65d3113a?w=2000&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MjB8fHdhbGxwYXBlcnxlbnwwfHwwfHx8Mg%3D%3D'
]

できました!上記の配列には、取得する画像のURLが含まれています。さて、残りはPythonで画像をダウンロードする方法だけです。

ステップ6:画像をダウンロードする

Pythonで画像をダウンロードする最も簡単な方法は、標準ライブラリのurl.requestパッケージからurlretrieve()メソッドを使用することです。この機能は、URLで指定されたネットワークオブジェクトをローカルファイルにコピーします。

scraper.pyファイルの上に次の行を追加して、url.requestをインポートしてください。

import urllib.request

プロジェクトフォルダーにimagesディレクトリを作成します。

mkdir images

ここにスクリプトによって画像ファイルが書き込まれます。

次に、スクレイピングされた画像のURLが記載されたリストを繰り返し処理します。各画像について、増分ファイル名を生成し、urlretrieve()を使用して画像をダウンロードします。

image_name_counter = 1

# download each image and add it
# to the "/images" local folder
for image_url in image_urls:
  print(f"downloading image no. {image_name_counter} ...")

  file_name = f"./images/{image_name_counter}.jpg"
  # download the image
  urllib.request.urlretrieve(image_url, file_name)

  print(f"images downloaded successfully to \"{file_name}\"\n")

  # increment the image counter
  image_name_counter += 1

これで、Pythonで画像をダウンロードするために必要な知識がすべて揃いました。print()命令は必須ではありませんが、スクリプトの実行内容を理解するのに便利です。

おめでとうございます!これで、PythonでWebサイトから画像をスクレイピングする方法を学んだことになります。さて、今からはスクレイピング画像のPythonスクリプトのコード全体を確認してみましょう。

ステップ7:仕上げ

以下が最終的なscraper.pyのコードです。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.common.exceptions import StaleElementReferenceException
import urllib.request

# to run Chrome in headless mode
options = Options()
options.add_argument("--headless")

# initialize a Chrome WerbDriver instance
# with the specified options
driver = webdriver.Chrome(
    service=ChromeService(),
    options=options
)

# to avoid issues with responsive content
driver.maximize_window()

# the URL of the target page
url = "https://unsplash.com/s/photos/wallpaper?license=free"
# visit the target page in the controlled browser
driver.get(url)

# select the node images on the page
image_html_nodes = driver.find_elements(By.CSS_SELECTOR, "[data-test=\"photo-grid-masonry-img\"]")

# where to store the scraped image url
image_urls = []

# extract the URLs from each image
for image_html_node in image_html_nodes:
  try:
    # use the URL in the "src" as the default behavior
    image_url = image_html_node.get_attribute("src")

    # extract the URL of the largest image from "srcset",
    # if this attribute exists
    srcset =  image_html_node.get_attribute("srcset")
    if srcset is not None:
      # get the last element from the "srcset" value
      srcset_last_element = srcset.split(", ")[-1]
      # get the first element of the value,
      # which is the image URL
      image_url = srcset_last_element.split(" ")[0]

    # add the image URL to the list
    image_urls.append(image_url)
  except StaleElementReferenceException as e:
    continue

# to keep track of the images saved to disk
image_name_counter = 1

# download each image and add it
# to the "/images" local folder
for image_url in image_urls:
  print(f"downloading image no. {image_name_counter} ...")

  file_name = f"./images/{image_name_counter}.jpg"
  # download the image
  urllib.request.urlretrieve(image_url, file_name)

  print(f"images downloaded successfully to \"{file_name}\"\n")

  # increment the image counter
  image_name_counter += 1

# close the browser and free up its resources
driver.quit()

素晴らしい!Pythonで、100行以下のコードでサイトから画像をダウンロードする自動スクリプトを作成できます。

次のコマンドで実行してください。

python scraper.py

Pythonスクレイピング画像スクリプトは、次の文字列をログに記録します。

downloading image no. 1 ...
images downloaded successfully to "./images/1.jpg"

# omitted for brevity...

downloading image no. 20 ...
images downloaded successfully to "./images/20.jpg"

/imagesフォルダーを確認すると、スクリプトによって自動的にダウンロードされた画像が表示されます。

画像フォルダーを確認中

Unsplashでは常にコンテンツが更新されているため、これらの画像は以前に表示されたUnsplashページのスクリーンショットの画像とは異なります。

ほら、このとおり!ミッション完了。

ステップ8:次のステップ

当初の目的は達成しましたが、Pythonスクリプトを改善するための実行方法がいくつか考えられます。最も重要な実行方法は次の通りです。

  • 画像URLをCSVにエクスポートするか、データベースに保存する:これにより、今後それらをダウンロードまたは使用できるようになります。
  • /imagesフォルダーに既にある画像をダウンロードしない:この改善により、ダウンロード済みの画像がスキップされ、ネットワークリソースが節約されます。
  • メタデータ情報も収集する:タグと作成者情報を取得すると、ダウンロードした画像に関する完全な情報を取得するのに役立ちます。PythonのWebスクレイピングに関するガイドをご覧ください。
  • より多くの画像をスクレイピング:無限スクロール操作をシミュレートし、さらに画像を読み込んで、すべてダウンロードします。

まとめ

このガイドでは、Webサイトからの画像のスクレイピングが有益である理由と、Pythonでのスクレイピングの方法を学びました。具体的には、サイトから画像を自動的にダウンロードできるPython画像スクレイピングスクリプトの作成方法に関する詳細なステップのチュートリアルを確認しました。チュートリアルから分かるように、スクレイピングプログラムはさほど複雑でなく、必要なコードも数行のみです。

同時に、アンチボットシステムに注意が必要です。Seleniumは素晴らしいツールですが、ボット対策システムに検出されては何の役にも立ちません。ボット対策システムにより、スクリプトがボットとして検出され、サイトの画像にアクセスできなくなる可能性があります。

これを回避するために、JavaScriptをレンダリングでき、フィンガープリント、Captcha、スクレイピング対策プログラムを自動的に処理して回避できるツールが必要になります。これこそが、Bright Dataのスクレイピングブラウザの存在意義です!

スクレイピングソリューションについてのご相談がある場合は、当社のデータ専門家にお問い合わせください。

クレジットカードは必要ありません

よくある質問

Webサイトからの画像スクレイピングは合法ですか?

Webサイトから画像をスクレイピングすること自体は違法行為ではありません。同時に、公開されている画像のみをダウンロードし、スクレイピング対策のrobots.txtファイルを尊重し、サイトの利用規約を遵守することが不可欠です。多くの人がWebスクレイピングは違法だと考えていますが、これは誤解です。詳細については、Webスクレイピングに関する誤解についての記事をご覧ください。

Pythonで画像をダウンロードするにはどのライブラリが最適ですか?

静的コンテンツのWebサイトでは、requestsのようなHTTPクライアントとbeautifulsoup4のようなHTMLパーサーがあれば十分です。動的コンテンツのWebサイトやインタラクティブなページでは、SeleniumやPlaywrightなどのブラウザ自動化ツールが必要になります。Webスクレイピングに最適なヘッドレスブラウザツールのリストをご確認ください

urllib.requestにおける「HTTP Error 403: Forbidden」に対処するにはどうすればいいですか?

HTTP403エラーは、対象Webサイトがurllib.requestで行われたリクエストを自動スクリプトからのものと認識したために発生します。この問題を回避する効果的な方法は、User-Agentヘッダーを現実的な値に設定することです。urlretrieve()メソッドを使用する場合、次の方法で実行できます。

opener = urllib.request.build_opener()
user_agent_string = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
opener.addheaders = [("User-Agent", user_agent_header)]
urllib.request.install_opener(opener)
# urllib.request.urlretrieve(...)