Bright Data: プロキシサービスで堅牢な Web スクレイピング

このチュートリアルでは、Python で Bright Data のプロキシネットワーク を使用する方法を学びます。Bright Data には Scraper API や Datasets などの高レベルツールもありますが、このガイドでは基盤となるサービスであるプロキシネットワークに焦点を当てます。

人気のある requests ライブラリと Bright Data の強力なプロキシを統合して、一般的な Web スクレイピングタスクを確実かつ効率的に実行する方法をカバーします。IP ブロックの克服、異なる国からのコンテンツへのアクセス、リクエストセッションの管理方法を学びましょう。それでは始めましょう!🌍

1. Web スクレイピングでプロキシを使用する理由

Web サイトをスクレイピングするとき、コンピューターの IP アドレスから多くのリクエストが送信されます。Web サイトはこの異常なアクティビティを簡単に検出し、IP をブロックしたり、誤解を招く情報を表示(クローキング)したり、CAPTCHA の解決を要求したりする可能性があります。

プロキシサーバーは仲介者として機能します。自身の IP アドレスを使用してターゲット Web サイトにリクエストを転送し、あなたの IP を隠します。Bright Data のようなプロキシネットワークは、世界中の多数の異なる IP(データセンター、住宅、ISP、モバイル)にアクセスできます。これにより、以下が可能になります。

  • IP バンとレート制限の回避: 異なる IP を順番に使用することで、リクエストは多くの異なるユーザーから送信されているように見え、スクレイパーの検出とブロックがはるかに困難になります。
  • 地理制限コンテンツへのアクセス: リクエストが特定の国から来ているように見せることができ、ローカライズされた価格、コンテンツ、サービスのスクレイピングが可能になります。
  • スケーラビリティと信頼性の向上: 大規模で信頼性の高いプロキシネットワークにより、スクレイパーは高い成功率で大規模に実行できます。

2. セットアップと設定

2.1. ライブラリのインストール

まず、HTTP リクエストを行うための requests ライブラリと、API 認証情報を安全に管理するための python-dotenv をインストールします。

#%pip install requests python-dotenv -q

2.2. Bright Data プロキシ認証情報の取得

開始する前に、Bright Data ダッシュボードからプロキシ認証情報が必要です。

  1. サインアップ: Bright Data でアカウントを作成します。
  2. Proxies & Scraping Infrastructure に移動: ダッシュボードでこのセクションに移動し、「Add」をクリックして新しいプロキシゾーンを作成します。
  3. ネットワークタイプの選択: ほとんどの Web スクレイピングタスクでは、Residential Proxies が最も効果的です。これを選択し、ゾーンを設定します。
  4. 認証情報の取得: ゾーンが作成されたら、それをクリックして "Access parameters" タブに移動します。Host、Port、Username、Password が表示されます。Host は通常 brd.superproxy.io です。

2.3. .env ファイルの設定

このノートブックと同じディレクトリに .env という名前のファイルを作成します。ここに認証情報を保存することで、認証情報を安全に保ち、コードから分離できます。以下のように、プレースホルダーの値を実際のアクセスパラメーターに置き換えて認証情報を追加してください。

BRIGHTDATA_HOST='brd.superproxy.io'
BRIGHTDATA_PORT='your_port'
BRIGHTDATA_USERNAME='brd-customer-hl_xxxxxxxx-zone-your_zone_name'
BRIGHTDATA_PASSWORD='your_zone_password'
import os
import requests
from dotenv import load_dotenv

# Load environment variables from the .env file
load_dotenv()

# Retrieve credentials
host = os.getenv("BRIGHTDATA_HOST")
port = os.getenv("BRIGHTDATA_PORT")
username = os.getenv("BRIGHTDATA_USERNAME")
password = os.getenv("BRIGHTDATA_PASSWORD")

# Check if all credentials are loaded
if not all([host, port, username, password]):
    raise ValueError("Proxy credentials not found in .env file. Please check your configuration.")
    
# Construct the proxy URL for the requests library
proxy_url = f"http://{username}:{password}@{host}:{port}"
proxies = {
    "http": proxy_url,
    "https": proxy_url
}

print("✅ Proxy credentials loaded successfully!")
✅ Proxy credentials loaded successfully!

3. 基本的なプロキシ経由リクエストの実行

