ブログに戻る

プロキシが常にバンされる場合の対処法:診断と問題解決の完全ガイド

プロキシがブロックされる理由を分析し、適切なローテーションからブラウザフィンガープリント設定まで、実践的なソリューションを提供します。

📅2025年12月15日

プロキシが常にバンされる場合:診断と問題解決の完全ガイド

プロキシの継続的なブロックは、スクレイピング、自動化、複数アカウント管理時の最も一般的な問題の1つです。この記事では、なぜこれが起こるのか、そして奇跡を期待してプロバイダーを無限に変更するのではなく、体系的に問題を解決する方法について説明します。

実際にプロキシがバンされる理由

解決策を探す前に、ブロックのメカニズムを理解する必要があります。最新の不正検出システムは多層防御を使用しており、プロキシのバンはその結果に過ぎず、原因ではありません。これらのシステムがどのように機能するかを理解することで、効果的な回避戦略を構築できます。

IP評判とブラックリスト

各IPアドレスには、その使用履歴に基づいて形成される評判があります。アドレスが以前スパム、DDoS攻撃、または大規模スクレイピングに使用されていた場合、Spamhaus、SORBS、または特定のサービスの独自リストなどのデータベースに登録されます。そのようなIPを通じて接続すると、システムはすぐに疑いを持ちます。

データセンタープロキシは特にこの問題の影響を受けやすいです。IP範囲全体が「ホスティング」とマークされ、そこからのトラフィックは自動的に高度なチェックを受けます。Amazon AWS、Google Cloud、DigitalOcean — これらのIP範囲はよく知られており、予防的にブロックされることが多いです。

IPQualityScore、Scamalytics、AbuseIPDBなどのサービスを通じてIP評判を確認できます。プロキシのfraud scoreが75を超える場合、これが問題です — プロバイダーを変更するか、別のプロキシタイプを選択してください。

リクエストパターン

人間は1秒間に100リクエストを送信しません。人間は完璧な周期性で2秒ごとにページを遷移しません。人間は画像、CSS、JavaScriptを無視して、HTMLだけをリクエストしません。不正検出システムはまさにこれらのパターンを分析し、「人間的」な行動からの逸脱は、ブロックのリスクを高めます。

リクエスト間の時間統計は特に有用です。安定した間隔は自動化の明らかな兆候です。ランダムな遅延を追加する(例えば1~5秒)ことで、検出の可能性を大幅に減らします。

メタデータの不一致

User-AgentがWindows上のChromeを示しているのに、HTTPヘッダーがPython requestsの特性を示している場合 — これは赤旗です。IPアドレスがドイツに地理的に位置しているのに、ブラウザ言語設定がロシア語を示している場合 — もう1つの赤旗です。JavaScriptのタイムゾーンがIP地理と一致しない場合 — 3番目の赤旗です。

このような不一致の蓄積により、システムは接続を疑わしいものとして分類し、CAPTCHAから完全なIPブロックまでの保護措置を適用します。

ブラウザフィンガープリント

最新の保護システムはブラウザの数十のパラメータを収集します:画面解像度、インストール済みフォント、プラグイン、WebGLレンダリング、オーディオコンテキストなど。これらのパラメータの組み合わせは、CookieなしでもユーザーをIPアドレスが変わっても識別できるユニークな「フィンガープリント」を作成します。

プロキシを変更しても、フィンガープリントが同じままの場合、システムは同じユーザーであることを理解します。1つのフィンガープリントが短時間に数百の異なるIPから表示される場合 — これは自動化の明らかな兆候です。

診断:ブロックの原因を理解する

ブロックの原因を推測する前に、診断を実施してください。これにより、数時間の試行錯誤が節約され、問題の実際の原因を見つけるのに役立ちます。診断への体系的なアプローチが、効果的なソリューションの鍵です。

ステップ1:プロキシ自体を確認する

メインスクリプトとは独立して、プロキシの基本的な機能を確認することから始めてください:

import requests

proxy = {
    "http": "http://user:pass@proxy-server:port",
    "https": "http://user:pass@proxy-server:port"
}

