Webサイトの画像を一括ダウンロードするPythonスクリプト

プログラミング

Webサイト画像一括ダウンロードPythonスクリプト

はじめに

Webサイトから画像を効率的に収集したい、というニーズは多くの場面で発生します。例えば、Webスクレイピングでデータを集める際、参考資料としてWebサイトの画像を保存したい場合、あるいは個人的なコレクションとして気に入ったWebサイトの画像をまとめてダウンロードしたい場合などが考えられます。

このような作業を手動で行うのは非常に手間がかかり、時間も浪費します。そこで、Pythonの力を用いて、Webサイト上の画像を自動的に一括ダウンロードするスクリプトを作成することは、非常に有効な解決策となります。

本稿では、Pythonを用いてWebサイトの画像を効率的に一括ダウンロードするためのスクリプトについて、その実装方法、必要なライブラリ、および応用的な使い方までを網羅的に解説します。プログラミング初心者の方でも理解できるように、コード例を交えながら丁寧に説明していきます。

スクリプトの基本構成

Webサイトの画像を一括ダウンロードするPythonスクリプトは、主に以下の3つのステップで構成されます。

1. WebページからHTMLを取得する

まず、対象となるWebサイトのHTMLソースコードを取得する必要があります。これには、Pythonの標準ライブラリであるurllib.requestや、より高機能なサードパーティライブラリであるrequestsを使用します。requestsライブラリは、HTTPリクエストを簡単に送信でき、エラーハンドリングも優れているため、一般的に推奨されます。

2. HTMLから画像ファイルのURLを抽出する

取得したHTMLソースコードの中から、画像ファイルのURL(<img>タグのsrc属性など)を抽出します。この際、HTMLを解析するためのライブラリが不可欠です。Beautiful Soupというライブラリは、HTMLやXMLを解析するのに非常に便利で、直感的かつ柔軟な操作が可能です。Beautiful Soupを使用すると、タグ名や属性を指定して簡単に目的の要素を見つけ出すことができます。

3. 抽出したURLから画像をダウンロードする

抽出した画像ファイルのURLに対してHTTPリクエストを送信し、画像データを取得します。取得した画像データは、ローカルファイルとして保存します。ここでもrequestsライブラリが活躍します。ダウンロードした画像は、指定したディレクトリに、元のファイル名や連番などで保存することができます。

必要なライブラリ

このスクリプトを作成するために、以下のPythonライブラリが必要になります。

  • requests: Webページの内容を取得するために使用します。
  • Beautiful Soup (bs4): HTMLを解析し、画像URLを抽出するために使用します。
  • os: ファイルやディレクトリの操作(保存場所の作成など)に使用します。

これらのライブラリは、Pythonのパッケージ管理システムであるpipを使って簡単にインストールできます。ターミナルやコマンドプロンプトで以下のコマンドを実行してください。

pip install requests beautifulsoup4

スクリプトの実装例

以下に、基本的な画像一括ダウンロードスクリプトの例を示します。

import requests
from bs4 import BeautifulSoup
import os
from urllib.parse import urljoin

def download_images(url, download_dir='downloaded_images'):
    """
    指定されたURLのWebページから画像をダウンロードする関数。

    Args:
        url (str): 画像をダウンロードするWebページのURL。
        download_dir (str): 画像を保存するディレクトリ名。
    """
    try:
        # ダウンロードディレクトリが存在しない場合は作成
        if not os.path.exists(download_dir):
            os.makedirs(download_dir)
            print(f"ディレクトリ '{download_dir}' を作成しました。")

        # Webページの内容を取得
        response = requests.get(url)
        response.raise_for_status() # HTTPエラーが発生した場合に例外を発生させる

        # HTMLを解析
        soup = BeautifulSoup(response.text, 'html.parser')

        # <img>タグをすべて検索
        img_tags = soup.find_all('img')

        if not img_tags:
            print("このページには画像が見つかりませんでした。")
            return

        print(f"{len(img_tags)} 個の画像が見つかりました。ダウンロードを開始します...")

        # 画像をダウンロード
        for i, img_tag in enumerate(img_tags):
            img_url = img_tag.get('src') # src属性から画像URLを取得

            if not img_url:
                continue # src属性がない場合はスキップ

            # 相対パスを絶対パスに変換
            img_url = urljoin(url, img_url)

            try:
                img_data = requests.get(img_url, stream=True)
                img_data.raise_for_status()

                # ファイル名を取得(URLの最後の部分をファイル名とする)
                img_filename = os.path.basename(img_url)
                if not img_filename: # URLが '/' で終わる場合など、ファイル名が取得できない場合
                    img_filename = f"image_{i+1}.jpg" # 連番でファイル名を生成

                # ファイルパスを作成
                filepath = os.path.join(download_dir, img_filename)

                # 画像をバイナリモードで保存
                with open(filepath, 'wb') as f:
                    for chunk in img_data.iter_content(chunk_size=8192):
                        f.write(chunk)
                print(f"ダウンロード完了: {img_filename}")

            except requests.exceptions.RequestException as e:
                print(f"画像 {img_url} のダウンロード中にエラーが発生しました: {e}")
            except Exception as e:
                print(f"画像 {img_url} の処理中に予期せぬエラーが発生しました: {e}")

        print("すべての画像のダウンロードが完了しました。")

    except requests.exceptions.RequestException as e:
        print(f"Webページ {url} の取得中にエラーが発生しました: {e}")
    except Exception as e:
        print(f"予期せぬエラーが発生しました: {e}")

