PythonとSeleniumで学ぶWebスクレイピングプロジェクト

目次

プロジェクト概要

前提条件

  • Pythonプログラミングの基本的な知識(変数、関数、クラスの概念)
  • VSCodeエディタの基本操作ができること
  • ターミナル(コマンドライン)の基本的な使用経験
  • Gitとバージョン管理の基礎知識
  • HTMLの基本構造に関する理解
  • ファイルとフォルダの基本的な管理ができること
  • プログラミング学習に対する意欲と継続力

開発環境

  • Python 3.7以上
  • VSCode(Visual Studio Code)
  • Google Chrome(最新版推奨)
  • Git(バージョン管理システム)
  • GitHubアカウント
  • selenium 4.33.0
  • webdriver-manager 4.0.2

プロジェクト内容

このプロジェクトでは、PythonのSeleniumライブラリを使用して、実際のWebサイトから書籍情報を自動的に収集するスクレイピングシステムを構築します。初心者でも段階的に学習できるよう、基本的な環境設定から高度なデータ処理機能まで、6つのステップに分けて実装していきます。

プロジェクトの核となるのは、Chrome WebDriverを使用したブラウザ自動化技術です。Headlessモードを活用することで、ブラウザウィンドウを表示せずにバックグラウンドで効率的にデータ収集を行えるシステムを開発します。

データ抽出においては、CSS セレクター技術を使用して正確な要素指定を行い、書籍のタイトル、価格、カテゴリ情報を取得します。取得したデータを辞書構造で整理し、統計情報の計算や価格帯別の分類機能も実装します。さらに、ユーザーが対話的に検索できる機能も追加し、実用的なアプリケーションとして完成させます。

実装内容

  • Chrome WebDriverの初期設定とHeadlessモード設定
  • 仮想環境の構築と依存関係管理(requirements.txt)
  • BookScraperクラスの設計と実装
  • CSS セレクターを使用した要素取得とデータ抽出ロジック
  • 書籍情報の辞書構造での管理システム
  • 統計情報計算機能(平均価格、最高価格、最低価格)
  • 価格帯別自動分類システム
  • キーワード検索機能の実装
  • インタラクティブなユーザーインターフェース
  • GitHubでのバージョン管理とポートフォリオ公開

得られるスキル

  • Selenium WebDriver(ブラウザ自動化の基本操作と高度な設定)
  • Headlessモード実装(バックグラウンドでのWebブラウザ制御技術)
  • CSS セレクター技術(正確な要素指定とデータ抽出手法)
  • Chrome WebDriver管理(webdriver-managerを使用した自動化)
  • データ構造設計(辞書とリストを活用した効率的なデータ管理)
  • エラーハンドリング(try-except文を使った安定性の高いプログラム設計)

得られる経験

  • 実際のWebサイトからのデータ取得経験
  • 大量データの処理と分析手法の習得
  • ユーザーインタラクションを含むプログラム開発
  • GitHubを使用したバージョン管理の実践的経験
  • 仮想環境での開発環境構築と管理

得られる成果物

  • 完全に動作するWebスクレイピングシステム
  • GitHubでのポートフォリオとして活用可能なプロジェクト
  • 再利用可能なコードベース
  • 詳細なドキュメント(README.md)
  • 適切な依存関係管理(requirements.txt)

このプロジェクトから応用できること

  • Selenium WebDriverを使用してECサイトの価格監視ツールを開発し、副業案件として提供
  • CSS セレクターによる要素取得技術を活用して不動産情報の自動収集システムを構築
  • Headlessモードを利用した求人情報の定期収集サービスの開発
  • データ分析機能を応用した競合他社の商品情報監視ツールの作成
  • 辞書構造によるデータ管理技術を使った在庫情報の自動更新システムの実装
  • インタラクティブ機能を活用したデータ検索・分析ツールの開発

Step1:開発環境の準備とプロジェクト構造の作成

このステップでは、書籍情報スクレイピングプロジェクトを始めるための開発環境を整備し、必要なファイル構造を作成していきます。プログラミングを始める前の準備段階として、とても重要な作業になります。

※Step1の内容がわからない際は、下記記事にGitHubとVSCodeの連携について詳細を記載しているためご参照ください。

GitHubリポジトリの作成

まず最初に、プロジェクト専用のGitHubリポジトリを作成しましょう。

  1. GitHubの公式サイトにアクセスし、自分のアカウントでログインします
  2. 画面右上の「+」アイコンをクリックし、ドロップダウンメニューから「New repository」を選択します
  3. 新しいリポジトリの作成ページで、以下の情報を入力します

リポジトリの設定

  • Repository namebook_scraping_project」と入力
  • DescriptionSeleniumを使用した書籍情報スクレイピングシステム」と入力
  • VisibilityPublic」を選択(ポートフォリオとして公開するため)
  • Initialize this repository with
    • Add a README file」にチェックを入れる
    • Add .gitignore」で「Python」を選択
    • Choose a license」は「None」のままにする
  1. Create repository」ボタンをクリックしてリポジトリを作成します

プロジェクトフォルダの作成

次に、プロジェクトを管理するための親フォルダを作成しましょう。

Windows・macOS共通

  1. デスクトップや任意の場所に新しいフォルダを作成します
  2. フォルダ名は任意ですが、ここでは例として「PythonPortfolio」に設定します

この親フォルダが、今後のプロジェクト全体を管理するルートディレクトリとなります。複数のプロジェクトを整理して管理するために作成しておくと便利です。

VSCodeでプロジェクトを開く