# プロキシの基本的な機能確認
try:
    response = requests.get("https://httpbin.org/ip", proxies=proxy, timeout=10)
    print(f"プロキシ経由のIP: {response.json()['origin']}")
except Exception as e:
    print(f"接続エラー: {e}")

# 実IPの漏洩確認
response = requests.get("https://browserleaks.com/ip", proxies=proxy)
# 実IPと比較してください

プロキシが単純なリクエストでも機能しない場合 — 問題はプロキシ自体または認証情報にあります。接続形式の正確性、プロバイダーの残高、制限を確認してください。

ステップ2:IP評判を確認する

複合的な評価のために複数のサービスを使用してください:

# プロキシIPを取得
proxy_ip = requests.get("https://api.ipify.org", proxies=proxy).text

# これらのサービスで確認してください:
# https://www.ipqualityscore.com/free-ip-lookup-proxy-vpn-test
# https://scamalytics.com/ip/{proxy_ip}
# https://www.abuseipdb.com/check/{proxy_ip}
# https://whatismyipaddress.com/ip/{proxy_ip}

print(f"上記のサービスでIP {proxy_ip} を確認してください")

次の指標に注意してください:fraud score(50未満であるべき)、IPタイプ(データセンターより住宅用が良い)、ブラックリストへの登録。IPがVPN/Proxyとマークされている場合 — 多くのサイトは最初から疑いを持ちます。

ステップ3:問題を分離する

異なるターゲットサイトで同じプロキシを試してください。すべてでブロックされる場合 — 問題はプロキシまたは設定にあります。特定のサイトでのみブロックされる場合 — 問題はそのサイトの保護またはあなたの行動にあります。

また、1つのサイトで異なるプロキシを試してください。すべてがブロックされる場合 — 問題はプロキシではなく、スクリプト、フィンガープリント、または行動パターンにあります。これは多くの人が見落とす重要なテストです。

ステップ4:サーバー応答の分析

異なるタイプのブロックは異なる方法で現れます。それらを区別することを学んでください:

def analyze_response(response):
    status = response.status_code
    
    if status == 403:
        print("アクセス禁止 — IPがブラックリストに登録されている可能性")
    elif status == 429:
        print("リクエストが多すぎます — 頻度を下げてください")
    elif status == 503:
        print("サービス利用不可 — DDoS保護の可能性")
    elif status == 407:
        print("プロキシ認証が必要 — 認証情報を確認してください")
    elif "captcha" in response.text.lower():
        print("CAPTCHAが検出されました — ボット疑いあり")
    elif "blocked" in response.text.lower():
        print("明示的なブロック — IPを変更して方法を見直してください")
    elif len(response.text) < 1000:
        print("疑わしく短い応答 — スタブの可能性")
    else:
        print(f"ステータス {status}、応答長: {len(response.text)}")

適切なローテーション:頻度、ロジック、実装

プロキシローテーションは単に「より頻繁にIPを変更する」ことではありません。不適切なローテーションは、その不在よりも害をもたらす可能性があります。異なる戦略と、それらをいつ適用するかについて考えてみましょう。

戦略1:リクエスト数に基づくローテーション

シンプルなアプローチ — 一定数のリクエスト後にIPを変更します。セッション性が不要なスクレイピングに適しています:

import random

class ProxyRotator:
    def __init__(self, proxy_list, requests_per_proxy=50):
        self.proxies = proxy_list
        self.requests_per_proxy = requests_per_proxy
        self.current_proxy = None
        self.request_count = 0
    
    def get_proxy(self):
        if self.current_proxy is None or self.request_count >= self.requests_per_proxy:
            # リクエスト数にランダム性を追加
            self.requests_per_proxy = random.randint(30, 70)
            self.current_proxy = random.choice(self.proxies)
            self.request_count = 0
        
        self.request_count += 1
        return self.current_proxy

# 使用方法
rotator = ProxyRotator(proxy_list)
for url in urls_to_scrape:
    proxy = rotator.get_proxy()
    response = requests.get(url, proxies={"http": proxy, "https": proxy})

リクエスト数の固定値(例えば正確に50)は検出可能なパターンです。ランダムな範囲を使用すると、動作がより予測不可能になります。

戦略2:時間ベースのローテーション

