Webスクレイピングは、Webサイトから必要な情報を自動的に収集する技術です。競合分析、価格調査、データ収集など、業務のさまざまな場面で活用されています。Pythonにはスクレイピングを効率的に行うためのライブラリが豊富に揃っており、比較的少ないコードで強力なデータ収集が実現できます。
本記事では、Pythonの代表的なスクレイピングライブラリであるBeautifulSoupとSeleniumの使い方を、実践的なコード例とともに解説します。法的・倫理的な注意点にも触れ、安全にスクレイピングを行うための知識をお伝えします。
Webスクレイピングの基礎知識
スクレイピングの技術的な手法に入る前に、基本的な概念と注意事項を理解しておきましょう。
スクレイピングの仕組み
Webスクレイピングは、以下のステップで行われます。
- HTTPリクエスト:対象のWebページにアクセスし、HTMLを取得する
- HTML解析:取得したHTMLを解析し、必要なデータの位置を特定する
- データ抽出:特定した位置からテキストやリンクなどの情報を取り出す
- データ保存:抽出したデータをCSVやデータベースに保存する
法的・倫理的な注意点
スクレイピングを行う前に、以下の点を必ず確認してください。
- robots.txt:対象サイトの
/robots.txtを確認し、クロールが許可されているかを確認する - 利用規約:サイトの利用規約でスクレイピングが禁止されていないか確認する
- アクセス頻度:サーバーに過度な負荷をかけないよう、リクエスト間隔を適切に設定する
- 個人情報:個人情報に該当するデータの収集は、個人情報保護法に抵触する可能性がある
- 著作権:収集したコンテンツの利用は著作権法の範囲内で行う
# robots.txtの確認方法
import urllib.robotparser
rp = urllib.robotparser.RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()
# 特定のURLへのアクセスが許可されているか確認
can_fetch = rp.can_fetch("*", "https://example.com/target-page")
print(f"アクセス許可: {can_fetch}")
環境構築とライブラリの選び方
必要なライブラリのインストール
# 基本的なスクレイピング用
pip install requests beautifulsoup4 lxml
# 動的サイト向け(Selenium)
pip install selenium
# データ保存・分析用
pip install pandas
ライブラリの使い分け
| ライブラリ | 特徴 | 適したケース |
|---|---|---|
| requests + BeautifulSoup | 軽量・高速。静的HTMLの解析に最適 | HTMLが直接返されるサイト |
| Selenium | ブラウザを自動操作。JavaScriptの実行が可能 | SPAやJSで描画されるサイト |
| Scrapy | 大規模クロール向けフレームワーク | 数千〜数万ページの収集 |
| Playwright | モダンなブラウザ自動化ツール | Seleniumの代替として |
BeautifulSoupによるスクレイピング
BeautifulSoupは、HTMLやXMLを解析するためのライブラリです。requestsと組み合わせることで、シンプルかつ高速なスクレイピングが可能です。
基本的な使い方
import requests
from bs4 import BeautifulSoup
# Webページの取得
url = "https://example.com"
response = requests.get(url)
response.encoding = response.apparent_encoding # 文字化け対策
# HTMLの解析
soup = BeautifulSoup(response.text, "lxml")
# ページタイトルの取得
print(soup.title.text)
# 特定のタグをすべて取得
links = soup.find_all("a")
for link in links:
href = link.get("href")
text = link.text.strip()
print(f"{text}: {href}")
要素の検索方法
# タグ名で検索
h2_tags = soup.find_all("h2")
# classで検索
items = soup.find_all("div", class_="item-card")
# idで検索
header = soup.find(id="main-header")
# CSSセレクタで検索
prices = soup.select("div.product > span.price")
nav_links = soup.select("nav ul li a")
# 属性で検索
images = soup.find_all("img", attrs={"data-src": True})
実践:ニュースサイトの記事一覧を取得
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
def scrape_news(url):
"""ニュースサイトの記事一覧をスクレイピングする"""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "lxml")
articles = []
for article in soup.select("article.news-item"):
title = article.select_one("h2 a")
date = article.select_one("time")
summary = article.select_one("p.summary")
if title:
articles.append({
"タイトル": title.text.strip(),
"URL": title.get("href", ""),
"日付": date.text.strip() if date else "",
"概要": summary.text.strip() if summary else "",
})
return articles
# 実行
articles = scrape_news("https://example.com/news")
df = pd.DataFrame(articles)
df.to_csv("news_articles.csv", index=False, encoding="utf-8-sig")
print(f"{len(articles)}件の記事を取得しました。")
複数ページのスクレイピング(ページネーション対応)
import time
def scrape_all_pages(base_url, max_pages=10):
"""複数ページをスクレイピングする"""
all_articles = []
for page in range(1, max_pages + 1):
url = f"{base_url}?page={page}"
print(f"ページ {page} を取得中...")
try:
articles = scrape_news(url)
if not articles:
print("記事が見つからないため終了します。")
break
all_articles.extend(articles)
except requests.exceptions.RequestException as e:
print(f"エラーが発生しました: {e}")
break
# サーバーへの負荷を軽減するため待機
time.sleep(2)
return all_articles
results = scrape_all_pages("https://example.com/news", max_pages=5)
print(f"合計 {len(results)} 件の記事を取得しました。")
Seleniumによる動的サイトのスクレイピング
JavaScriptで動的に描画されるサイト(SPA: Single Page Application等)では、requestsでHTMLを取得してもデータが含まれていない場合があります。Seleniumを使えば、実際のブラウザを操作してJavaScriptが実行された後のHTMLを取得できます。
Seleniumの基本セットアップ
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 selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Chrome オプションの設定
options = Options()
options.add_argument("--headless") # ヘッドレスモード(画面非表示)
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
# ドライバーの起動
driver = webdriver.Chrome(options=options)
try:
# ページにアクセス
driver.get("https://example.com")
# ページタイトルの取得
print(driver.title)
# 要素の取得
elements = driver.find_elements(By.CSS_SELECTOR, "div.item")
for elem in elements:
print(elem.text)
finally:
driver.quit()
要素の待機と操作
# 要素が表示されるまで待機(最大10秒)
wait = WebDriverWait(driver, 10)
element = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "div.loaded-content"))
)
# クリック操作
button = driver.find_element(By.CSS_SELECTOR, "button.load-more")
button.click()
# テキスト入力
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("Python スクレイピング")
search_box.submit()
# スクロール操作(無限スクロール対応)
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(2) # スクロール後のコンテンツ読み込みを待つ
実践:動的サイトからデータを収集
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import pandas as pd
import time
def scrape_dynamic_site(url):
"""JavaScript描画のサイトからデータを収集する"""
options = Options()
options.add_argument("--headless")
driver = webdriver.Chrome(options=options)
results = []
try:
driver.get(url)
# コンテンツの読み込みを待機
wait = WebDriverWait(driver, 15)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".product-list")))
# 「もっと見る」ボタンを繰り返しクリック
for _ in range(5):
try:
load_more = driver.find_element(By.CSS_SELECTOR, "button.load-more")
load_more.click()
time.sleep(2)
except Exception:
break
# データの抽出
products = driver.find_elements(By.CSS_SELECTOR, ".product-card")
for product in products:
name = product.find_element(By.CSS_SELECTOR, ".product-name").text
price = product.find_element(By.CSS_SELECTOR, ".product-price").text
results.append({"商品名": name, "価格": price})
finally:
driver.quit()
return results
data = scrape_dynamic_site("https://example.com/products")
df = pd.DataFrame(data)
print(df)
エラーハンドリングと安定運用
実運用のスクレイピングでは、エラーハンドリングとリトライ処理が不可欠です。
リトライ処理の実装
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session():
"""リトライ機能付きのHTTPセッションを作成する"""
session = requests.Session()
retries = Retry(
total=3, # 最大リトライ回数
backoff_factor=1, # リトライ間隔(1秒、2秒、4秒と指数的に増加)
status_forcelist=[500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retries)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session
session = create_session()
response = session.get("https://example.com", timeout=10)
エラーハンドリングのパターン
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
def safe_scrape(url):
"""安全にスクレイピングを行う関数"""
try:
response = session.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "lxml")
# データ抽出処理
return {"status": "success", "data": soup}
except requests.exceptions.Timeout:
logger.warning(f"タイムアウト: {url}")
return {"status": "timeout", "data": None}
except requests.exceptions.HTTPError as e:
logger.error(f"HTTPエラー: {e}")
return {"status": "http_error", "data": None}
except requests.exceptions.ConnectionError:
logger.error(f"接続エラー: {url}")
return {"status": "connection_error", "data": None}
except Exception as e:
logger.error(f"予期しないエラー: {e}")
return {"status": "error", "data": None}
スクレイピングのベストプラクティス
安定的かつ倫理的にスクレイピングを運用するためのベストプラクティスを紹介します。
アクセスマナーの遵守
- 適切なUser-Agentを設定:ボットであることを明示し、連絡先を含める
- リクエスト間隔を設ける:最低でも1〜2秒の間隔を設定する
- robots.txtを尊重する:アクセスが禁止されているページにはリクエストしない
- APIが提供されている場合はAPIを使う:スクレイピングよりもAPIの利用を優先する
コードの品質
- 取得したデータを随時保存し、途中で停止しても再開できるようにする
- ログを出力し、実行状況を追跡可能にする
- サイトの構造変更に備え、セレクタは定数として管理する
# セレクタを定数として管理する例
SELECTORS = {
"article": "article.news-item",
"title": "h2 a",
"date": "time.published",
"content": "div.article-body",
}
# 構造変更時はここを修正するだけで済む
articles = soup.select(SELECTORS["article"])
BeautifulSoup vs Selenium の判断基準
| 判断基準 | BeautifulSoup | Selenium |
|---|---|---|
| 実行速度 | 高速 | 低速 |
| JavaScript実行 | 不可 | 可能 |
| リソース消費 | 少ない | 多い(ブラウザ起動) |
| ログイン操作 | Cookie手動設定 | ブラウザ操作で可能 |
| 大量ページ収集 | 向いている | 負荷が高い |
まずはBeautifulSoupで試し、JavaScriptの実行が必要な場合にのみSeleniumを使う、という判断が効率的です。
まとめ
本記事では、PythonでのWebスクレイピングの基本から実践的な手法までを解説しました。
- スクレイピングはHTTPリクエスト、HTML解析、データ抽出の3ステップで行われる
- 静的サイトにはrequests + BeautifulSoup、動的サイトにはSeleniumが適している
- robots.txtや利用規約の確認、適切なアクセス間隔の設定は必須
- リトライ処理やエラーハンドリングを実装し、安定した運用を目指す
- APIが提供されている場合は、スクレイピングよりもAPIの利用を優先する
スクレイピングは強力なデータ収集手段ですが、対象サイトへの配慮と法的な確認を怠らないことが大切です。適切なマナーを守りながら活用し、業務のデータ収集を効率化しましょう。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像