PythonでのWebスクレイピング:Requests、BeautifulSoup、Seleniumを使った完全ガイド
Updated on
APIでは取得できないデータが必要です——競合サイトの商品価格、学術ポータルの研究論文、求人プラットフォームの求人情報、複数ソースのニュース記事。手動でのデータコピーは遅く、エラーが起きやすく、大規模では不可能です。Webスクレイピングはこのプロセスを自動化しますが、間違ったツールやアプローチを選ぶとリクエストのブロック、パーサーの破損、法的な問題が発生します。
このガイドでは、PythonのWebスクレイピングスタック全体をカバーします:静態ページ用のrequests + BeautifulSoup、JavaScript描画コンテンツ用のSelenium、大規模クロール用のScrapy。実際の課題に対処する実践的なテクニックを学びます。
クイックスタート:requests + BeautifulSoup
静的HTMLページのスクレイピングに最も一般的な組み合わせ:
import requests
from bs4 import BeautifulSoup
# ページを取得
url = 'https://books.toscrape.com/'
response = requests.get(url)
response.raise_for_status() # エラーステータスコードで例外を発生
# HTMLを解析
soup = BeautifulSoup(response.text, 'html.parser')
# 書名を抽出
books = soup.select('article.product_pod h3 a')
for book in books[:5]:
print(book['title'])インストール
# 必要なパッケージをインストール
# pip install requests beautifulsoup4 lxmlBeautifulSoupでのHTML解析
要素の検索
from bs4 import BeautifulSoup
html = """
<div class="products">
<div class="product" id="p1">
<h2 class="name">Widget A</h2>
<span class="price">$9.99</span>
<p class="desc">A useful widget</p>
</div>
<div class="product" id="p2">
<h2 class="name">Widget B</h2>
<span class="price">$14.99</span>
<p class="desc">A better widget</p>
</div>
</div>
"""
soup = BeautifulSoup(html, 'html.parser')
# タグで検索
print(soup.find('h2').text) # "Widget A"
# クラスで検索
prices = soup.find_all('span', class_='price')
for p in prices:
print(p.text) # "$9.99", "$14.99"
# IDで検索
product = soup.find(id='p2')
print(product.find('h2').text) # "Widget B"
# CSSセレクタ
names = soup.select('.product .name')
for n in names:
print(n.text)よく使うセレクタ
| メソッド | 例 | 検索対象 |
|---|---|---|
find('tag') | soup.find('h2') | 最初のh2要素 |
find_all('tag') | soup.find_all('a') | すべてのアンカー要素 |
find(class_='x') | soup.find(class_='price') | そのクラスを持つ最初の要素 |
find(id='x') | soup.find(id='main') | そのIDを持つ要素 |
select('css') | soup.select('div.product > h2') | CSSセレクタのマッチ |
select_one('css') | soup.select_one('#header') | 最初のCSSマッチ |
データの抽出
from bs4 import BeautifulSoup
html = '<a href="/page/2" class="next" data-page="2">Next Page</a>'
soup = BeautifulSoup(html, 'html.parser')
link = soup.find('a')
# テキストコンテンツ
print(link.text) # "Next Page"
print(link.get_text(strip=True)) # "Next Page"(空白除去)
# 属性
print(link['href']) # "/page/2"
print(link.get('class')) # ['next']
print(link['data-page']) # "2"ヘッダーとセッションの処理
多くのウェブサイトは適切なヘッダーのないリクエストをブロックします:
import requests
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept-Language': 'en-US,en;q=0.9',
}
# セッションを使用してcookieと永続的なヘッダーを管理
session = requests.Session()
session.headers.update(headers)
response = session.get('https://books.toscrape.com/')
soup = BeautifulSoup(response.text, 'html.parser')
print(f"{len(soup.select('article.product_pod'))}冊の本を発見")ページネーション
次のページリンクをたどる
import requests
from bs4 import BeautifulSoup
base_url = 'https://books.toscrape.com/catalogue/'
all_books = []
url = 'https://books.toscrape.com/catalogue/page-1.html'
while url:
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# 現在のページから書籍を抽出
for book in soup.select('article.product_pod'):
title = book.select_one('h3 a')['title']
price = book.select_one('.price_color').text
all_books.append({'title': title, 'price': price})
# 次のページを探す
next_btn = soup.select_one('li.next a')
if next_btn:
url = base_url + next_btn['href']
else:
url = None
print(f"これまでに{len(all_books)}冊スクレイピング...")
print(f"合計:{len(all_books)}冊")SeleniumでJavaScriptページをスクレイピング
ページがJavaScriptでコンテンツを描画する場合、requestsは描画前の生のHTMLしか取得できません。Seleniumを使って実際のブラウザを制御します:
from selenium import webdriver
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 = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
try:
driver.get('https://quotes.toscrape.com/js/')
# コンテンツの読み込みを待機
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, 'quote'))
)
quotes = driver.find_elements(By.CLASS_NAME, 'quote')
for quote in quotes:
text = quote.find_element(By.CLASS_NAME, 'text').text
author = quote.find_element(By.CLASS_NAME, 'author').text
print(f'"{text}" -- {author}')
finally:
driver.quit()ツール比較
| ツール | 最適な用途 | 速度 | JavaScript | 学習曲線 |
|---|---|---|---|---|
requests + BeautifulSoup | 静的HTMLページ | 高速 | 非対応 | 簡単 |
Selenium | JavaScript描画ページ | 低速 | 対応 | 中程度 |
Scrapy | 大規模クロール | 高速 | プラグインで対応 | 急勾配 |
Playwright | モダンなJSアプリ | 中程度 | 対応 | 中程度 |
httpx + selectolax | 高性能パーシング | 非常に高速 | 非対応 | 簡単 |
Scrapyによる大規模スクレイピング
数千ページのクロールには、Scrapyが内蔵の並行処理、リトライロジック、データパイプラインを提供します:
# spider.py
import scrapy
class BookSpider(scrapy.Spider):
name = 'books'
start_urls = ['https://books.toscrape.com/']
def parse(self, response):
for book in response.css('article.product_pod'):
yield {
'title': book.css('h3 a::attr(title)').get(),
'price': book.css('.price_color::text').get(),
}
# ページネーションをたどる
next_page = response.css('li.next a::attr(href)').get()
if next_page:
yield response.follow(next_page, self.parse)
# 実行コマンド:scrapy runspider spider.py -o books.jsonデータのクリーニングと保存
import requests
from bs4 import BeautifulSoup
import pandas as pd
# データをスクレイピング
url = 'https://books.toscrape.com/'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
books = []
for item in soup.select('article.product_pod'):
title = item.select_one('h3 a')['title']
price_text = item.select_one('.price_color').text
price = float(price_text.replace('£', ''))
rating_class = item.select_one('p.star-rating')['class'][1]
books.append({
'title': title,
'price': price,
'rating': rating_class,
})
# DataFrameに変換
df = pd.DataFrame(books)
print(df.head())
# CSVに保存
df.to_csv('books.csv', index=False)倫理的なスクレイピングの実践
| 実践 | 説明 |
|---|---|
| robots.txtを確認 | サイトのクロールルールを尊重する |
| 遅延を追加 | リクエスト間にtime.sleep()を使用(1〜3秒) |
| 身元を明示 | 説明的なUser-Agent文字列を設定する |
| レスポンスをキャッシュ | 既に取得済みのページを再スクレイピングしない |
| APIを確認 | 多くのサイトにはより高速で許可された公開APIがある |
| 利用規約を読む | 一部のサイトはスクレイピングを明示的に禁止している |
| レート制限 | 同時リクエストでサーバーを圧倒しない |
import time
import requests
def polite_scrape(urls, delay=2):
"""遅延付きの礼儀正しいスクレイピング。"""
results = []
for url in urls:
response = requests.get(url)
results.append(response.text)
time.sleep(delay) # 礼儀正しく
return resultsスクレイピングしたデータの分析
Webスクレイピングでデータを収集した後、PyGWalker (opens in a new tab)を使えばJupyterでスクレイピングしたデータセットをインタラクティブに探索・可視化できます:
import pandas as pd
import pygwalker as pyg
df = pd.read_csv('scraped_data.csv')
walker = pyg.walk(df)JupyterでAIの支援を受けながらスクレイピングスクリプトを繰り返し実行するには、RunCell (opens in a new tab)がAI搭載の環境を提供し、セレクタのデバッグやパーシングロジックのテストを対話的に行えます。
よくある質問
Pythonでのウェブスクレイピングに最適なライブラリは?
静的ページにはrequests + BeautifulSoupが最もシンプルで人気のある選択肢です。JavaScript描画ページにはSeleniumまたはPlaywrightを使用します。数千ページの大規模クロールには、Scrapyが内蔵の並行処理とパイプライン管理を提供します。
JavaScriptを使用するウェブサイトをスクレイピングするには?
SeleniumまたはPlaywrightを使ってヘッドレスブラウザを制御しJavaScriptを実行します。または、ブラウザのNetworkタブでJSONデータを返すAPIエンドポイントを確認してください——APIを直接スクレイピングする方がブラウザ自動化より高速で信頼性が高いです。
ウェブスクレイピングは合法ですか?
ウェブスクレイピングの合法性は管轄区域、ウェブサイトの利用規約、データの使用方法によって異なります。公開データのスクレイピングは多くの管轄区域で一般的に合法ですが、常にサイトのrobots.txtと利用規約を確認してください。個人データや著作権で保護されたコンテンツのスクレイピングは避けてください。
スクレイピング中にブロックされるのを避けるには?
リクエスト間に遅延(1〜3秒)を使用し、User-Agent文字列をローテーションし、robots.txtを尊重し、cookieにセッションを使用し、同時リクエストを過度に行わないようにします。継続的にブロックされる場合は、サイトに公開APIがないか確認してください。
ウェブスクレイピングでページネーションを処理するには?
HTMLで「次のページ」リンクまたはボタンを見つけ、そのURLを抽出し、ページがなくなるまでループで辿ります。または、ページがクエリパラメータ(例:?page=2)を使用している場合は、ページ番号を直接反復処理します。
まとめ
PythonのWebスクレイピングエコシステムはあらゆるシナリオをカバーします:静的ページの素早いスクレイピングにはrequests + BeautifulSoup、JavaScript多用サイトにはSelenium、本番規模のクロールにはScrapy。使える最もシンプルなツールから始め、必要な場合のみ複雑さを追加し、常に倫理的にスクレイピングしてください——robots.txtを尊重し、遅延を追加し、まずAPIを確認してください。スクレイピングしたデータはCSVやDataFrameなどの構造化フォーマットで保存し、即座に分析できるようにしましょう。