セッション性が重要なタスク(例えばアカウント操作)の場合、IPを時間に基づいてローテーションする方が良いです:

import time
import random

class TimeBasedRotator:
    def __init__(self, proxy_list, min_minutes=10, max_minutes=30):
        self.proxies = proxy_list
        self.min_seconds = min_minutes * 60
        self.max_seconds = max_minutes * 60
        self.current_proxy = None
        self.rotation_time = 0
    
    def get_proxy(self):
        current_time = time.time()
        
        if self.current_proxy is None or current_time >= self.rotation_time:
            self.current_proxy = random.choice(self.proxies)
            # 次のローテーションまでのランダムな間隔
            interval = random.randint(self.min_seconds, self.max_seconds)
            self.rotation_time = current_time + interval
            print(f"新しいプロキシ、次のローテーションは{interval//60}分後")
        
        return self.current_proxy

戦略3:アカウント用のスティッキーセッション

複数のアカウントで作業する場合、各アカウントが一定のIPを使用することが重要です。ログイン済みアカウントのIP変更は確実にバンにつながります:

class AccountProxyManager:
    def __init__(self, proxy_pool):
        self.proxy_pool = proxy_pool
        self.account_proxies = {}  # account_id -> proxy
        self.used_proxies = set()
    
    def get_proxy_for_account(self, account_id):
        # アカウントに既にプロキシが割り当てられている場合は返す
        if account_id in self.account_proxies:
            return self.account_proxies[account_id]
        
        # 利用可能なプロキシを見つける
        available = [p for p in self.proxy_pool if p not in self.used_proxies]
        
        if not available:
            raise Exception("新しいアカウント用の利用可能なプロキシがありません")
        
        proxy = random.choice(available)
        self.account_proxies[account_id] = proxy
        self.used_proxies.add(proxy)
        
        return proxy
    
    def release_account(self, account_id):
        """アカウント削除時にプロキシを解放"""
        if account_id in self.account_proxies:
            proxy = self.account_proxies.pop(account_id)
            self.used_proxies.discard(proxy)

# 使用方法
manager = AccountProxyManager(residential_proxy_list)

for account in accounts:
    proxy = manager.get_proxy_for_account(account.id)
    # このアカウントのすべてのアクションは1つのIPを通じて実行

戦略4:適応型ローテーション

最も高度なアプローチ — ターゲットサイトからのシグナルに応じてプロキシを変更します:

class AdaptiveRotator:
    def __init__(self, proxy_list):
        self.proxies = proxy_list
        self.current_proxy = random.choice(proxy_list)
        self.proxy_scores = {p: 100 for p in proxy_list}  # プロキシの初期「健全性」
    
    def get_proxy(self):
        return self.current_proxy
    
    def report_result(self, success, response_code=200):
        """各リクエスト後に呼び出される"""
        if success and response_code == 200:
            # 成功したリクエスト — スコアをわずかに上げる
            self.proxy_scores[self.current_proxy] = min(100, 
                self.proxy_scores[self.current_proxy] + 1)
        elif response_code == 429:
            # レート制限 — スコアを大幅に下げてローテーション
            self.proxy_scores[self.current_proxy] -= 30
            self._rotate()
        elif response_code == 403:
            # バン — スコアをリセットしてローテーション
            self.proxy_scores[self.current_proxy] = 0
            self._rotate()
        elif response_code == 503:
            # 保護の可能性 — スコアを下げてローテーション
            self.proxy_scores[self.current_proxy] -= 20
            self._rotate()
    
    def _rotate(self):
        # スコアが最も高いプロキシを選択
        available = [(p, s) for p, s in self.proxy_scores.items() if s > 20]
        if not available:
            # すべてのプロキシが「殺された」 — スコアをリセット
            self.proxy_scores = {p: 50 for p in self.proxies}
            available = list(self.proxy_scores.items())
        
        # スコアに基づいた加重選択
        self.current_proxy = max(available, key=lambda x: x[1])[0]
        print(f"スコア{self.proxy_scores[self.current_proxy]}のプロキシにローテーション")

ブラウザフィンガープリントとブロックの役割