セットアップをテストしましょう。https://geo.brdtest.com/mygeo.json にリクエストを行います。これは、着信リクエストの IP アドレスに関する地理位置情報の詳細を返す Bright Data サービスです。まず実際の IP を確認し、次にプロキシを介して同じリクエストを行って IP が変わるのを確認します。

target_url = 'https://geo.brdtest.com/mygeo.json'

response_local = requests.get(target_url)
response_local.raise_for_status() # Raise an exception for bad status codes
local_ip = response_local.json().get('geo')
local_ip
{'city': 'Singapore',
 'region': '',
 'region_name': '',
 'postal_code': '31',
 'latitude': 1.3352,
 'longitude': 103.8529,
 'tz': 'Asia/Singapore',
 'lum_city': 'singapore'}
from IPython.display import display, Markdown

target_url = 'https://geo.brdtest.com/mygeo.json'

# 1. Request with your local IP
try:
    print("Requesting without a proxy...")
    response_local = requests.get(target_url)
    response_local.raise_for_status() # Raise an exception for bad status codes
    local_data = response_local.json().get('geo')
    display(f"🌍 Your Local Geo: {local_data}")
except requests.exceptions.RequestException as e:
    print(f"An error occurred: {e}")

print("-" * 30)

# 2. Request with the Bright Data proxy
try:
    print("Requesting with a Bright Data proxy...")
    # The 'proxies' dict tells requests to route the call through the proxy.
    # `verify=False` is used here like the -k flag in cURL to handle SSL certificates via the proxy.
    response_proxy = requests.get(target_url, proxies=proxies, verify=False)
    response_proxy.raise_for_status()
    proxy_data = response_proxy.json().get('geo')
    display(f"🕵️ Your Proxy Geo: {proxy_data}")
except requests.exceptions.RequestException as e:
    print(f"An error occurred: {e}")
Requesting without a proxy...
"🌍 Your Local Geo: {'city': 'Singapore', 'region': '', 'region_name': '', 'postal_code': '31', 'latitude': 1.3352, 'longitude': 103.8529, 'tz': 'Asia/Singapore', 'lum_city': 'singapore'}"
------------------------------
Requesting with a Bright Data proxy...
"🕵️ Your Proxy Geo: {'city': 'London', 'region': 'ENG', 'region_name': 'England', 'postal_code': 'EC4R', 'latitude': 51.5164, 'longitude': -0.093, 'tz': 'Europe/London', 'lum_city': 'london', 'lum_region': 'eng'}"

4. 高度な使い方: ジオターゲティング

プロキシネットワークの最も強力な機能の 1 つはジオターゲティングです。事実上あらゆる国からのリクエストに見せかけることができます。Bright Data では、プロキシユーザー名に国パラメーターを追加することでこれを行います。

形式は username-country-COUNTRYCODE です。例えば、ドイツの IP を使用するには、ユーザー名を your_username-country-de にします。

def get_proxied_ip(country_code=None):
    """Fetches the origin IP through a potentially geo-targeted proxy."""
    
    proxy_user_geo = username
    if country_code:
        proxy_user_geo += f"-country-{country_code.lower()}"
    
    geo_proxy_url = f"http://{proxy_user_geo}:{password}@{host}:{port}"
    geo_proxies = {
        'http': geo_proxy_url,
        'https': geo_proxy_url
    }
    
    try:
        print(f"Requesting with a proxy from country: {country_code or 'Any (Rotating)'}...")
        response = requests.get(target_url, proxies=geo_proxies, timeout=10, verify=False)
        response.raise_for_status()
        data = response.json()
        display(f"  -> 🕵️ Proxy Data: {data.get('geo')}")
    except requests.exceptions.RequestException as e:
        print(f"  -> An error occurred: {e}\n")

# Example: Request from Germany (DE) and Canada (CA)
get_proxied_ip(country_code="de")
get_proxied_ip(country_code="ca")
Requesting with a proxy from country: de...
"  -> 🕵️ Proxy Data: {'city': 'Düsseldorf', 'region': 'NW', 'region_name': 'North Rhine-Westphalia', 'postal_code': '40468', 'latitude': 51.2562, 'longitude': 6.7827, 'tz': 'Europe/Berlin', 'lum_city': 'dusseldorf', 'lum_region': 'nw'}"
Requesting with a proxy from country: ca...
"  -> 🕵️ Proxy Data: {'city': 'Mississauga', 'region': 'ON', 'region_name': 'Ontario', 'postal_code': 'L5A', 'latitude': 43.5873, 'longitude': -79.614, 'tz': 'America/Toronto', 'lum_city': 'mississauga', 'lum_region': 'on'}"

