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

目次

プロジェクト概要

前提条件

  • Python基礎知識 – 変数、関数、クラスの基本的な理解
  • HTML基礎 – タグ、要素、属性の基本概念
  • プログラミング経験 – 簡単なPythonプログラムの作成経験
  • 開発環境 – VSCodeがインストール済み
  • インターネット環境 – 安定したネットワーク接続
  • 学習意欲 – 新しい技術への積極的な取り組み姿勢

開発環境

  • Python 3.7以上
  • Google Chrome(最新版)
  • Visual Studio Code
  • Git
  • GitHub アカウント

プロジェクト内容

このプロジェクトでは、PythonSeleniumを使用して実際のWebサイトから書籍情報を自動収集するスクレイピングシステムを構築します。現代のWebスクレイピング技術の中核となるSelenium WebDriverの操作方法を実践的に学び、Chrome WebDriverの設定からHeadlessモードでの実行まで、プロフェッショナルなスクレイピング環境を構築していきます。

プロジェクトでは、books.toscrape.comという練習用サイトを対象に、書籍のタイトル、価格、カテゴリ情報を自動取得します。単純なデータ取得だけでなく、取得したデータの統計分析価格帯別分類キーワード検索機能など、実用的なデータ処理システムまで実装します。

技術的には、XPathを使用した正確な要素指定、辞書構造でのデータ管理、try-except文による堅牢なエラーハンドリングを学びます。また、オブジェクト指向プログラミングの概念を活用したクラス設計により、保守性の高いコードを作成する手法も習得できます。

プロジェクトの後半では、完成したシステムをGitHubにアップロードし、ポートフォリオとして活用できる形に仕上げます。これにより、実際の開発現場で求められるバージョン管理の技術も同時に学習できます。

このプロジェクトを通じて、Webスクレイピングの基礎から実践的な応用まで幅広くカバーし、副業や案件獲得にも活用できる実用的なスキルを身につけることができます。完成したシステムは、ECサイトの価格監視、不動産情報収集、求人情報の自動取得など、様々な分野に応用可能な汎用性の高い技術基盤となります。

実装内容

  • 仮想環境の構築とライブラリ管理
  • Chrome WebDriverの設定と最適化
  • Headlessモードでのバックグラウンド実行
  • XPathを使用した要素の正確な取得
  • 書籍情報の自動抽出とデータ構造化
  • 統計分析機能(平均価格、価格帯分類)
  • キーワード検索とフィルタリング機能
  • インタラクティブなユーザーインターフェース
  • エラーハンドリングとプログラムの安定性向上
  • GitHubでのバージョン管理とポートフォリオ化

得られるスキル

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

得られる経験

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

得られる成果物

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

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」に設定します

このPythonPortfolioフォルダが、今後のプロジェクト全体を管理する親ディレクトリとなります。

VSCodeでプロジェクトを開く

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

  1. VSCodeを起動します
  2. 左側のアクティビティバーからソース管理アイコン(分岐のようなアイコン)をクリックします
  3. リポジトリの複製」ボタンをクリックします
  4. GitHubから複製」を選択します
  5. 先ほど作成した「book_scraping_project」リポジトリを選択します
  6. リポジトリ宛先として「PythonPortfolio」フォルダを選択します
  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. 同じくエクスプローラーパネルで「新しいファイル」アイコンをクリック
  2. ファイル名を「requirements.txt」として作成

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

requirements.txtの内容設定

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

selenium==4.33.0
webdriver-manager==4.0.2

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

  • selenium Webブラウザを自動化するためのツールです。JavaScriptが動的に生成するコンテンツも取得できる強力なスクレイピングライブラリです
  • webdriver-manager SeleniumでWebブラウザを制御するためのWebDriverを自動的にダウンロード・管理してくれる便利なツールです

プロジェクト構造の確認

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

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

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

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

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

pip install -r requirements.txt

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

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

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

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

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

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

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

pip install -r requirements.txt

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

インストール確認

ライブラリが正常にインストールされたかを確認しましょう。

pip list

このコマンドを実行すると、現在の仮想環境にインストールされているすべてのパッケージが表示されます。seleniumwebdriver-managerが一覧に含まれていることを確認してください。

Seleniumの詳細機能

Seleniumは、Webブラウザを自動操作するための最も強力なツールの一つです。

主な特徴

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