フィンガープリントはブラウザの特性の集合であり、Cookieなしでもユーザーを識別できます。IPを変更しても、フィンガープリントが同じままの場合、保護システムは同じユーザーであることを理解します。

フィンガープリントの構成

最新のフィンガープリントには数十のパラメータが含まれます。主なカテゴリは次のとおりです:

カテゴリ パラメータ 識別での重要度
User-Agent ブラウザ、バージョン、OS 中程度
スクリーン 解像度、色深度、ピクセル比 中程度
フォント インストール済みフォントのリスト 高い
WebGL レンダラー、ベンダー、レンダリングハッシュ 非常に高い
Canvas 描画された画像のハッシュ 非常に高い
Audio AudioContextフィンガープリント 高い
タイムゾーン タイムゾーン、オフセット 中程度
言語 navigator.languages 中程度
プラグイン navigator.plugins 低い(最新ブラウザ)

フィンガープリントとIPの一貫性

フィンガープリントがIP地理と一致することが重要です。プロキシがドイツにある場合、フィンガープリントはドイツのユーザーのように見える必要があります:

// 不一致の例(悪い):
// IP:ドイツ
// タイムゾーン:America/New_York
// 言語:["ru-RU", "ru"]
// これは疑いを引き起こします

// 一貫したフィンガープリント(良い):
// IP:ドイツ
// タイムゾーン:Europe/Berlin
// 言語:["de-DE", "de", "en-US", "en"]

フィンガープリント管理ツール

本格的な作業には、専門的なツールを使用してください:

Stealth付きPlaywright:

from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync

with sync_playwright() as p:
    browser = p.chromium.launch(
        proxy={"server": "http://proxy:port", "username": "user", "password": "pass"}
    )
    
    context = browser.new_context(
        viewport={"width": 1920, "height": 1080},
        locale="de-DE",
        timezone_id="Europe/Berlin",
        geolocation={"latitude": 52.52, "longitude": 13.405},
        permissions=["geolocation"]
    )
    
    page = context.new_page()
    stealth_sync(page)  # ステルスパッチを適用
    
    page.goto("https://target-site.com")

puppeteer-extra付きPuppeteer:

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');

puppeteer.use(StealthPlugin());

const browser = await puppeteer.launch({
    args: [`--proxy-server=http://proxy:port`]
});

const page = await browser.newPage();

// タイムゾーンをオーバーライド
await page.evaluateOnNewDocument(() => {
    Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
        value: function() {
            return { timeZone: 'Europe/Berlin' };
        }
    });
});

アンチディテクトブラウザ

アカウント作業には、アンチディテクトブラウザ(Multilogin、GoLogin、Dolphin Antyなど)がよく使用されます。これらは、ユニークなフィンガープリントを持つ分離されたプロファイルを作成できます。各プロファイルには独自のパラメータセット、Cookie、localStorage — 完全に分離された環境があります。

アンチディテクトの利点は、フィンガープリント問題を「すぐに」解決することです。欠点は、コストと自動化の複雑さです(ただし、多くはAPIを持っています)。

行動パターン:ボットに見えないようにする

完璧なフィンガープリントと清潔なIPがあっても、非人間的な行動によってバンを受ける可能性があります。最新のシステムは技術パラメータだけでなく、サイトとの相互作用パターンも分析します。

時間遅延

人間は一定の間隔でリクエストを送信しません。正規分布を使用してランダムな遅延を追加してください:

import random
import time
import numpy as np

def human_delay(min_sec=1, max_sec=5, mean=2.5):
    """
    人間的な遅延を生成します。
    対数正規分布を使用 — 
    ほとんどの遅延は短いですが、時々長い遅延があります。
    """
    delay = np.random.lognormal(mean=np.log(mean), sigma=0.5)
    delay = max(min_sec, min(max_sec, delay))
    return delay

def human_typing_delay():
    """キー入力間の遅延"""
    return random.uniform(0.05, 0.25)

# 使用方法
for url in urls:
    response = requests.get(url, proxies=proxy)
    process(response)
    time.sleep(human_delay())  # リクエスト間のランダムな一時停止

ナビゲーションの模倣

