1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

スクレイピング失敗からのリトライ戦略設計|AI実務ノート 編集部

Last updated at Posted at 2026-01-03

1. 結論(この記事で得られること)

この記事では、スクレイピング処理におけるリトライ戦略の実装パターンを、実務で即使えるレベルで解説します。

得られるもの:

  • Exponential Backoffを含むリトライパターンの実装コード
  • レート制限、タイムアウト、一時エラーの見極め方
  • AI(Claude/GPT)を使った障害原因の最速特定法
  • テスト・監視戦略とインシデント対応の型

僕も昔、リトライなしのスクレイピングバッチを書いて、深夜3時にアラートで起こされた経験があります。あの時ちゃんとエラーハンドリング設計していれば…という後悔から、このノウハウをまとめました。

2. 前提(環境・読者層)

想定読者:

  • Python でスクレイピング経験あり(requests / BeautifulSoup / Selenium など)
  • リトライ処理を「とりあえず3回ループ」で書いて後悔したことがある
  • 本番運用での障害対応・監視設計に興味がある

動作環境:

  • Python 3.9+
  • requests, tenacity(リトライライブラリ)
  • Playwright または Selenium(動的サイト用)

3. Before:よくあるつまずきポイント

3-1. ナイーブなリトライ実装

# ❌ よく見る危険なパターン
def scrape_page(url):
    for i in range(3):
        try:
            response = requests.get(url, timeout=10)
            return response.text
        except:
            if i == 2:
                raise
            time.sleep(1)

問題点:

  • すべての例外を無差別にリトライ(404もリトライしてしまう)
  • 固定間隔sleep → 相手サーバーに負荷をかけ続ける
  • リトライ失敗時のログが残らない
  • タイムアウトと接続エラーの区別がない

3-2. 実際に起こる現場の問題

僕が過去に遭遇したケース:

  • Case1: CloudflareのDDoS保護でブロックされたのにリトライし続け、IP BAN
  • Case2: 503エラー(メンテナンス)を30分リトライして無駄にリソース消費
  • Case3: レート制限(429)の Retry-After ヘッダーを無視して即座にリトライ

これらはすべて「リトライすべきエラー」と「即座に諦めるべきエラー」の区別がついていなかったことが原因です。

4. After:基本的な解決パターン

4-1. エラー分類マトリクス

429 (Rate Limit)
 リトライ: ✅
 戦略: Retry-Afterヘッダーに従う

500, 502, 503
 リトライ: ✅
 戦略: Exponential Backoff

504 (Timeout)
 リトライ: ✅
 戦略: タイムアウト値を増やして再試行

404, 403, 401
 リトライ: ❌
 戦略: 即座に失敗扱い(ログのみ)

ConnectionError
 リトライ: ✅
 戦略: ネットワーク起因、短時間で回復可能性

4-2. 改善版:基本実装

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import time
 
def create_session_with_retry():
    """リトライ機能付きセッション生成"""
    session = requests.Session()
 
    # リトライ対象のステータスコード
    retry_strategy = Retry(
        total=3,
        status_forcelist=[429, 500, 502, 503, 504],
        backoff_factor=1,  # 1秒、2秒、4秒...
        respect_retry_after_header=True
    )
 
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("http://", adapter)
    session.mount("https://", adapter)
 
    return session
 
def scrape_with_basic_retry(url):
    session = create_session_with_retry()
 
    try:
        response = session.get(
            url,
            timeout=(5, 15),  # (接続, 読み取り)
            headers={'User-Agent': 'MyBot/1.0'}
        )
        response.raise_for_status()
        return response.text
 
    except requests.exceptions.HTTPError as e:
        if e.response.status_code in [404, 403, 401]:
            print(f"⚠️ リトライ不要エラー: {e}")
            return None
        raise
    except requests.exceptions.Timeout:
        print(f"⏱️ タイムアウト: {url}")
        raise

この実装なら、レート制限やサーバーエラーには自動で適切に対応できます。


続きはnoteで

この記事の実装編・詳細解説はnoteで公開しています。

実際のコード例や、実務で遭遇するハマりポイントなど、より踏み込んだ内容を書いています。

👉 スクレイピング失敗からのリトライ戦略設計(実務 × AI 時代の最短解法)|AI実務ノート 編集部

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?