作成したGitHubリポジトリをローカルにクローンして開きましょう。

  1. VSCodeを起動します
  2. 左側のアクティビティバーからソース管理アイコン(分岐のようなアイコン)をクリックします
  3. リポジトリの複製」ボタンをクリックします
  4. GitHubから複製」を選択します
  5. 先ほど作成した「book_scraping_project」リポジトリを選択します
  6. リポジトリ宛先として先ほど作成した親フォルダを選択します
  7. クローンしたリポジトリを開きますか」というウィンドウが表示されたら「開く」をクリックします

VSCodeの左側にエクスプローラーパネルが表示され、現在のプロジェクトフォルダの中身が確認できるようになります。

仮想環境の作成とアクティベート

Python開発において仮想環境は非常に重要です。仮想環境とは、プロジェクトごとに独立したPython実行環境を作成する仕組みのことです。これにより、プロジェクト間でのライブラリの競合を防ぎ、クリーンな開発環境を維持できます。

詳しくは下記記事をご参照ください。

Windows環境での仮想環境作成

  1. VSCodeの上部メニューから「ターミナル」→「新しいターミナル」を選択
  2. 以下のコマンドで仮想環境を作成します
python -m venv myenv
  1. 仮想環境をアクティベートします
myenv\Scripts\activate

macOS環境での仮想環境作成

  1. VSCodeの上部メニューから「ターミナル」→「新しいターミナル」を選択
  2. 以下のコマンドで仮想環境を作成します
python3 -m venv myenv
  1. 仮想環境をアクティベートします
source myenv/bin/activate

重要なポイント

  • 仮想環境名は「myenv」として作成
  • ターミナルの行頭に「(myenv)」と表示されることを確認
  • この表示があることで、仮想環境が正常にアクティベートされていることがわかります

プロジェクトファイル構造の作成

次に、今回の書籍情報スクレイピングプロジェクトに必要なファイルを作成していきます。VSCodeのエクスプローラーパネルを使用してファイルを作成しましょう。

メインプログラムファイルの作成

  1. VSCodeの左側エクスプローラーパネルで「新しいファイル」アイコン(ファイルの絵とプラスのマークがあるアイコン)をクリック
  2. ファイル名を「book_scraper.py」として作成

この「book_scraper.py」ファイルが、今回の書籍情報スクレイピング機能を実装するメインプログラムとなります。.py拡張子Pythonスクリプトファイルであることを示しており、実行可能なプログラムコードを記述するために使用します。

依存関係管理ファイルの作成

  1. プロジェクトのルートフォルダ(book_scraping_project)をクリックして選択
  2. 新しいファイル」アイコンをクリック
  3. ファイル名を「requirements.txt」として作成

requirements.txtファイルは、Pythonプロジェクトで使用する外部ライブラリとそのバージョンを記録するための重要なファイルです。これにより、他の開発者や本番環境でも同じライブラリ環境を再現できます。

requirements.txtの内容設定

作成した「requirements.txt」ファイルをVSCodeで開き、以下の内容を記述してください。

selenium==4.33.0
webdriver-manager==4.0.2

今回のプロジェクトでは、2つのライブラリを使用します。

  • selenium Webブラウザを自動化するためのツールです。JavaScriptが動的に生成するコンテンツも取得できる強力なスクレイピングライブラリです。実際のブラウザを制御して、人間がWebサイトを操作するのと同じような動作を自動化できます
  • webdriver-manager SeleniumでWebブラウザを制御するためのWebDriverを自動的にダウンロード・管理してくれる便利なツールです。従来は手動でWebDriverをダウンロードする必要がありましたが、このライブラリにより自動化が実現されます

プロジェクト構造の確認

この時点で、VSCodeのエクスプローラーパネルには以下のような構造が表示されているはずです。

book_scraping_project/
├── myenv/                   # 仮想環境フォルダ
├── book_scraper.py          # メインプログラムファイル
├── requirements.txt         # 依存関係管理ファイル
├── README.md                # プロジェクト説明書(GitHub作成時に自動生成)
└── .gitignore               # Git除外設定ファイル(GitHub作成時に自動生成)

この基本構造が、今回の書籍情報スクレイピングプロジェクトの基盤となります。GitHubで作成したリポジトリをクローンしたため、README.md.gitignoreファイルが最初から含まれています。

仮想環境が正常にアクティベートされていることを再度確認し(ターミナルに「(myenv)」が表示されている状態)、次のステップに進む準備を整えましょう。

ライブラリのインストール

仮想環境がアクティベートされている状態で、VSCodeのターミナルから以下のコマンドを実行します。

pip install -r requirements.txt

このコマンドにより、requirements.txtに記載されたseleniumとwebdriver-managerライブラリがインストールされます。インストール処理には数分かかる場合がありますので、しばらくお待ちください。

インストールが完了すると、ターミナルに「Successfully installed selenium-4.33.0 webdriver-manager-4.0.2」のようなメッセージが表示され、プロジェクトの環境準備が完了します。

Step2:必要なライブラリの導入と機能解説

このステップでは、Step1で作成したrequirements.txtファイルに記載されているライブラリを実際にインストールし、各ライブラリの詳細な機能について学んでいきます。

ライブラリの機能詳細解説

インストールしたライブラリの具体的な機能と、スクレイピングプロジェクトでの活用方法を詳しく説明します。

Seleniumの詳細機能

  • 実ブラウザ制御 Chrome、Firefox、Safari等の実際のブラウザを操作
  • JavaScript対応 動的に生成されるコンテンツも取得可能
  • ユーザー操作の再現 クリック、文字入力、スクロールなどの操作を自動化

webdriver-managerの詳細機能

  • 自動ダウンロード 最適なWebDriverを自動で取得
  • バージョン管理 ブラウザの更新に自動対応
  • 簡単な実装 1行のコードで初期設定完了

