【Selenium】複数のページのスクレイピング方法
はじめに
Webスクレイピングを行っていると、「一覧ページから詳細ページに入って情報を取得し、また一覧に戻る」という動作を繰り返し、複数ページの情報を取得したい場面があると思います。
今回は、Pythonのブラウザ自動操作ライブラリSeleniumを使って、架空の書店サイト(Books to Scrape)から、複数ページに渡って本の詳細情報(タイトル、価格、在庫状況)を収集するスクリプトを作成しました。
Seleniumとは?
SeleniumとはサイトのHTMLのタグ構造を読み取り、正確に情報をスクレイピングできるライブラリです。
CSSセレクタの役割
CSSセレクタ:By.CSS_SELECTORはSeleniumに対してこれからどんな文法で要素を探すかを指定する役割を持っています。
例えば以下のようなHTMLからA Light in the Atticを取り出したい時は
<div class="product_main">
<h1>A Light in the Attic</h1>
<p class="price_color">£51.77</p>
...
</div>title = driver.find_element(By.CSS_SELECTOR, "div.product_main h1").textのようにして取り出します。
このコードの意味は
要素を探してきてください(find_element)。 探し方のルールはCSSセレクタを使います(By.CSS_SELECTOR)。 具体的な条件はproduct_mainクラスを持つdivの中のh1です("div.product_main h1")。 見つかったら、その中身のテキストを取り出してください(.text)
事前準備
必要なライブラリをインストールします。
pip install selenium webdriver-manager pandasselenium: ブラウザ操作の本体webdriver-manager: 面倒なChromeDriverのバージョン管理を自動化pandas: データの整理・加工
対象サイトの構成確認
対象サイトを左クリックして検証をクリックするとそのサイトのHTMLが表示されるので取得したい情報のタグ構造、class名をあらかじめ整理します。
本の名前(Title)
- HTML構造:
<div class="product_main">の中にある<h1>タグ - Selenium指定:
div.product_main h1
<div class="product_main">
<h1>A Light in the Attic</h1>
<p class="price_color">£51.77</p>
...
</div>価格(Price)
- HTML構造:
<p>タグで、クラス名にprice_color - Selenium指定:
p.price_color
<div class="product_main">
...
<p class="price_color">£51.77</p>
<p class="instock availability">
<i class="icon-ok"></i>
In stock (22 available)
</p>
...
</div>在庫状況(Availability)
- HTML構造:
<p>タグで、クラス名にinstockとavailabilityが付いているもの - Selenium指定:
p.instock.availability
<div class="product_main">
...
<p class="price_color">£51.77</p>
<p class="instock availability">
<i class="icon-ok"></i>
In stock (22 available)
</p>
...
</div>
ページ移動テクニック:URL直打ちとブラウザバック
Webスクレイピングでは、目的のページにどうやってたどり着くかが重要です。今回のコードでは、効率よくデータを集めるために2つの移動方法を使い分けています。
1. ページネーション(規則的なURLへの移動)
1ページ目、2ページ目…と進む処理では、「次へ」ボタンをクリックするのではなく、URLを直接書き換えて移動しています。
多くのWebサイトでは、ページが変わるとURLの一部が規則的に変化します。
- 1ページ目:
.../catalogue/page-1.html - 2ページ目:
.../catalogue/page-2.html
この規則性を利用し、Pythonの「f文字列(フォーマット済み文字列)」を使ってURLを生成し、driver.get() で直接ジャンプしています。
for page_num in range(1, 6):
# 数字部分(page_num)だけを変えてURLを作る
url = f"<https://books.toscrape.com/catalogue/page-{page_num}.html>"
# そのURLへ移動する
driver.get(url)この方法は、「次へ」ボタンを探してクリックするよりも処理が確実で、高速に巡回できるテクニックです。
2. 詳細ページへの往復(クリックとブラウザバック)
一方で、本の一覧から詳細ページへ移動する際は、URLが予測できない(本のタイトルごとに異なる)ため、リンクのクリックを使います。
そして、データを取得し終わったら、元の場所に戻るために driver.back() を使います。
# 詳細ページへ移動(クリック)
target_book.click()
# --- ここでデータを取得 ---
# 一覧ページへ戻る(ブラウザの「戻る」ボタンと同じ)
driver.back()driver.back() は、私たちが普段ブラウザの左上にある「←(戻る)」ボタンを押すのと同じ動作をします。 これにより、再び一覧ページに戻り、次の本をクリックする準備が整います。
移動後の「待ち時間」が大切
ページを移動(get や back)した直後は、ブラウザが新しいページを読み込む処理を行っています。 読み込みが終わる前に次の命令を出すとエラーになるため、今回は time.sleep(1) を入れて、一呼吸置いてから次の処理に進むようにしています。
まとめ
今回はSeleniumを使った複数のページのスクレイピングについて解説しました。
- ページネーション処理: URLの規則性を使って
forループで回す。 - 詳細ページへの往復:
click()して情報を取ってback()する。
このパターンを覚えておけば、ECサイトの商品情報収集や、ブログ記事の収集など、様々な場面に応用が利きます。ぜひ試してみてください!
全体コード
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
import pandas as pd
import time
# ブラウザ起動
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.implicitly_wait(10)
data = []
# --- 外側のループ:ページ遷移(1ページ目〜5ページ目) ---
for page_num in range(1, 6):
# URLを生成してアクセス
url = f"https://books.toscrape.com/catalogue/page-{page_num}.html"
print(f"★ {page_num}ページ目にアクセス中: {url}")
driver.get(url)
time.sleep(1) # ページ読み込み待ち
# そのページにある本の件数を確認(ループ回数を決めるため)
# CSSセレクタ: article.product_pod の中の h3 の中の aタグ
links = driver.find_elements(By.CSS_SELECTOR, "article.product_pod h3 a")
book_count = len(links)
print(f" -> このページには {book_count} 冊の本があります")
# --- 内側のループ:本をクリックして詳細取得して戻る ---
for i in range(book_count):
try:
# 【重要】ページを戻ると要素が古くなるため、毎回最新のリンク一覧を取得し直す
current_links = driver.find_elements(By.CSS_SELECTOR, "article.product_pod h3 a")
# ターゲットの本をクリック
target_book = current_links[i]
target_book.click()
# --- 詳細ページでの処理 ---
time.sleep(1) # 詳細ページの読み込み待ち
# 1. タイトル (h1タグ)
title = driver.find_element(By.CSS_SELECTOR, "div.product_main h1").text
# 2. Price
price = driver.find_element(By.CSS_SELECTOR, "p.price_color").text
# 3. Availability
availability = driver.find_element(By.CSS_SELECTOR, "p.instock.availability").text.strip()
# リストに保存
data.append({
"Page": page_num, # 何ページ目のデータか記録しておくと便利
"Title": title,
"Price(incl. tax)": price,
"Availability": availability
})
# --- 一覧ページに戻る ---
driver.back()
# 戻った後の読み込み待ち(これがないと次の要素取得でエラーになりやすい)
time.sleep(1)
except Exception as e:
print(f" エラー発生: {e}")
# エラーが起きても止まらずに一覧に戻って次へ進む
driver.back()
time.sleep(1)
# ブラウザ終了
driver.quit()
# --- データ整形と表示 ---
df = pd.DataFrame(data)
# 通貨記号(£)を削除して数値化
df['Price(incl. tax)'] = df['Price(incl. tax)'].str.replace('£', '').astype(float)
print("\n" + "="*50)
print(f"スクレイピング完了! 合計 {len(df)} 件取得しました。")