# 実行例
if __name__ == "__main__":
    target_url = input("画像をダウンロードしたいWebページのURLを入力してください: ")
    download_dir_name = input("画像を保存するディレクトリ名を入力してください (デフォルト: downloaded_images): ")
    if not download_dir_name:
        download_dir_name = 'downloaded_images'

    download_images(target_url, download_dir_name)

スクリプトの解説

上記のスクリプトは、以下の要素で構成されています。

  • import文: 必要なライブラリをインポートしています。
  • download_images(url, download_dir='downloaded_images')関数:
    • os.makedirs(download_dir): 指定されたディレクトリが存在しない場合に作成します。
    • requests.get(url): 指定されたURLからWebページの内容を取得します。
    • response.raise_for_status(): HTTPリクエストが失敗した場合(ステータスコードが400番台や500番台の場合)に例外を発生させます。これにより、エラー処理が容易になります。
    • BeautifulSoup(response.text, 'html.parser'): 取得したHTMLテキストをBeautiful Soupで解析可能なオブジェクトに変換します。
    • soup.find_all('img'): HTMLドキュメント全体から<img>タグをすべて検索し、リストとして返します。
    • img_tag.get('src'): 各<img>タグからsrc属性の値(画像ファイルのURL)を取得します。
    • urljoin(url, img_url): Webページで指定されている画像URLが相対パス(例: /images/logo.png)の場合、元のWebページのURLと結合して完全な絶対URLに変換します。これはurllib.parseモジュールの機能です。
    • requests.get(img_url, stream=True): 画像ファイルをダウンロードします。stream=Trueを指定することで、大きなファイルもメモリを圧迫せずに段階的にダウンロードできます。
    • os.path.basename(img_url): URLの末尾からファイル名部分を抽出します。
    • os.path.join(download_dir, img_filename): ダウンロードディレクトリとファイル名を結合して、保存先のフルパスを作成します。
    • with open(filepath, 'wb') as f: ... f.iter_content(chunk_size=8192): 画像データをバイナリモード('wb')でファイルに書き込みます。iter_contentを使ってチャンクごとに書き込むことで、メモリ効率が良くなります。
  • if __name__ == "__main__":ブロック:

    このスクリプトが直接実行された場合にのみ、このブロック内のコードが実行されます。ユーザーにURLと保存ディレクトリ名を尋ね、download_images関数を呼び出しています。

応用的な使い方と考慮事項

1. 画像形式のフィルタリング

Webサイトには、画像だけでなく、CSSやJavaScriptファイルへのリンクなども含まれている場合があります。必要に応じて、ダウンロードする画像ファイルの拡張子(例: .jpg, .png, .gif)でフィルタリングすることで、目的の画像のみをダウンロードするようにスクリプトを改良できます。

# img_url を取得した後、以下のような処理を追加
if img_url.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp')):
    # ダウンロード処理に進む
    pass
else:
    continue # 対象外のファイルはスキップ

2. ページネーション(複数ページからのダウンロード)

複数のページにわたって画像が配置されている場合、requestsBeautiful Soupを使って、次のページのURLを特定し、ループ処理で各ページから画像をダウンロードするように拡張できます。次のページへのリンクは、<a>タグのhref属性などを解析して見つけ出すことが一般的です。

3. エラーハンドリングの強化

ネットワークエラー、タイムアウト、存在しないURLへのアクセスなど、様々なエラーが発生する可能性があります。try-exceptブロックを適切に配置し、エラーメッセージを表示したり、リトライ処理を実装したりすることで、スクリプトの堅牢性を高めることができます。

4. ダウンロード速度の制御と遅延

頻繁なアクセスはWebサーバーに負荷をかけ、IPアドレスのブロックにつながる可能性があります。time.sleep()関数を使って、リクエスト間に意図的な遅延を入れることで、サーバーへの負荷を軽減し、より友好的なスクレイピングを行います。

import time
# ...
time.sleep(1) # 1秒待機してから次のリクエストを行う
# ...

5. User-Agentの設定

一部のWebサイトでは、デフォルトのrequestsライブラリからのアクセスをボットとみなし、ブロックすることがあります。User-Agentヘッダーを、ブラウザからアクセスしているかのように偽装することで、この問題を回避できる場合があります。

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
response = requests.get(url, headers=headers)

6. JavaScriptで動的に生成されるコンテンツ

近年、多くのWebサイトではJavaScriptによってコンテンツが動的に生成されます。requestsBeautiful Soupの組み合わせでは、JavaScript実行後のHTMLを取得できないため、これらの画像はダウンロードできません。このような場合、Seleniumのようなブラウザ自動化ツールを使用する必要があります。Seleniumは、実際のブラウザを操作するため、JavaScriptで生成されたコンテンツも取得できますが、実装がやや複雑になります。

7. 利用規約の確認

Webスクレイピングを行う際には、対象Webサイトの利用規約(Terms of Service)やrobots.txtファイルを必ず確認してください。無許可でのスクレイピングは、利用規約違反となり、法的な問題に発展する可能性があります。

まとめ

本稿では、PythonのrequestsライブラリとBeautiful Soupライブラリを利用して、Webサイトの画像を効率的に一括ダウンロードするスクリプトの作成方法について解説しました。基本的なスクリプトの実装から、エラーハンドリング、応用的な機能、そしてWebスクレイピングを行う上での注意点までを網羅しました。

このスクリプトは、Web上の情報を効率的に収集したいという多くのニーズに応える強力なツールとなります。今回紹介したコードをベースに、ご自身の目的に合わせてカスタマイズし、活用していただければ幸いです。Webスクレイピングは、情報収集の可能性を大きく広げる技術ですが、常に倫理的かつ合法的な範囲で行うことが重要です。