動作確認テストの実装

各ライブラリが正常に動作するか、簡単なテストプログラムを作成しましょう。VSCodeでbook_scraper.pyファイルを開き、以下のテストコードを記述してください。

# ライブラリのインポートテスト
try:
    # Seleniumの中核機能をインポート
    from selenium import webdriver
    # WebDriverサービスの管理用
    from selenium.webdriver.chrome.service import Service
    # WebDriverの自動管理用
    from webdriver_manager.chrome import ChromeDriverManager
    print("✓ すべてのライブラリが正常にインポートされました")
except ImportError as e:
    # インポートエラーが発生した場合の処理
    print(f"✗ ライブラリのインポートエラー: {e}")

# WebDriverの動作確認テスト
try:
    # ChromeDriverManagerで自動的にドライバーをインストール
    service = Service(ChromeDriverManager().install())
    print("✓ WebDriverManagerが正常に動作しています")
except Exception as e:
    # WebDriverの初期化でエラーが発生した場合の処理
    print(f"✗ WebDriverManagerエラー: {e}")

動作テスト実行

VSCodeのターミナルで以下のコマンドを実行してテストを行います。

python book_scraper.py

出力結果

✓ すべてのライブラリが正常にインポートされました
✓ WebDriverManagerが正常に動作しています

Step3:基本スクレイピング機能の実装

このステップでは、Chrome WebDriverの設定とHeadlessモードを含む基本的なスクレイピング機能を実装していきます。実際にWebサイトにアクセスして情報を取得するための土台を構築します。

基本ライブラリのインポート

book_scraper.pyファイルの内容を全て削除し、以下の本格的なスクレイピングプログラムを記述します。

# Webスクレイピング用ライブラリのインポート
from selenium import webdriver  # ブラウザ自動制御の中核機能
from selenium.webdriver.chrome.service import Service  # WebDriverサービス管理
from selenium.webdriver.chrome.options import Options  # Chromeブラウザの詳細設定
from selenium.webdriver.common.by import By  # 要素の検索方法を指定
from webdriver_manager.chrome import ChromeDriverManager  # ChromeDriver自動管理
import time  # 処理の待機時間制御

BookScraperクラスの作成

スクレイピング機能をまとめて管理するクラスを作成します。このクラスの中に、今後必要な機能をすべてdef文(関数)として実装していきます。

class BookScraper:
    def __init__(self, headless=True):
        """
        BookScraperクラスの初期化メソッド
        headless: Trueの場合、ブラウザウィンドウを表示しない
        """
        # インスタンス変数の初期化
        self.headless = headless  # Headlessモードの設定を保存
        self.driver = None  # WebDriverインスタンスを格納する変数
        # WebDriverの初期設定メソッドを呼び出し
        self.setup_driver()

Chrome WebDriverの設定機能

次に、クラス内にsetup_driverメソッドを実装します。

    def setup_driver(self):
        """Chrome WebDriverの詳細設定を行うメソッド"""
        # Chromeブラウザのオプション設定を初期化
        chrome_options = Options()
        
        # Headlessモードの設定(ブラウザウィンドウを表示しない)
        if self.headless:
            chrome_options.add_argument("--headless")
        
        # パフォーマンス向上のための設定
        chrome_options.add_argument("--disable-dev-shm-usage")  # 共有メモリ使用を無効化
        chrome_options.add_argument("--no-sandbox")  # サンドボックス機能を無効化
        chrome_options.add_argument("--disable-gpu")  # GPU加速を無効化
        chrome_options.add_argument("--disable-extensions")  # 拡張機能を無効化
        chrome_options.add_argument("--disable-plugins")  # プラグインを無効化
        
        # WebDriverサービスの初期化(ChromeDriverManagerで自動管理)
        service = Service(ChromeDriverManager().install())
        # Chrome WebDriverインスタンスを作成
        self.driver = webdriver.Chrome(service=service, options=chrome_options)
        
        # ページ読み込み待機時間の設定(10秒)
        self.driver.implicitly_wait(10)

Webサイトアクセス機能の実装

続いて、クラス内にaccess_websiteメソッドを追加します。

    def access_website(self, url):
        """
        指定されたURLにアクセスしてページを読み込むメソッド
        url: アクセス対象のWebサイトURL
        戻り値: アクセス成功時True、失敗時False
        """
        try:
            # アクセス開始をコンソールに表示
            print(f"アクセス中: {url}")
            # 指定されたURLにアクセス
            self.driver.get(url)
            
            # ページ読み込み完了まで3秒待機
            time.sleep(3)
            
            # ページタイトルを取得して表示
            page_title = self.driver.title
            print(f"ページタイトル: {page_title}")
            
            # アクセス成功を示すTrueを返す
            return True
            
        except Exception as e:
            # エラーが発生した場合の処理
            print(f"アクセスエラー: {e}")
            # アクセス失敗を示すFalseを返す
            return False

ブラウザ終了処理の実装

リソース管理のため、クラス内にclose_browserメソッドを実装します。

    def close_browser(self):
        """ブラウザを安全に終了するメソッド"""
        # WebDriverインスタンスが存在する場合のみ終了処理を実行
        if self.driver:
            # ブラウザを完全に終了
            self.driver.quit()
            print("ブラウザを終了しました")

メイン実行関数の作成

クラスの外に、作成したBookScraperクラスを実際に使用するメイン処理を実装します。