スクレイピングでの活用場面

  • 動的サイト対応 – JavaScriptでコンテンツが読み込まれるモダンなWebサイト
  • ページ遷移 – 複数ページにわたるデータ収集
  • ログイン必要サイト – 認証が必要なサイトへの自動ログイン

webdriver-managerの詳細機能

webdriver-managerは、Seleniumの面倒な初期設定を自動化してくれる便利なツールです。

従来の問題点

Seleniumを使用する際、従来は以下の手順が必要でした。

  1. 使用するブラウザのバージョン確認
  2. 対応するWebDriverを手動でダウンロード
  3. WebDriverのパスを環境変数に設定
  4. ブラウザ更新のたびにWebDriverも更新

webdriver-managerの解決策

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

動作確認テスト

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

# ライブラリのインポートテスト
try:
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from webdriver_manager.chrome import ChromeDriverManager
    print("✓ すべてのライブラリが正常にインポートされました")
except ImportError as e:
    print(f"✗ ライブラリのインポートエラー: {e}")

# WebDriverの動作確認
try:
    service = Service(ChromeDriverManager().install())
    print("✓ WebDriverManagerが正常に動作しています")
except Exception as e:
    print(f"✗ WebDriverManagerエラー: {e}")

テスト実行

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

python book_scraper.py

正常にインストールされていれば、「✓ すべてのライブラリが正常にインポートされました」と「✓ WebDriverManagerが正常に動作しています」の2つのメッセージが表示されます。

エラーが発生した場合の対処法

もしエラーが発生した場合は、以下を確認してください。

  1. 仮想環境の確認 – ターミナルに(myenv)が表示されているか
  2. Pythonバージョン – Python 3.7以上がインストールされているか
  3. ネットワーク接続 – インターネット接続が安定しているか

次のステップの準備

動作確認が完了したら、テストコードは削除してbook_scraper.pyを空のファイルに戻しておきましょう。次のStep3では、実際のスクレイピング機能の実装を開始します。

これで開発に必要なすべてのライブラリが準備完了しました。各ライブラリの役割を理解することで、これから実装するスクレイピングコードの動作原理もより深く理解できるようになります。

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

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

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

book_scraper.pyファイルを開き、必要なライブラリをインポートします。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
import time

各インポートの役割について説明します。

  • webdriver – Seleniumの中核機能でブラウザを制御
  • Service – WebDriverサービスの管理
  • Options – Chromeブラウザの詳細設定
  • By – 要素の検索方法を指定
  • ChromeDriverManager – ChromeDriverの自動管理
  • time – 処理の待機時間制御

BookScraperクラスの作成

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

class BookScraper:
    def __init__(self, headless=True):
        self.headless = headless
        self.driver = None
        self.setup_driver()

__init__メソッドはクラスの初期化を行う特別な関数です。ここでブラウザの表示設定を受け取り、WebDriverの初期設定を呼び出します。

Chrome WebDriverの設定機能

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

    def setup_driver(self):
        chrome_options = Options()
        
        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")
        chrome_options.add_argument("--disable-extensions")
        chrome_options.add_argument("--disable-plugins")
        
        service = Service(ChromeDriverManager().install())
        self.driver = webdriver.Chrome(service=service, options=chrome_options)
        self.driver.implicitly_wait(10)

このメソッドはクラス内で定義されているため、selfキーワードを使用してクラスの属性にアクセスできます。

Chrome設定オプションの詳細解説

各設定オプションの意味を説明します。

Headlessモードについて

Headlessモードは、ブラウザウィンドウを画面に表示せずにバックグラウンドで動作させる機能です。

利点

  • 処理速度向上 – グラフィカル表示が不要なため高速動作
  • サーバー環境対応 – GUI環境がないサーバーでも実行可能
  • リソース節約 – メモリとCPU使用量を削減

パフォーマンス最適化設定

chrome_options.add_argument("--disable-dev-shm-usage")

共有メモリの使用を無効化し、メモリ不足エラーを防止

chrome_options.add_argument("--no-sandbox")

サンドボックス機能を無効化してセキュリティ制限を緩和

chrome_options.add_argument("--disable-gpu")

GPU加速を無効化してHeadlessモードでの安定性を向上

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

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

def access_website(self, url):
        try:
            print(f"アクセス中: {url}")
            self.driver.get(url)
            
            time.sleep(3)
            
            page_title = self.driver.title
            print(f"ページタイトル: {page_title}")
            
            return True
            
        except Exception as e:
            print(f"アクセスエラー: {e}")
            return False

