統合

Shifterと一緒に使用する Beautiful Soup

ShifterのレジデンシャルおよびISPプロキシをBeautiful Soupと組み合わせることで、クリーンで表現力豊かなPythonスクレイピングを実現できます。Beautiful SoupがHTMLパースを担い、Shifterがレジデンシャル IPを提供するため、ヘッドレスブラウザは不要です。

クイックスタート

インストール

pip install beautifulsoup4 requests lxml

基本的な使い方

import requests
from bs4 import BeautifulSoup

proxy_url = "customer-USERNAME-country-us-sid-123ABC:PASSWORD@p.shifter.io:443"
proxies   = {"http": proxy_url, "https": proxy_url}

response = requests.get("https://example.com", proxies=proxies, timeout=30)
soup = BeautifulSoup(response.text, "lxml")

print(soup.title.string)
for article in soup.select("article.post"):
    print(article.h2.text.strip(), "->", article.a["href"])

機能

requests、httpx、aiohttp、およびproxies dictをサポートする任意のPython HTTPクライアントとスムーズに連携します
デフォルトはリクエストごとのローテーション。スティッキーセッションには`sid`、N秒間のタイムドピンには`ttl-N`を使用
bs4 4.xおよびPython 3.7+と互換性あり — lxmlとhtml.parserバックエンドの両方に対応
ユーザー名パラメータを使用した 195+ か国での Geo ターゲティング -- 国、地域、都市、ASN
静的またはJSが少ないターゲットに対し、ヘッドレスブラウザスクレイピングより桁違いに高速
Scrapy、FastAPIスクレイパー、Airflowタスク、AWS Lambda、およびあらゆるPythonデータパイプラインへのドロップイン対応

スティッキーセッション + 複数ページクロール

プロキシのユーザー名に `sid-XXX` を追加することで、ページネーションクロール全体を通じてレジデンシャル IP を 1 つ固定できます。ジオターゲティングには `country-uk` と `city-london` を追加してください。

import requests
import secrets
from bs4 import BeautifulSoup
from urllib.parse import urljoin

sid = secrets.token_hex(4)

proxy_url = (
    f"customer-USERNAME-country-uk-city-london-sid-{sid}-ttl-300:"
    f"PASSWORD@p.shifter.io:443"
)

# Use a session so connection pooling and cookies persist across requests.
session = requests.Session()
session.proxies = {"http": proxy_url, "https": proxy_url}
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
    "Accept-Language": "en-GB,en;q=0.9",
})

products  = []
url       = "https://example.co.uk/products"

while url:
    response = session.get(url, timeout=30)
    soup     = BeautifulSoup(response.text, "lxml")

    for card in soup.select(".product-card"):
        products.append({
            "title": card.select_one("h2").text.strip(),
            "price": card.select_one(".price").text.strip(),
            "url":   urljoin(url, card.select_one("a")["href"]),
        })

    next_link = soup.select_one("a.next-page")
    url       = urljoin(url, next_link["href"]) if next_link else None

print(f"Scraped {len(products)} products")

concurrent.futuresを使用した並列スクレイピング

リクエストごとのローテーションにはsidを削除してください。ThreadPoolExecutor + requests + Shifterを組み合わせることで、IPあたりのレート制限に引っかからずに数十の同時フェッチにスケールできます。

import requests
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor, as_completed

# No sid -> every request gets a different residential IP.
PROXY_URL = "customer-USERNAME-country-us:PASSWORD@p.shifter.io:443"

def scrape(url: str) -> dict:
    response = requests.get(
        url,
        proxies={"http": PROXY_URL, "https": PROXY_URL},
        headers={"User-Agent": "Mozilla/5.0 AppleWebKit/537.36"},
        timeout=30,
    )
    soup = BeautifulSoup(response.text, "lxml")

    return {
        "url":   url,
        "title": (soup.title.string or "").strip(),
        "h1":    [h.text.strip() for h in soup.select("h1")],
        "links": [a["href"] for a in soup.select("a[href]")[:20]],
    }

urls = [
    "https://example.com/category/laptops",
    "https://example.com/category/phones",
    "https://example.com/category/tablets",
    "https://example.com/category/wearables",
    # ... hundreds more
]

with ThreadPoolExecutor(max_workers=16) as pool:
    futures = {pool.submit(scrape, u): u for u in urls}
    for f in as_completed(futures):
        try:
            result = f.result()
            print(result["url"], "->", result["title"])
        except Exception as exc:
            print("error:", futures[f], exc)

リトライとバックオフを備えた堅牢なクロール

本番環境のスクレイピングでは、5xx および接続エラーに対してリトライが必要です。一時的なブロックを回避するために、urllib3 Retry と Shifter を組み合わせ、試行ごとに新しい sid を使用します。