def main():
    """メイン実行関数"""
    # スクレイピング対象のURL(練習用サイト)
    target_url = "https://books.toscrape.com/"
    
    # BookScraperインスタンスを作成(Headlessモードで実行)
    scraper = BookScraper(headless=True)
    
    try:
        # Webサイトにアクセスを試行
        if scraper.access_website(target_url):
            print("✓ Webサイトへのアクセスが成功しました")
        else:
            print("✗ Webサイトへのアクセスに失敗しました")
            
    except Exception as e:
        # 予期しないエラーが発生した場合の処理
        print(f"実行エラー: {e}")
        
    finally:
        # 正常終了・異常終了に関わらず必ずブラウザを終了
        scraper.close_browser()

# プログラムのエントリーポイント
if __name__ == "__main__":
    main()

動作テスト実行

VSCodeのターミナルで以下のコマンドを実行してテストします。

python book_scraper.py

出力結果

アクセス中: https://books.toscrape.com/
ページタイトル: All products | Books to Scrape - Sandbox
✓ Webサイトへのアクセスが成功しました
ブラウザを終了しました

Step4:データ抽出ロジックの構築

このステップでは、Webページから実際の書籍情報を取得するためのデータ抽出機能を実装します。CSS セレクターを使用して、特定の要素を正確に取得する方法を学びます。

書籍情報取得メソッドの追加

BookScraperクラス内に、書籍一覧を取得する新しいメソッドを追加します。

    def get_book_menu(self, blink):
        """
        書籍メニュー情報を取得するメソッド
        blink: 対象WebサイトのURL
        戻り値: カテゴリ別に整理された書籍データの辞書
        """
        # 書籍データを格納する辞書を初期化
        current_book_data = {}
        
        try:
            # 書籍要素を全て取得
            book_elements = self.driver.find_elements(By.CSS_SELECTOR, "article.product_pod")
            print(f"発見した書籍数: {len(book_elements)}")
            
            # カテゴリ別に書籍を分類
            categories = {
                "Travel": [],
                "Mystery": [],
                "Historical Fiction": [],
                "Sequential Art": [],
                "Classics": [],
                "Philosophy": [],
                "Romance": [],
                "Womens Fiction": [],
                "Fiction": [],
                "Childrens": []
            }
            
            # 各書籍要素を処理
            for index, book in enumerate(book_elements):
                try:
                    # 書籍タイトルを取得
                    title_element = book.find_element(By.CSS_SELECTOR, "h3 a")
                    item_name = title_element.get_attribute("title")
                    
                    # 価格を取得
                    price_element = book.find_element(By.CSS_SELECTOR, "div.product_price p.price_color")
                    item_price = price_element.text.replace("£", "")
                    
                    # カテゴリを決定(簡単な分類ロジック)
                    category = self.determine_category(item_name, index)
                    
                    # カテゴリに書籍情報を追加
                    if category in categories:
                        categories[category].append([item_name, item_price])
                        
                except Exception as e:
                    # 個別の書籍取得に失敗した場合はスキップ
                    continue
            
            # カテゴリごとのデータを辞書形式に変換
            for category_name, items in categories.items():
                if items:  # アイテムが存在する場合のみ処理
                    category_name_data = {}
                    for item in items:
                        item_name = item[0]  # 書籍名
                        item_price = item[1]  # 価格
                        category_name_data[item_name] = item_price
                    
                    current_book_data[category_name] = category_name_data
                    
        except Exception as e:
            print(f"データ取得エラー: {e}")
        
        return current_book_data
    
    def determine_category(self, title, index):
        """
        書籍タイトルとインデックスからカテゴリを決定するメソッド
        title: 書籍タイトル
        index: 書籍のインデックス
        戻り値: カテゴリ名
        """
        # タイトルに基づく簡単な分類
        title_lower = title.lower()
        
        if any(word in title_lower for word in ["travel", "journey", "world", "guide"]):
            return "Travel"
        elif any(word in title_lower for word in ["mystery", "murder", "detective", "crime"]):
            return "Mystery"
        elif any(word in title_lower for word in ["history", "historical", "war", "ancient"]):
            return "Historical Fiction"
        elif any(word in title_lower for word in ["art", "comic", "graphic"]):
            return "Sequential Art"
        elif any(word in title_lower for word in ["classic", "shakespeare", "literature"]):
            return "Classics"
        elif any(word in title_lower for word in ["philosophy", "think", "wisdom", "mind"]):
            return "Philosophy"
        elif any(word in title_lower for word in ["love", "romance", "heart", "wedding"]):
            return "Romance"
        elif any(word in title_lower for word in ["woman", "women", "girl", "female"]):
            return "Womens Fiction"
        elif any(word in title_lower for word in ["child", "children", "kid", "young"]):
            return "Childrens"
        else:
            return "Fiction"

メイン実行部分の更新

main関数を更新して、新しく追加したデータ抽出機能を使用するようにします。

def main():
    """メイン実行関数"""
    print("書籍スクレイピングを開始します")
    # ユーザーから対象サイトのURLを入力
    blink = str(input("対象サイトのURL: "))
    
    # BookScraperインスタンスを作成
    scraper = BookScraper(headless=True)
    
    try:
        # Webサイトにアクセスを試行
        if scraper.access_website(blink):
            print("✓ Webサイトへのアクセスが成功しました")
            
            # 書籍データを取得
            data = scraper.get_book_menu(blink)
            
            # 取得したデータをカテゴリごとに表示
            for category in data:
                category_name = category
                print("## ", category_name)
                print()
                
                # カテゴリ内のアイテムを取得
                items = data.get(category)
                # 各アイテムの名前と価格を表示
                for item in items:
                    print(item, "  : ¥", items.get(item))
                print()
            
        else:
            print("✗ Webサイトへのアクセスに失敗しました")
            
    except Exception as e:
        # 予期しないエラーが発生した場合の処理
        print(f"実行エラー: {e}")
        
    finally:
        # 正常終了・異常終了に関わらず必ずブラウザを終了
        scraper.close_browser()