このaccess_websiteメソッドも、クラス内で定義されているためself.driverでWebDriverインスタンスにアクセスできます。クラス設計により、関連する機能がすべて一つのクラス内にまとまって管理されます。

ブラウザ終了処理の実装

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

def close_browser(self):
        if self.driver:
            self.driver.quit()
            print("ブラウザを終了しました")

メイン実行関数の作成

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

def main():
    target_url = "https://books.toscrape.com/"
    
    scraper = BookScraper(headless=True)
    
    try:
        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()

クラス設計のメリット

今回BookScraperクラスを作成し、その中に各機能をdef文で実装することで以下のメリットがあります。

  • コードの整理 – 関連する機能がクラス内にまとまる
  • 再利用性 – 同じクラスから複数のインスタンスを作成可能
  • 保守性 – 機能追加や修正が簡単
  • データ共有 – クラス内のメソッド間でself経由でデータ共有

実装コードの動作テスト

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

python book_scraper.py

正常に動作すれば以下のような出力が表示されます。

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

Headlessモードの動作確認

動作確認のため、ブラウザウィンドウを表示させるテストも行いましょう。

main関数の以下の行を変更

# 変更前
scraper = BookScraper(headless=True)

# 変更後(ブラウザウィンドウが表示される)
scraper = BookScraper(headless=False)

この状態で再実行すると、Chromeブラウザウィンドウが開いてWebサイトにアクセスする様子を確認できます。テスト完了後は、再びheadless=Trueに戻しておきましょう。

よくあるエラーと対処法

ChromeDriverのバージョンエラー

SessionNotCreatedException: Message: session not created

ChromeDriverManagerが自動的に最新版をダウンロードするまで少し時間がかかる場合があります。

ネットワークタイムアウトエラー

TimeoutException: Message: timeout

インターネット接続が不安定な場合に発生します。implicitly_waitの時間を延長するか、ネットワーク環境を確認してください。

次のステップに向けて

Step3完了時点で、以下の機能が実装されました。

  • BookScraperクラス – スクレイピング機能の統合管理
  • Chrome WebDriver設定 – 最適化されたブラウザ設定
  • Headlessモード – バックグラウンド実行機能
  • 基本アクセス機能 – Webサイトへの安全なアクセス

次のStep4では、この基盤を使って実際のデータ抽出ロジックを実装していきます。現在はWebサイトへのアクセスまでが完了し、本格的なスクレイピング処理の準備が整いました。

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

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

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

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

def get_book_menu(self, blink):
        current_book_data = {}
        
        for i in range(2, 20):
            try:
                category_name = self.driver.find_element(
                    By.XPATH, 
                    f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/h4"
                ).get_attribute("innerHTML")
                
                if category_name != "" and category_name != "Recommended":
                    items = []
                    
                    for j in range(1, 25):
                        try:
                            item_name = self.driver.find_element(
                                By.XPATH, 
                                f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/div[2]/div[{j}]/div/div/div[2]/div/div/h4"
                            ).get_attribute("innerHTML")
                            
                            try:
                                item_price = self.driver.find_element(
                                    By.XPATH, 
                                    f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/div[2]/div[{j}]/div/div/div[2]/div/div/div[2]/span"
                                ).get_attribute("innerHTML")
                            except:
                                item_price = self.driver.find_element(
                                    By.XPATH, 
                                    f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/div[2]/div[{j}]/div/div/div[2]/div/div/div/span"
                                ).get_attribute("innerHTML")
                            
                            item_price = item_price[1:]
                            listing = [item_name, item_price]
                            items.append(listing)
                            
                        except:
                            pass
                    
                    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:
                pass
        
        return current_book_data

このget_book_menuメソッドをBookScraperクラス内に追加します。このメソッドは二重ループ構造を使用して、カテゴリごとに書籍情報を取得し、辞書形式でデータを整理します。

XPathの詳細解説

実装したXPathの構造について詳しく説明します。

基本的なXPath構造

f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/h4"

このXPathは以下の階層構造を表しています。

  • html – HTML文書のルート要素
  • body – bodyタグ
  • div[1] – 最初のdivタグ
  • section[{i}] – i番目のsectionタグ(カテゴリー)
  • h4 – カテゴリ名を含むh4タグ

動的な要素指定

f"section[{i}]"

f文字列を使用してXPathにループ変数を埋め込むことで、複数のセクションを自動的に処理できます。

データ構造の構築プロセス