import requests
import secrets
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class ShifterClient:
    """requests.Session that rotates the residential IP on retry."""

    def __init__(self, country="us"):
        self.country = country
        self._session = requests.Session()

        retry = Retry(
            total=5,
            backoff_factor=1.5,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["GET", "POST", "HEAD"],
        )
        adapter = HTTPAdapter(max_retries=retry, pool_connections=20)
        self._session.mount("http://",  adapter)
        self._session.mount("https://", adapter)

    def _proxy(self) -> str:
        sid = secrets.token_hex(4)
        return (
            f"customer-USERNAME-country-{self.country}-sid-{sid}:"
            f"PASSWORD@p.shifter.io:443"
        )

    def get(self, url: str, **kwargs) -> requests.Response:
        return self._session.get(
            url,
            proxies={"http": self._proxy(), "https": self._proxy()},
            timeout=kwargs.pop("timeout", 30),
            **kwargs,
        )

client   = ShifterClient(country="de")
response = client.get("https://example.de/products")
soup     = BeautifulSoup(response.text, "lxml")

for product in soup.select(".product"):
    print(product.h2.text.strip(), product.select_one(".price").text.strip())

httpx(非同期)+ Beautiful Soup

何千ものページへの非同期ファンアウトが必要な場合は、requests を httpx に置き換えてください。同じ Shifter URL、ネイティブの async/await、Beautiful Soup との完全な互換性を維持します。

# pip install httpx beautifulsoup4 lxml
import asyncio
import httpx
from bs4 import BeautifulSoup

PROXY = "customer-USERNAME-country-fr-sid-789GHI:PASSWORD@p.shifter.io:443"

async def fetch(client: httpx.AsyncClient, url: str) -> dict:
    resp = await client.get(url, timeout=30)
    soup = BeautifulSoup(resp.text, "lxml")
    return {
        "url":      url,
        "title":    (soup.title.string or "").strip(),
        "headings": [h.text.strip() for h in soup.select("h2")],
    }

async def main():
    async with httpx.AsyncClient(proxy=PROXY) as client:
        urls = [
            f"https://example.fr/products?page={i}" for i in range(1, 51)
        ]
        results = await asyncio.gather(*[fetch(client, u) for u in urls])

    for r in results:
        print(r["url"], "->", r["title"])

asyncio.run(main())
FAQ

よくある質問

Shifter と Beautiful Soup の併用に関するよくある質問。

いいえ。Beautiful Soupはパーサーであり、HTTPリクエストは行いません。プロキシはbs4と組み合わせるHTTPクライアント(requests、httpx、aiohttp、urllib)に設定します。ShifterでHTMLを取得したら、それをいつも通りBeautifulSoup()に渡すだけです。

proxies dictをrequests.get()に渡してください: `{"http": "http://USER:PASS@p.shifter.io:443", "https": "..."}`. BeautifulSoup()の第1引数にはresponse.textを使用します。複数ページのクロールには、クッキーとコネクションを維持するためにrequests.Sessionを再利用してください。

単発スクリプト、ノートブック、小規模から中規模のスクレイピングにはBeautiful Soupを使用してください。軽量で読みやすいです。組み込みのキューイング、リトライ、永続性、スケールでの同時実行が必要な場合はScrapyを使用してください。どちらもShifterとシームレスに連携します。

プロキシのユーザー名にセッションIDを追加してください。例:`customer-USERNAME-country-us-sid-123ABC`。すべてのフェッチで単一のrequests.Sessionを再利用すると、ShifterはIPを同じレジデンシャルIPを返し続けます。`ttl-N`を追加するとIPの有効期間を延長できます。

はい。bs4 をhttpx(非同期)またはaiohttp と組み合わせてください。返されるHTML は同一であり、同じ方法でBeautifulSoup() に渡すことができます。数千ページを処理する場合、非同期ファンアウトはrequests を使用したThreadPoolExecutor よりも大幅に高速です。

はい。Shifter は SDK ではなく標準の HTTP / SOCKS5 ゲートウェイであり、サーバーレスと非互換な点は何もありません。Lambda レイヤーに requests と bs4 と lxml をバンドルし、環境変数でプロキシ URL を設定して、通常通り呼び出してください。

始める

でShifterを使い始める Beautiful Soup

Shifterの205M+件のレジデンシャルおよびISPプロキシをBeautiful Soupと組み合わせることで、クリーンで表現力豊かなPythonスクレイピングを実現できます。リクエスト単位のローテーション、スティッキーセッション、httpxによる完全な非同期サポートに対応しています。

Shifterを無料で試す数分でセットアップ完了。いつでもキャンセル可能。