人間は直接リンクから商品ページに移動しません。ホームページに移動し、検索を使用し、カテゴリを閲覧します。このパスを模倣してください:

async def human_like_navigation(page, target_url):
    """ターゲットページへの人間的なナビゲーションを模倣"""
    
    # 1. ホームページに移動
    await page.goto("https://example.com")
    await page.wait_for_timeout(random.randint(2000, 4000))
    
    # 2. ときどきホームページをスクロール
    if random.random() > 0.5:
        await page.evaluate("window.scrollBy(0, 300)")
        await page.wait_for_timeout(random.randint(1000, 2000))
    
    # 3. 検索またはナビゲーションを使用
    if random.random() > 0.3:
        search_box = await page.query_selector('input[type="search"]')
        if search_box:
            await search_box.type("search query", delay=100)
            await page.keyboard.press("Enter")
            await page.wait_for_timeout(random.randint(2000, 4000))
    
    # 4. ターゲットページに移動
    await page.goto(target_url)
    
    # 5. 人間的にページをスクロール
    await human_scroll(page)

async def human_scroll(page):
    """人間的なスクロールを模倣"""
    scroll_height = await page.evaluate("document.body.scrollHeight")
    current_position = 0
    
    while current_position < scroll_height * 0.7:  # 最後まで行かない
        scroll_amount = random.randint(200, 500)
        await page.evaluate(f"window.scrollBy(0, {scroll_amount})")
        current_position += scroll_amount
        await page.wait_for_timeout(random.randint(500, 1500))

マウスの動き

一部のシステムはマウスの動きを追跡します。ポイントAからポイントBへの直線移動はボットの兆候です。人間はマイクロ補正を伴う曲線でマウスを動かします:

import bezier
import numpy as np

def generate_human_mouse_path(start, end, num_points=50):
    """
    ベジェ曲線を使用して、人間的なマウスパスを生成します
    わずかなノイズを追加します。
    """
    # ベジェ曲線の制御点
    control1 = (
        start[0] + (end[0] - start[0]) * random.uniform(0.2, 0.4) + random.randint(-50, 50),
        start[1] + (end[1] - start[1]) * random.uniform(0.2, 0.4) + random.randint(-50, 50)
    )
    control2 = (
        start[0] + (end[0] - start[0]) * random.uniform(0.6, 0.8) + random.randint(-50, 50),
        start[1] + (end[1] - start[1]) * random.uniform(0.6, 0.8) + random.randint(-50, 50)
    )
    
    # ベジェ曲線を作成
    nodes = np.asfortranarray([
        [start[0], control1[0], control2[0], end[0]],
        [start[1], control1[1], control2[1], end[1]]
    ])
    curve = bezier.Curve(nodes, degree=3)
    
    # 曲線上のポイントを生成
    points = []
    for t in np.linspace(0, 1, num_points):
        point = curve.evaluate(t)
        # マイクロノイズを追加
        x = point[0][0] + random.uniform(-2, 2)
        y = point[1][0] + random.uniform(-2, 2)
        points.append((x, y))
    
    return points

async def human_click(page, selector):
    """人間的なマウス動きでセレクタをクリック"""
    element = await page.query_selector(selector)
    box = await element.bounding_box()
    
    # ターゲットポイント — 中心ではなく、要素内のランダムなポイント
    target_x = box['x'] + random.uniform(box['width'] * 0.2, box['width'] * 0.8)
    target_y = box['y'] + random.uniform(box['height'] * 0.2, box['height'] * 0.8)
    
    # 現在のマウス位置(またはランダムな開始位置)
    start_x = random.randint(0, 1920)
    start_y = random.randint(0, 1080)
    
    # パスを生成
    path = generate_human_mouse_path((start_x, start_y), (target_x, target_y))
    
    # パスに沿ってマウスを動かす
    for x, y in path:
        await page.mouse.move(x, y)
        await page.wait_for_timeout(random.randint(5, 20))
    
    # クリック前の小さな一時停止
    await page.wait_for_timeout(random.randint(50, 150))
    await page.mouse.click(target_x, target_y)

リソースの読み込み