5. 高度な使い方: スティッキーセッション

デフォルトでは、住宅プロキシゾーンを介して送信される各リクエストは異なる IP を使用する可能性があります。これはブロックを回避するのに最適です。ただし、複数ページのフォームやショッピングカートをナビゲートするときなど、複数のリクエストにわたって同じ IP を維持する必要がある場合があります。

これを「スティッキーセッション」と呼びます。使用するには、ユーザー名にセッション ID パラメーターを追加します: username-session-SESSIONID。SESSIONID は任意のランダムな文字列または数値を選択できます。同じセッション ID を使用するすべてのリクエストは、同じ IP アドレスを経由してルーティングされます。

import random
import requests

# Simplified sticky session test using a single IP echo endpoint.
IP_ENDPOINT = "https://api.ipify.org?format=json"

def test_session_ip(session_id: int, attempts: int = 2, timeout: int = 10):
    """Check whether the same proxy IP is kept across multiple requests using a session ID.
    Args:
        session_id: Arbitrary integer/str to pin the session.
        attempts: How many requests to make (default 2 for a simple comparison).
        timeout: Seconds before timing out each request.
    """
    session_username = f"{username}-session-{session_id}"
    session_proxy_url = f"http://{session_username}:{password}@{host}:{port}"
    session_proxies = {
        'http': session_proxy_url,
        'https': session_proxy_url
    }

    display(f"--- Testing Sticky Session (Session ID: {session_id}) ---")
    ips = []
    for i in range(attempts):
        try:
            display(f"  Request #{i+1} -> querying {IP_ENDPOINT}")
            resp = requests.get(IP_ENDPOINT, proxies=session_proxies, timeout=timeout, verify=False)
            resp.raise_for_status()
            ip = resp.json().get('ip')
            display(f"    Returned IP: {ip}")
            ips.append(ip)
        except Exception as e:
            display(f"    Error: {e}")
            ips.append(None)

    # Simple evaluation
    if len(ips) >= 2 and all(ips) and len(set(ips)) == 1:
        display("✅ Sticky success: All requests used the same IP.")
    else:
        display("❌ Not sticky (or undetermined). IPs observed:")
        for idx, ip in enumerate(ips, start=1):
            display(f"    Attempt {idx}: {ip}")
        display("    (Different or missing IPs can mean rotation is enforced or the request failed.)")

# Run the simplified test
random_session_id = random.randint(100000, 999999)
test_session_ip(random_session_id)
'--- Testing Sticky Session (Session ID: 648044) ---'
'  Request #1 -> querying https://api.ipify.org?format=json'
'    Returned IP: 45.185.133.250'
'  Request #2 -> querying https://api.ipify.org?format=json'
'    Returned IP: 45.185.133.250'
'✅ Sticky success: All requests used the same IP.'

6. ベストプラクティスとまとめ

Bright Data プロキシネットワークを Python で正常に設定・使用しました!IP を隠蔽し、特定の国をターゲットにし、セッションを維持する方法を学びました。

スクレイパーをさらに堅牢にするために、常に以下を忘れないでください。

  • リアルなヘッダーを設定する: IP を変更するだけでなく、実際の Web ブラウザーを模倣するために User-Agent ヘッダーも設定する必要があります。これは、ボットとして識別されるのを回避するための重要なステップです。
  • エラーハンドリングを実装する: ネットワークリクエストは失敗する可能性があります。潜在的なタイムアウト、接続エラー、または不良な HTTP ステータスコードを適切に処理するために、常にリクエストを try...except ブロックでラップしてください。
  • robots.txt を尊重する: 良いインターネット市民でありましょう。Web サイトの robots.txt ファイル(例: example.com/robots.txt)を確認し、自動化プログラムがアクセスすべきでないサイトの部分に関するルールを確認してください。

このチュートリアルは、強力で耐障害性の高い Web スクレイパーを構築するための強固な基盤を提供します。より高度な機能や異なるプロキシタイプを探索するには、公式 Bright Data ドキュメント を確認してください。

Happy scraping! 🎉