# プログラムのエントリーポイント
if __name__ == "__main__":
    main()

動作テスト実行

VSCodeのターミナルで以下のコマンドを実行してテストします。

python book_scraper.py

実行後、「対象サイトのURL: 」というプロンプトが表示されるので、以下のURLを入力してください。

https://books.toscrape.com/
出力結果
書籍スクレイピングを開始します
対象サイトのURL: https://books.toscrape.com/
アクセス中: https://books.toscrape.com/
ページタイトル: All products | Books to Scrape - Sandbox
✓ Webサイトへのアクセスが成功しました
発見した書籍数: 20
##  Historical Fiction

Sapiens: A Brief History of Humankind   : ¥ 54.23

##  Sequential Art

Starving Hearts (Triangular Trade Trilogy, #1)   : ¥ 13.99
Rip it Up and Start Again   : ¥ 35.02

##  Classics

Shakespeare's Sonnets   : ¥ 20.66

##  Womens Fiction

The Coming Woman: A Novel Based on the Life of the Infamous Feminist, Victoria Woodhull   : ¥ 17.93

##  Fiction

A Light in the Attic   : ¥ 51.77
Tipping the Velvet   : ¥ 53.74
Soumission   : ¥ 50.10
Sharp Objects   : ¥ 47.82
The Requiem Red   : ¥ 22.65
The Dirty Little Secrets of Getting Your Dream Job   : ¥ 33.34
The Boys in the Boat: Nine Americans and Their Epic Quest for Gold at the 1936 Berlin Olympics   : ¥ 22.60
The Black Maria   : ¥ 52.15
Set Me Free   : ¥ 17.46
Scott Pilgrim's Precious Little Life (Scott Pilgrim #1)   : ¥ 52.29
Our Band Could Be Your Life: Scenes from the American Indie Underground, 1981-1991   : ¥ 57.25
Olio   : ¥ 23.88
Mesaerion: The Best Science Fiction Stories 1800-1849   : ¥ 37.59
Libertarianism for Beginners   : ¥ 51.33
It's Only the Himalayas   : ¥ 45.17

ブラウザを終了しました

Step5:データ処理と表示機能の追加

このステップでは、取得した書籍データをより効果的に処理し、辞書構造での管理や統計情報の表示など、実用的なデータ処理機能を実装します。

データ分析メソッドの追加

BookScraperクラス内に、取得したデータを分析する機能を追加します。

    def analyze_data(self, book_data):
        """
        取得した書籍データを分析するメソッド
        book_data: 書籍データの辞書
        """
        # データが存在しない場合の処理
        if not book_data:
            print("分析対象のデータがありません")
            return
        
        # 統計情報を格納する変数を初期化
        total_categories = len(book_data)  # カテゴリ数
        total_items = 0  # 総商品数
        all_prices = []  # 全価格のリスト
        
        # 各カテゴリを順次処理
        for category in book_data:
            items = book_data[category]  # カテゴリ内のアイテム取得
            category_count = len(items)  # カテゴリ内の商品数
            total_items += category_count  # 総商品数に加算
            
            # カテゴリ内の各アイテムの価格を処理
            for item_name, price in items.items():
                try:
                    # 価格を数値に変換してリストに追加
                    price_value = float(price)
                    all_prices.append(price_value)
                except ValueError:
                    # 価格変換に失敗した場合はスキップ
                    continue
        
        # 基本統計情報を表示
        print(f"カテゴリ数: {total_categories}")
        print(f"総商品数: {total_items}")
        
        # 価格データが存在する場合の統計計算
        if all_prices:
            # 平均価格の計算
            average_price = sum(all_prices) / len(all_prices)
            # 最高価格と最低価格の取得
            max_price = max(all_prices)
            min_price = min(all_prices)
            
            # 統計情報を表示
            print(f"平均価格: ¥{average_price:.2f}")
            print(f"最高価格: ¥{max_price}")
            print(f"最低価格: ¥{min_price}")

カテゴリ別詳細表示メソッドの実装

各カテゴリの詳細情報を表示するメソッドをクラス内に追加します。

    def display_category_details(self, book_data):
        """
        カテゴリ別の詳細情報を表示するメソッド
        book_data: 書籍データの辞書
        """
        print("\n" + "="*60)
        print("カテゴリ別詳細情報")
        print("="*60)
        
        # 各カテゴリを順次処理
        for category_name in book_data:
            items = book_data[category_name]  # カテゴリ内のアイテム
            item_count = len(items)  # アイテム数
            
            # アイテムが存在する場合の処理
            if items:
                prices = []  # 価格リストを初期化
                # カテゴリ内の各価格を数値化してリストに追加
                for price in items.values():
                    try:
                        prices.append(float(price))
                    except ValueError:
                        # 価格変換に失敗した場合はスキップ
                        continue
                
                # 価格データが存在する場合の統計計算
                if prices:
                    # カテゴリ内の統計情報を計算
                    avg_price = sum(prices) / len(prices)
                    max_price = max(prices)
                    min_price = min(prices)
                    
                    # カテゴリ情報を表示
                    print(f"\n【{category_name}】")
                    print(f"商品数: {item_count}点")
                    print(f"価格帯: ¥{min_price} ~ ¥{max_price}")
                    print(f"平均価格: ¥{avg_price:.2f}")
                    
                    # 高額商品TOP3を表示
                    # 価格で降順ソートして上位3件を取得
                    top_items = sorted(items.items(), key=lambda x: float(x[1]), reverse=True)[:3]
                    print("高額商品TOP3")
                    for i, (name, price) in enumerate(top_items, 1):
                        print(f"  {i}. {name} - ¥{price}")

価格帯別分類機能の実装

商品を価格帯別に分類する機能をクラス内に追加します。

    def classify_by_price_range(self, book_data):
        """
        商品を価格帯別に分類するメソッド
        book_data: 書籍データの辞書
        """
        # 価格帯別の分類用辞書を初期化
        price_ranges = {
            "低価格帯(¥0-30)": [],
            "中価格帯(¥30-50)": [],
            "高価格帯(¥50以上)": []
        }
        
        # 各カテゴリを順次処理
        for category in book_data:
            items = book_data[category]  # カテゴリ内のアイテム
            # カテゴリ内の各アイテムを処理
            for item_name, price in items.items():
                try:
                    # 価格を数値に変換
                    price_value = float(price)
                    
                    # 価格帯による分類
                    if price_value < 30:
                        # 低価格帯に追加
                        price_ranges["低価格帯(¥0-30)"].append((item_name, price_value, category))
                    elif price_value < 50:
                        # 中価格帯に追加
                        price_ranges["中価格帯(¥30-50)"].append((item_name, price_value, category))
                    else:
                        # 高価格帯に追加
                        price_ranges["高価格帯(¥50以上)"].append((item_name, price_value, category))
                        
                except ValueError:
                    # 価格変換に失敗した場合はスキップ
                    continue
        
        # 分類結果を表示
        print("\n" + "="*60)
        print("価格帯別商品分類")
        print("="*60)
        
        # 各価格帯の結果を表示
        for range_name, items in price_ranges.items():
            print(f"\n【{range_name}】 {len(items)}点")
            
            # アイテムが存在する場合の詳細表示
            if items:
                # 価格で降順ソートして上位5件を表示
                sorted_items = sorted(items, key=lambda x: x[1], reverse=True)
                for item_name, price, category in sorted_items[:5]:
                    print(f"  • {item_name[:40]}... (¥{price}) - {category}")
                
                # 5件を超える場合は残り件数を表示
                if len(items) > 5:
                    print(f"  ... その他{len(items) - 5}点")

検索機能の実装

特定のキーワードで商品を検索する機能をクラス内に追加します。

    def search_items(self, book_data, keyword):
        """
        キーワードで商品を検索するメソッド
        book_data: 書籍データの辞書
        keyword: 検索キーワード
        """
        search_results = []  # 検索結果を格納するリスト
        
        # 各カテゴリを順次処理
        for category in book_data:
            items = book_data[category]  # カテゴリ内のアイテム
            # カテゴリ内の各アイテムをチェック
            for item_name, price in items.items():
                # 商品名に検索キーワードが含まれるかチェック(大文字小文字無視)
                if keyword.lower() in item_name.lower():
                    # 検索結果に追加
                    search_results.append({
                        "name": item_name,
                        "price": price,
                        "category": category
                    })
        
        # 検索結果を表示
        print(f"\n検索キーワード: '{keyword}'")
        print(f"検索結果: {len(search_results)}件")
        print("-" * 40)
        
        # 検索結果が存在する場合の詳細表示
        if search_results:
            for result in search_results:
                print(f"商品名: {result['name']}")
                print(f"価格: ¥{result['price']}")
                print(f"カテゴリ: {result['category']}")
                print("-" * 30)
        else:
            print("該当する商品が見つかりませんでした")

メイン実行部分の拡張

main関数を更新して、新しく追加した分析・表示機能を活用します。

def main():
    """メイン実行関数"""
    print("書籍スクレイピングシステム")
    print("=" * 30)
    
    # ユーザーから対象サイトのURLを入力
    blink = str(input("対象サイトのURL: "))
    
    # BookScraperインスタンスを作成
    scraper = BookScraper(headless=True)
    
    try:
        # Webサイトにアクセスを試行
        if scraper.access_website(blink):
            print("✓ Webサイトへのアクセスが成功しました")
            
            # 書籍データを取得
            data = scraper.get_book_menu(blink)
            
            # データが正常に取得できた場合の処理
            if data:
                # 各種分析・表示機能を実行
                scraper.analyze_data(data)  # データ分析
                scraper.display_category_details(data)  # カテゴリ別詳細
                scraper.classify_by_price_range(data)  # 価格帯別分類
                
                # インタラクティブな検索機能
                while True:
                    print("\n検索機能を使用しますか? (y/n)")
                    choice = input().lower()
                    
                    if choice == 'y':
                        # 検索キーワードを入力
                        keyword = input("検索キーワードを入力してください: ")
                        scraper.search_items(data, keyword)
                    elif choice == 'n':
                        # 検索を終了
                        break
                    else:
                        print("y または n を入力してください")
            else:
                print("データの取得に失敗しました")
            
        else:
            print("✗ Webサイトへのアクセスに失敗しました")
            
    except Exception as e:
        # 予期しないエラーが発生した場合の処理
        print(f"実行エラー: {e}")
        
    finally:
        # 正常終了・異常終了に関わらず必ずブラウザを終了
        scraper.close_browser()

# プログラムのエントリーポイント
if __name__ == "__main__":
    main()

動作テスト実行

VSCodeのターミナルで以下のコマンドを実行してテストします。

python book_scraper.py

実行後、「対象サイトのURL: 」というプロンプトが表示されるので、以下のURLを入力してください。

https://books.toscrape.com/

出力結果

出力例
書籍スクレイピングシステム
==============================
対象サイトのURL: https://books.toscrape.com/
アクセス中: https://books.toscrape.com/
ページタイトル: All products | Books to Scrape - Sandbox
✓ Webサイトへのアクセスが成功しました
発見した書籍数: 20
カテゴリ数: 5
総商品数: 20
平均価格: ¥38.05
最高価格: ¥57.25
最低価格: ¥13.99

============================================================
カテゴリ別詳細情報
============================================================

【Historical Fiction】
商品数: 1点
価格帯: ¥54.23 ~ ¥54.23
平均価格: ¥54.23
高額商品TOP3
  1. Sapiens: A Brief History of Humankind - ¥54.23

【Sequential Art】
商品数: 2点
価格帯: ¥13.99 ~ ¥35.02
平均価格: ¥24.51
高額商品TOP3
  1. Rip it Up and Start Again - ¥35.02
  2. Starving Hearts (Triangular Trade Trilogy, #1) - ¥13.99

【Classics】
商品数: 1点
価格帯: ¥20.66 ~ ¥20.66
平均価格: ¥20.66
高額商品TOP3
  1. Shakespeare's Sonnets - ¥20.66

【Womens Fiction】
商品数: 1点
価格帯: ¥17.93 ~ ¥17.93
平均価格: ¥17.93
高額商品TOP3
  1. The Coming Woman: A Novel Based on the Life of the Infamous Feminist, Victoria Woodhull - ¥17.93

【Fiction】
商品数: 15点
価格帯: ¥17.46 ~ ¥57.25
平均価格: ¥41.28
高額商品TOP3
  1. Our Band Could Be Your Life: Scenes from the American Indie Underground, 1981-1991 - ¥57.25
  2. Tipping the Velvet - ¥53.74
  3. Scott Pilgrim's Precious Little Life (Scott Pilgrim #1) - ¥52.29

============================================================
価格帯別商品分類
============================================================

【低価格帯(¥0-30)】 7点
  • Olio... (¥23.88) - Fiction
  • The Requiem Red... (¥22.65) - Fiction
  • The Boys in the Boat: Nine Americans and... (¥22.6) - Fiction
  • Shakespeare's Sonnets... (¥20.66) - Classics
  • The Coming Woman: A Novel Based on the L... (¥17.93) - Womens Fiction
  ... その他2点

【中価格帯(¥30-50)】 5点
  • Sharp Objects... (¥47.82) - Fiction
  • It's Only the Himalayas... (¥45.17) - Fiction
  • Mesaerion: The Best Science Fiction Stor... (¥37.59) - Fiction
  • Rip it Up and Start Again... (¥35.02) - Sequential Art
  • The Dirty Little Secrets of Getting Your... (¥33.34) - Fiction

【高価格帯(¥50以上)】 8点
  • Our Band Could Be Your Life: Scenes from... (¥57.25) - Fiction
  • Sapiens: A Brief History of Humankind... (¥54.23) - Historical Fiction
  • Tipping the Velvet... (¥53.74) - Fiction
  • Scott Pilgrim's Precious Little Life (Sc... (¥52.29) - Fiction
  • The Black Maria... (¥52.15) - Fiction
  ... その他3点

検索機能を使用しますか? (y/n)
y
検索キーワードを入力してください:  Life

検索キーワード: ' Life'
検索結果: 3件
----------------------------------------
商品名: The Coming Woman: A Novel Based on the Life of the Infamous Feminist, Victoria Woodhull
価格: ¥17.93
カテゴリ: Womens Fiction
------------------------------
商品名: Scott Pilgrim's Precious Little Life (Scott Pilgrim #1)
価格: ¥52.29
カテゴリ: Fiction
------------------------------
商品名: Our Band Could Be Your Life: Scenes from the American Indie Underground, 1981-1991
価格: ¥57.25
カテゴリ: Fiction
------------------------------

Step6:プロジェクトをGitHubにアップロード

このステップでは、完成した書籍情報スクレイピングプロジェクトをGitHubにアップロードし、ポートフォリオとして活用できる形にします。最終的なファイル整理とコードの公開を行っていきます。

.gitignoreファイルの更新

既存の.gitignoreファイルに、プロジェクト固有の除外設定を追加します。

VSCodeで「.gitignore」ファイルを開き、以下の内容を末尾に追加してください。

# Virtual environment
myenv/

# Scraped data files
*.log

これらを追加する理由について説明します。

  • myenv/ 仮想環境フォルダはプロジェクト固有のため、リポジトリに含める必要がありません
  • *.log スクレイピング実行時に生成されるログファイルを除外

README.mdファイルの更新

リポジトリ作成時に生成されたREADME.mdファイルの内容を、プロジェクトの詳細情報に更新します。

VSCodeで「README.md」ファイルを開き、内容を以下のように全て置き換えてください。

# 書籍情報スクレイピングプロジェクト

## プロジェクト内容
Seleniumを使用してWebサイトから書籍情報を自動取得するスクレイピングシステムです。実際のWebサイトから書籍のタイトル、価格、カテゴリ情報を効率的に収集し、データ分析機能も備えています。Pythonの基本的なWebスクレイピング技術を学習することを目的として実装しました。

## プロジェクト構成
```
book_scraping_project/
├── book_scraper.py          # メインスクレイピングプログラム
├── requirements.txt         # 依存関係管理
├── README.md               # プロジェクト説明書
└── .gitignore              # Git除外ファイル設定
```

## 必要要件/開発環境
- **Python 3.7以上**
- **Google Chrome** (最新版推奨)
- **VSCode** (開発環境)
- **Git** (バージョン管理)

### 使用ライブラリ
- **selenium 4.33.0** - Webブラウザ自動化
- **webdriver-manager 4.0.2** - WebDriver自動管理

## 機能
- **自動ブラウザ制御** - Headlessモードでのバックグラウンド実行
- **書籍情報取得** - タイトル、価格、カテゴリの自動抽出
- **データ分析** - 価格統計、カテゴリ別分析
- **検索機能** - キーワードによる書籍検索
- **価格帯別分類** - 自動的な価格帯分類機能
- **インタラクティブ操作** - ユーザーとの対話型操作

## 実行方法

### 1. リポジトリのクローン
```bash
git clone https://github.com/yourusername/book_scraping_project.git
cd book_scraping_project
```

### 2. 仮想環境の作成・アクティベート
**Windows:**
```bash
python -m venv myenv
myenv\Scripts\activate
```

**macOS:**
```bash
python3 -m venv myenv
source myenv/bin/activate
```

### 3. 依存関係のインストール
```bash
pip install -r requirements.txt
```

### 4. プログラムの実行
```bash
python book_scraper.py
```

実行後、スクレイピング対象のURLを入力してください。
推奨テストサイト: https://books.toscrape.com/

## データ取得について
- **対象サイト** スクレイピング練習用の公式サイトを使用
- **取得データ** 書籍タイトル、価格、カテゴリ情報
- **処理方式** CSS セレクターによる正確な要素指定
- **出力形式** コンソール表示およびインタラクティブ検索

## 開発者
YuYu(自分の名前に変更)

プロジェクトのコミット・プッシュ

プロジェクトの全ての変更をGitHubに反映させましょう。

変更のステージングとコミット

  1. VSCodeの左側のアクティビティバーからソース管理アイコンをクリックします
  2. 変更内容セクションで、すべての変更ファイルを確認します
  3. 各ファイルの横にある「+」アイコンをクリックしてステージングします
  4. コミットメッセージ入力欄に「Add complete book scraping system」と入力します
  5. コミット」ボタンをクリックします

GitHubへのプッシュ

  1. コミット完了後、「変更の同期」または「プッシュ」ボタンをクリックします
  2. 初回の場合、GitHub認証が求められる場合があります
  3. 認証完了後、ローカルの変更がGitHubリポジトリに反映されます

GitHubでの確認

プロジェクトが正常にアップロードされたか確認しましょう。

  1. ブラウザでGitHubリポジトリページを開きます
  2. 以下のファイルが正しくアップロードされていることを確認します
  • book_scraper.py (メインプログラムファイル)
  • requirements.txt (依存関係管理ファイル)
  • README.md (更新されたプロジェクト説明書)
  • .gitignore (更新された除外設定ファイル)
  1. README.mdが適切に表示され、プロジェクトの説明が読みやすく表示されていることを確認します
  2. ファイル一覧にmyenv/フォルダや*.logファイルが含まれていないことを確認します(.gitignoreで除外されているため)

これで本格的な書籍情報スクレイピングシステムが完成し、GitHubでのポートフォリオ公開も完了しました。作成したプロジェクトは、就職活動や案件獲得時の実績として活用できる実用的なシステムとなっています。

まとめ

このプロジェクトを通じて、PythonとSeleniumを使用したWebスクレイピングの基礎から実践的な応用まで幅広く学習することができました。

得られたスキル

  • Selenium WebDriver – ブラウザ自動化の基本操作と高度な設定
  • Headlessモード実装 – バックグラウンドでのWebブラウザ制御技術
  • CSS セレクター技術 – 正確な要素指定とデータ抽出手法
  • Chrome WebDriver管理 – webdriver-managerを使用した自動化
  • データ構造設計 – 辞書とリストを活用した効率的なデータ管理
  • エラーハンドリング – try-except文を使った安定性の高いプログラム設計

得られた経験

  • 実際のWebサイトからのデータ取得経験
  • 大量データの処理と分析手法の習得
  • ユーザーインタラクションを含むプログラム開発
  • GitHubを使用したバージョン管理の実践的経験
  • 仮想環境での開発環境構築と管理

得られた成果物

  • 完全に動作するWebスクレイピングシステム
  • GitHubでのポートフォリオとして活用可能なプロジェクト
  • 再利用可能なコードベース
  • 詳細なドキュメント(README.md)
  • 適切な依存関係管理(requirements.txt)

次に学ぶべきこと

  • Beautiful Soupを使用したHTML解析技術の学習
  • pandasを使用したCSV出力機能の追加
  • 定期実行機能の実装(scheduleライブラリの活用)
  • データベースへの保存機能の実装
  • API連携によるデータ送信機能の開発

このプロジェクトから応用できること

  • ECサイトの価格監視ツールをSelenium WebDriverで開発し、クラウドワークスなどで副業案件として提供
  • 不動産情報収集システムをCSS セレクターによる要素取得技術で構築し、不動産会社の業務効率化に活用
  • 求人情報の自動収集をHeadlessモードで実装し、人材紹介会社のデータ収集業務を自動化
  • 競合他社の商品情報監視を定期実行機能で構築し、現職のマーケティング業務に活用
  • 在庫情報の自動更新システムをデータ分析機能で実装し、EC運営会社の在庫管理業務を効率化
  • データ収集代行サービスを統合システムとして提供し、中小企業のデータ収集ニーズに対応

このスクレイピング技術を基盤として、様々な業界での自動化ニーズに対応できるサービスを開発することが可能です。特に、定期的なデータ収集や監視が必要な業務では、高い価値を提供できる技術として活用できます。

この記事が気に入ったら
フォローしてね!

この記事が参考になった方はシェアしてね!
  • URLをコピーしました!

本コンテンツへの意見や質問

コメントする

目次