実際のブラウザはHTMLだけでなく、CSS、JavaScript、画像、フォントも読み込みます。requestsを使用してHTMLだけをリクエストする場合は疑わしいです。headless-ブラウザを使用する場合、この問題は自動的に解決されますが、HTTPクライアントを使用する場合は、このポイントを考慮する必要があります。

タスクに応じたプロキシタイプの選択

異なるプロキシタイプには異なる特性があり、異なるタスクに適しています。不適切な選択は、ブロックの一般的な原因です。

データセンタープロキシ

データセンタープロキシは、ホスティングプロバイダーに属するIPアドレスです。これらはAS(大規模データセンターの自律システム)の所属により識別できます。

利点:

  • 高速で安定
  • 低コスト
  • 大規模なIPプール

欠点:

  • 簡単に検出可能
  • ブラックリストに登録されることが多い
  • 厳格な保護を持つサイトには不適切

適用対象: SEOツール、可用性確認、厳格な保護のないAPIとの連携、テスト。

住宅用プロキシ

住宅用プロキシは、パートナープログラムまたはアプリケーション内のSDKを通じて提供される実際のユーザーのIPアドレスです。これらは通常のインターネットプロバイダー(ISP)に属しています。

利点:

  • 通常のユーザーのように見える
  • 低いfraud score
  • 広い地理的カバレッジ
  • 検出が難しい

欠点:

  • より高いコスト(トラフィック課金)
  • 速度はエンドユーザーに依存
  • IP「消失」の可能性(ユーザーがデバイスをオフにした)

適用対象: 保護されたサイトのスクレイピング、ソーシャルメディアとの連携、e-commerce、検出を避けることが重要なあらゆるタスク。

モバイルプロキシ

モバイルプロキシは、モバイルオペレーター(MTS、Beeline、Megafonなど)のIPアドレスです。CGNAT技術により特別なステータスを持ちます。

利点:

  • サイトからの最大の信頼
  • 1つのIPを数千の実際のユーザーが使用 — バンが難しい
  • アカウント作業に理想的
  • リクエスト時のIP変更(ネットワーク再接続)

欠点:

  • 最も高いコスト
  • 限定的な速度
  • 地理的選択肢が少ない

適用対象: マルチアカウンティング、Instagram/Facebook/TikTokとの連携、アカウント登録、バンのリスクが高いあらゆるタスク。

比較表

パラメータ データセンタ 住宅用 モバイル
検出可能性 高い 低い 非常に低い
速度 高い 中程度 低~中程度
コスト $ $$ $$$
ソーシャルメディア向け 不適切 適切 理想的
スクレイピング向け シンプルなサイト あらゆるサイト 過剰

保護回避の高度なテクニック

基本的な方法が機能しない場合は、より複雑なテクニックを使用する必要があります。保護回避のいくつかの高度なアプローチを考えてみましょう。

Cloudflareおよび同様の保護の処理

Cloudflare、Akamai、PerimeterX — これらのシステムはブラウザを確認するためにJavaScriptチャレンジを使用します。単純なHTTPリクエストは通過しません。解決策のオプション:

1. 実際のブラウザを使用:

from playwright.sync_api import sync_playwright

def bypass_cloudflare(url, proxy):
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=False,  # ときどきheadlessが検出される
            proxy={"server": proxy}
        )
        
        page = browser.new_page()
        page.goto(url)
        
        # チェック通過を待つ(通常5~10秒)
        page.wait_for_timeout(10000)
        
        # チェックを通過したか確認
        if "challenge" not in page.url:
            # 後続のリクエスト用にCookieを保存
            cookies = page.context.cookies()
            return cookies
        
        browser.close()
        return None

2. 既製のソリューションを使用:

# cloudscraper — Cloudflare回避ライブラリ
import cloudscraper

scraper = cloudscraper.create_scraper(
    browser={
        'browser': 'chrome',
        'platform': 'windows',
        'desktop': True
    }
)

scraper.proxies = {"http": proxy, "https": proxy}
response = scraper.get("https://protected-site.com")

CAPTCHAの解決

サイトがCAPTCHAを表示する場合、いくつかのアプローチがあります:

認識サービス: 2Captcha、Anti-Captcha、CapMonster。これらはあなたのためにCAPTCHAを解決します