メソッド内でのデータ処理の流れを説明します。

カテゴリ情報の取得

category_name = self.driver.find_element(
    By.XPATH, 
    f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/h4"
).get_attribute("innerHTML")

各セクションのh4タグからカテゴリ名を取得し、空文字列や「Recommended」カテゴリを除外しています。

アイテム情報の取得

item_name = self.driver.find_element(
    By.XPATH, 
    f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/div[2]/div[{j}]/div/div/div[2]/div/div/h4"
).get_attribute("innerHTML")

各カテゴリ内のアイテム名を順次取得します。

価格情報の複数パターン対応

try:
    item_price = self.driver.find_element(
        By.XPATH, 
        f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/div[2]/div[{j}]/div/div/div[2]/div/div/div[2]/span"
    ).get_attribute("innerHTML")
except:
    item_price = self.driver.find_element(
        By.XPATH, 
        f"/html/body/div[1]/div/main/div/section[4]/section/section[2]/section[{i}]/div[2]/div[{j}]/div/div/div[2]/div/div/div/span"
    ).get_attribute("innerHTML")

価格情報は複数の場所に配置される可能性があるため、try-except文を使用して異なるXPathパターンを試行します。

データの整理と辞書構造

取得したデータを辞書形式で整理する処理について説明します。

リストからの変換

listing = [item_name, item_price]
items.append(listing)

各アイテムの名前と価格をリストとして格納し、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

カテゴリ名をキーとして、各カテゴリの辞書データを格納することで、二階層の辞書構造を作成します。

メイン実行部分の更新

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

def main():
    print("書籍スクレイピングを開始します")
    blink = str(input("対象サイトのURL: "))
    
    scraper = BookScraper(headless=True)
    
    try:
        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()

エラーハンドリングの活用

コード内で使用されているエラーハンドリングの意図を説明します。

要素が見つからない場合の対応

except:
    pass

XPathで指定した要素が存在しない場合、例外が発生しますが、passを使用して処理を継続します。これにより、一部のデータが取得できなくても全体の処理が停止しません。

価格取得の複数試行

複数のXPathパターンを試すことで、サイトの構造変更に対する耐性を持たせています。

動作テスト実行

VSCodeのターミナルでプログラムを実行してテストします。

python book_scraper.py

実行すると、URLの入力を求められるので、テスト用サイトのURLを入力します。正常に動作すれば、以下のような出力が表示されます。

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

## Fiction
A Light in the Attic  : ¥ 51.77
Tipping the Velvet  : ¥ 53.74
...

XPath作成のポイント

効果的なXPathを作成するためのポイントを説明します。

絶対パスの使用

今回は絶対パス(/html/body…)を使用していますが、これによりHTML構造の変更に敏感になります。しかし、特定のサイト専用のスクレイピングでは、正確性を重視して絶対パスを使用することが多くあります。

インデックスの活用

section[{i}]のようにインデックスを使用することで、同じ要素の複数インスタンスを効率的に処理できます。

次のステップに向けて

Step4完了時点で、以下の機能が追加されました。

  • XPathによる要素取得 – 正確な要素指定技術
  • 二重ループ処理 – カテゴリとアイテムの階層処理
  • 辞書構造データ – 整理されたデータ管理
  • 複数パターン対応 – 異なる構造への柔軟な対応

次のStep5では、取得したデータの処理と表示機能をさらに拡張し、より使いやすい形でデータを整理・出力する機能を実装していきます。

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

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

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

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

def analyze_data(self, 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}")

このanalyze_dataメソッドは、取得したデータの統計情報を計算し、データの概要を把握するのに役立ちます。

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

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

def display_category_details(self, 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}")
                    
                    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):
        price_ranges = {
            "低価格帯(¥0-1000)": [],
            "中価格帯(¥1000-3000)": [],
            "高価格帯(¥3000以上)": []
        }
        
        for category in book_data:
            items = book_data[category]
            for item_name, price in items.items():
                try:
                    price_value = float(price)
                    
                    if price_value < 1000:
                        price_ranges["低価格帯(¥0-1000)"].append((item_name, price_value, category))
                    elif price_value < 3000:
                        price_ranges["中価格帯(¥1000-3000)"].append((item_name, price_value, category))
                    else:
                        price_ranges["高価格帯(¥3000以上)"].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:
                sorted_items = sorted(items, key=lambda x: x[1], reverse=True)
                for item_name, price, category in sorted_items[:5]:
                    print(f"  • {item_name} (¥{price}) - {category}")
                
                if len(items) > 5:
                    print(f"  ... その他{len(items) - 5}点")

