Skip to content

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 lxml

BeautifulSoupでの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ページ高速非対応簡単
SeleniumJavaScript描画ページ低速対応中程度
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などの構造化フォーマットで保存し、即座に分析できるようにしましょう。

📚