検索機能の実装

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

def search_items(self, 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("該当する商品が見つかりませんでした")

拡張表示機能の実装

従来の表示機能を拡張し、より見やすい形で情報を表示します。

def display_enhanced_results(self, book_data):
        print("\n" + "="*80)
        print("取得データ一覧")
        print("="*80)
        
        for category in book_data:
            category_name = category
            print(f"\n【カテゴリ: {category_name}】")
            print("-" * 50)
            
            items = book_data.get(category)
            item_list = list(items.items())
            
            sorted_items = sorted(item_list, key=lambda x: float(x[1]))
            
            for i, (item_name, price) in enumerate(sorted_items, 1):
                print(f"{i:2d}. {item_name:<40} ¥{price:>8}")
            
            print(f"\n小計: {len(items)}点")
            print("=" * 60)

メイン実行部分の拡張

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

def main():
    print("書籍スクレイピングシステム")
    print("=" * 30)
    
    blink = str(input("対象サイトのURL: "))
    
    scraper = BookScraper(headless=True)
    
    try:
        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)
                scraper.display_enhanced_results(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()

データ処理技術の詳細解説

実装した各機能で使用されている技術について説明します。

辞書のイテレーション

for item_name, price in items.items():

辞書の.items()メソッドを使用して、キーと値のペアを同時に取得できます。

リスト内包表記とソート

sorted_items = sorted(items.items(), key=lambda x: float(x[1]))

sorted()関数とlambda式を組み合わせて、価格でソートした商品リストを作成します。

例外処理による堅牢性

try:
    price_value = float(price)
except ValueError:
    continue

価格データの変換に失敗した場合でも、処理を継続できるようにしています。

インタラクティブ機能の追加

ユーザーが対話的にシステムを操作できる機能を実装しました。

検索ループ

while True:
    choice = input().lower()
    if choice == 'y':
        # 検索処理
    elif choice == 'n':
        break

この構造により、ユーザーが複数回検索を行えるようになります。

動作テスト実行

VSCodeのターミナルでプログラムを実行してテストします。

python book_scraper.py

実行すると、以下のような拡張された出力が表示されます。

書籍スクレイピングシステム
==============================
対象サイトのURL: https://books.toscrape.com/
アクセス中: https://books.toscrape.com/
ページタイトル: All products | Books to Scrape - Sandbox
✓ Webサイトへのアクセスが成功しました

カテゴリ数: 5
総商品数: 48
平均価格: ¥1250.50
最高価格: ¥5880
最低価格: ¥320

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

【Fiction】
商品数: 12点
価格帯: ¥320 ~ ¥2400
平均価格: ¥1205.67
高額商品TOP3:
  1. Premium Book - ¥2400
  2. Classic Novel - ¥1850
  3. Modern Story - ¥1650

次のステップに向けて

Step5完了時点で、以下の高度な機能が追加されました。

  • データ分析機能 – 統計情報の自動計算
  • カテゴリ別詳細表示 – 各カテゴリの詳細分析
  • 価格帯別分類 – 自動的な価格帯分類
  • 検索機能 – キーワードによる商品検索
  • インタラクティブ操作 – ユーザーとの対話機能

次のStep6では、完成したプロジェクトをGitHubにアップロードし、他の開発者と共有できる形にして、ポートフォリオとして活用できるようにします。

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

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

.gitignoreファイルの更新

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

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

# Virtual environment
myenv/

# Scraped data files
*.log

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

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

README.mdファイルの作成

既存の「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/

## 開発者
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を使用したWebスクレイピングの基礎から実践的な応用まで幅広く学習することができました。

得られたスキル

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

得られた経験

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

得られた成果物

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

次に学ぶべきこと

  • Beautiful Soupを使用したHTML解析技術の学習
  • pandasを使用したデータ分析とCSV出力機能の追加
  • スケジューリングによる定期実行機能の実装
  • APIとの連携によるデータ送信機能の開発
  • データベースへの保存機能の実装

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

  • ECサイトの価格監視ツールの開発による副業案件
  • 不動産情報収集システムの構築
  • 求人情報の自動収集サービスの提供
  • 在庫管理システムへの応用
  • 競合他社の情報収集ツールとしての活用
  • データ収集代行サービスの事業化

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

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

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

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

コメントする

目次