โ† Kembali ke blog

Cara Mengatasi Rate Limiting dengan Proxy: Arsitektur, Rotasi IP, dan Contoh Kode untuk Pengembang

Rate limiting memblokir parser atau klien API Anda? Kami membahas cara mengatasi batasan jumlah permintaan menggunakan proxy โ€” dengan contoh kode dan solusi arsitektur.

๐Ÿ“…12 Mei 2026
```html

Pembatasan laju โ€” salah satu penyebab paling umum mengapa pengambil data gagal, integrasi API terputus, dan skrip otomatis mendapatkan status 429 Terlalu Banyak Permintaan. Server melihat terlalu banyak permintaan dari satu IP โ€” dan hanya berhenti merespons. Dalam artikel ini, kita akan membahas cara membangun infrastruktur yang tepat dengan proxy untuk menghindari batasan permintaan tanpa pemblokiran dan gangguan โ€” dengan contoh kode nyata menggunakan Python dan Node.js.

Apa itu pembatasan laju dan mengapa penundaan biasa tidak membantu

Pembatasan laju (rate limiting) โ€” adalah mekanisme perlindungan server yang membatasi jumlah permintaan dari satu sumber dalam jangka waktu tertentu. Sumber tersebut biasanya adalah alamat IP, tetapi sistem yang lebih canggih juga mempertimbangkan token otorisasi, User-Agent, cookies, dan bahkan pola perilaku.

Ketika skrip Anda melebihi batas, server mengembalikan salah satu dari respons berikut:

  • 429 Terlalu Banyak Permintaan โ€” status HTTP standar untuk pembatasan laju
  • 503 Layanan Tidak Tersedia โ€” kadang digunakan sebagai pengganti 429
  • 403 Terlarang โ€” jika IP sudah dimasukkan dalam daftar blokir
  • Respons kosong atau timeout โ€” saat pemblokiran agresif

Pemikiran pertama sebagian besar pengembang adalah menambahkan time.sleep(1) di antara permintaan. Ini hanya berfungsi pada batas yang sangat lunak (misalnya, 60 permintaan per menit). Namun, skenario nyata lebih rumit:

Batas nyata dari platform populer:

  • Twitter/X API (gratis): 500.000 tweet per bulan, tetapi tidak lebih dari 15 permintaan setiap 15 menit
  • Google Search: ~100 permintaan per hari dari satu IP tanpa otorisasi
  • Wildberries, Ozon: pembatasan laju agresif โ€” pemblokiran setelah 30โ€“50 permintaan per menit
  • GitHub API: 60 permintaan/jam tanpa token, 5000/jam dengan token
  • Situs yang dilindungi Cloudflare: dapat memblokir setelah 10โ€“20 permintaan per menit

Jika Anda perlu mengumpulkan 100.000 kartu produk dari marketplace atau memantau harga secara real-time โ€” penundaan tidak akan membantu. Anda memerlukan arsitektur lain. Dan di sinilah proxy menjadi keharusan, bukan pilihan.

Penting untuk dipahami: pembatasan laju terkait dengan alamat IP. Jika Anda memiliki 100 IP yang berbeda โ€” Anda pada dasarnya memiliki 100 "kuota" independen. Ini adalah prinsip kunci untuk menghindari pembatasan melalui proxy.

Bagaimana proxy menyelesaikan masalah pembatasan permintaan

Mekanismenya sederhana: setiap permintaan ke server target berasal dari alamat IP yang berbeda. Dari sudut pandang server โ€” ini adalah pengguna yang berbeda. Kuota masing-masing hampir tidak terpakai, sehingga pemblokiran tidak terjadi.

Mari kita lihat perbedaan antara bekerja tanpa proxy dan dengan kolam proxy pada contoh konkret. Misalkan server mengizinkan 10 permintaan per menit dari satu IP:

Skenario Permintaan per menit Pemblokiran Waktu untuk 10.000 permintaan
Satu IP, tanpa proxy 10 Ya, setelah 10 permintaan ~16 jam
10 proxy, rotasi 100 Tidak ~1.7 jam
100 proxy, rotasi 1000 Tidak ~10 menit

Selain meningkatkan kapasitas bandwidth, proxy juga memberikan beberapa keuntungan saat bekerja dengan pembatasan laju:

  • Isolasi sesi โ€” jika satu IP diblokir, yang lainnya tetap berfungsi
  • Penyebaran geografis โ€” permintaan datang dari berbagai wilayah, yang mengurangi kecurigaan
  • Sesi lengket โ€” kemampuan untuk "menempel" pada satu IP untuk skenario multi-langkah (otorisasi + tindakan)
  • Kontrol beban โ€” Anda dapat dengan tepat mendosis permintaan untuk setiap IP, tanpa melebihi batas

Jenis proxy mana yang harus dipilih untuk tugas Anda

Tidak semua proxy sama efektifnya melawan pembatasan laju. Pemilihan jenis tergantung pada situs target, volume permintaan, dan anggaran. Mari kita bahas tiga jenis utama:

Proxy Residensial

Ini adalah alamat IP dari pengguna rumah nyata. Mereka terlihat seperti lalu lintas internet biasa dan sangat jarang terkena pemblokiran. Proxy residensial adalah pilihan optimal untuk situs dengan perlindungan agresif: marketplace (Wildberries, Ozon), media sosial, sumber daya yang dilindungi Cloudflare. Kekurangan utama โ€” harga yang lebih tinggi dibandingkan dengan proxy pusat data.

Proxy Seluler

Alamat IP dari operator seluler (3G/4G/5G). Ciri khasnya โ€” satu IP dapat digunakan oleh ribuan pelanggan nyata secara bersamaan, sehingga situs sangat enggan memblokir alamat semacam itu. Proxy seluler menunjukkan hasil terbaik di tempat di mana proxy residensial mulai diblokir โ€” misalnya, saat pengambilan data Instagram frekuensi tinggi atau bekerja dengan API platform yang menganalisis jenis koneksi.

Proxy Pusat Data

IP cepat dan murah dari pusat data server. Sangat cocok untuk pengambilan data dari situs tanpa perlindungan serius: API terbuka, agregator berita, basis data publik. Untuk tugas dengan pembatasan laju, mereka diperlukan lebih banyak (karena mereka lebih sering masuk dalam daftar blokir), tetapi dengan rotasi yang tepat, mereka dapat menangani volume permintaan yang besar dengan baik. Lebih lanjut โ€” di halaman proxy pusat data.

Jenis Proxy Anonimitas Kecepatan Harga Skenario Terbaik
Residensial Sangat Tinggi Sedang $$ Marketplace, media sosial, Cloudflare
Seluler Maksimal Sedang $$$ API Instagram, pengambilan data frekuensi tinggi
Pusat Data Sedang Tinggi $ API terbuka, data publik

Strategi rotasi IP: per-permintaan, sesi lengket, round-robin

Hanya memiliki proxy tidak menyelesaikan masalah โ€” penting untuk mengelolanya dengan benar. Ada beberapa strategi rotasi, masing-masing cocok untuk skenario tertentu.

Rotasi per-permintaan (IP baru untuk setiap permintaan)

Setiap permintaan HTTP dilakukan melalui alamat IP baru. Ini adalah strategi paling agresif untuk menghindari pembatasan laju โ€” server secara fisik tidak memiliki waktu untuk mengumpulkan hitungan untuk satu IP. Cocok untuk:

  • Pengambilan kartu produk (setiap kartu โ€” permintaan terpisah)
  • Pengumpulan data dari mesin pencari
  • Permintaan stateless apa pun yang tidak memerlukan sesi

Sesi lengket (IP tetap untuk sesi)

Satu IP digunakan sepanjang sesi (biasanya 1โ€“30 menit). Ini sangat penting untuk skenario yang memerlukan otorisasi: masuk ke akun, melakukan tindakan, keluar. Jika IP berubah di antara langkah-langkah โ€” server dapat memblokir sesi sebagai mencurigakan.

Round-robin dengan batas permintaan per IP

Strategi yang paling tepat. Anda mengetahui batas server (misalnya, 10 permintaan per menit) dan mendistribusikan permintaan di kolam proxy sehingga setiap IP tidak pernah melebihi ambang ini. Memerlukan implementasi antrean dengan mempertimbangkan waktu permintaan terakhir untuk setiap IP.

Rumus untuk menghitung jumlah proxy yang diperlukan:

N proxy = (Kecepatan target permintaan/menit) รท (Batas server/menit per IP)
Contoh: perlu 500 permintaan/menit, batas server โ€” 10/menit โ†’ perlu setidaknya 50 proxy. Tambahkan 20% cadangan untuk kemungkinan pemblokiran: total 60 proxy.

Contoh kode Python: requests, aiohttp, Scrapy

Mari kita beralih ke praktik. Berikut adalah template siap pakai untuk tiga alat Python yang paling populer.

1. requests + rotasi proxy secara manual

Versi paling sederhana โ€” daftar proxy dan pemilihan acak untuk setiap permintaan:

import requests
import random
import time

PROXIES = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    # ... tambahkan jumlah yang diperlukan
]

def get_random_proxy():
    proxy = random.choice(PROXIES)
    return {"http": proxy, "https": proxy}

def fetch_with_retry(url, max_retries=3):
    for attempt in range(max_retries):
        proxy = get_random_proxy()
        try:
            response = requests.get(
                url,
                proxies=proxy,
                timeout=10,
                headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}
            )
            if response.status_code == 429:
                print(f"Terbatas pada {proxy}, beralih...")
                time.sleep(1)
                continue
            return response
        except requests.RequestException as e:
            print(f"Percobaan {attempt+1} gagal: {e}")
            time.sleep(2)
    return None

# Penggunaan
urls = ["https://example.com/item/1", "https://example.com/item/2"]
for url in urls:
    result = fetch_with_retry(url)
    if result:
        print(f"OK: {url} โ€” {len(result.text)} bytes")

2. Kolam proxy cerdas dengan mempertimbangkan batas laju

Versi yang lebih canggih โ€” kelas ProxyPool, yang melacak waktu penggunaan terakhir setiap IP dan tidak melebihi batas yang ditetapkan:

import requests
import time
from collections import defaultdict
from threading import Lock

class ProxyPool:
    def __init__(self, proxies, rate_limit=10, window=60):
        """
        proxies: daftar string dalam format 'http://user:pass@host:port'
        rate_limit: maksimum permintaan dari satu IP dalam window detik
        window: jendela waktu dalam detik
        """
        self.proxies = proxies
        self.rate_limit = rate_limit
        self.window = window
        self.usage = defaultdict(list)  # proxy -> [timestamps]
        self.lock = Lock()

    def get_available_proxy(self):
        now = time.time()
        with self.lock:
            for proxy in self.proxies:
                # Menghapus cap yang sudah kedaluwarsa
                self.usage[proxy] = [
                    t for t in self.usage[proxy]
                    if now - t < self.window
                ]
                if len(self.usage[proxy]) < self.rate_limit:
                    self.usage[proxy].append(now)
                    return {"http": proxy, "https": proxy}
        return None  # Semua proxy telah mencapai batas

    def fetch(self, url, **kwargs):
        proxy = self.get_available_proxy()
        if proxy is None:
            print("Semua proxy dibatasi, menunggu...")
            time.sleep(5)
            return self.fetch(url, **kwargs)
        
        try:
            response = requests.get(url, proxies=proxy, timeout=10, **kwargs)
            return response
        except requests.RequestException as e:
            print(f"Permintaan gagal: {e}")
            return None

# Penggunaan
pool = ProxyPool(
    proxies=[
        "http://user:[email protected]:8080",
        "http://user:[email protected]:8080",
    ],
    rate_limit=10,  # 10 permintaan per menit per IP
    window=60
)

for i in range(100):
    r = pool.fetch(f"https://example.com/page/{i}")
    if r:
        print(f"Halaman {i}: {r.status_code}")

3. aiohttp untuk pengambilan data asinkron

Pendekatan asinkron memungkinkan penggunaan puluhan proxy secara bersamaan tanpa memblokir thread:

import asyncio
import aiohttp
import itertools

PROXIES = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
]

proxy_cycle = itertools.cycle(PROXIES)

async def fetch(session, url, proxy):
    try:
        async with session.get(
            url,
            proxy=proxy,
            timeout=aiohttp.ClientTimeout(total=10)
        ) as response:
            if response.status == 429:
                await asyncio.sleep(2)
                return None
            return await response.text()
    except Exception as e:
        print(f"Kesalahan: {e}")
        return None

async def main(urls):
    connector = aiohttp.TCPConnector(limit=50)
    async with aiohttp.ClientSession(connector=connector) as session:
        tasks = [
            fetch(session, url, next(proxy_cycle))
            for url in urls
        ]
        results = await asyncio.gather(*tasks)
        return results

urls = [f"https://example.com/item/{i}" for i in range(200)]
results = asyncio.run(main(urls))
print(f"Dikumpulkan: {sum(1 for r in results if r is not None)} halaman")

4. Scrapy dengan rotasi melalui middleware

Untuk Scrapy ada solusi siap pakai โ€” scrapy-rotating-proxies. Namun Anda juga dapat menulis middleware sendiri:

# middlewares.py
import random

class RotatingProxyMiddleware:
    def __init__(self, proxies):
        self.proxies = proxies

    @classmethod
    def from_crawler(cls, crawler):
        return cls(proxies=crawler.settings.getlist("PROXY_LIST"))

    def process_request(self, request, spider):
        proxy = random.choice(self.proxies)
        request.meta["proxy"] = proxy

    def process_response(self, request, response, spider):
        if response.status == 429:
            spider.logger.warning(f"Dibatasi, proxy: {request.meta.get('proxy')}")
            # Anda dapat menambahkan logika untuk mengecualikan proxy yang bermasalah
        return response

# settings.py
PROXY_LIST = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
]
DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.RotatingProxyMiddleware": 350,
}
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 10

Contoh kode Node.js: axios, got, Puppeteer

Node.js adalah pilihan populer untuk otomatisasi browser dan bekerja dengan API. Berikut adalah pola siap pakai untuk bekerja dengan proxy.

1. axios dengan rotasi proxy

const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');

const proxies = [
  'http://user:[email protected]:8080',
  'http://user:[email protected]:8080',
  'http://user:[email protected]:8080',
];

let proxyIndex = 0;

function getNextProxy() {
  const proxy = proxies[proxyIndex % proxies.length];
  proxyIndex++;
  return proxy;
}

async function fetchWithProxy(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const proxyUrl = getNextProxy();
    const agent = new HttpsProxyAgent(proxyUrl);
    
    try {
      const response = await axios.get(url, {
        httpsAgent: agent,
        httpAgent: agent,
        timeout: 10000,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
        },
      });
      return response.data;
    } catch (error) {
      if (error.response?.status === 429) {
        console.log(`Dibatasi, beralih proxy...`);
        await new Promise(r => setTimeout(r, 1000));
        continue;
      }
      console.error(`Percobaan ${i + 1} gagal:`, error.message);
    }
  }
  return null;
}

// Penggunaan
(async () => {
  const urls = Array.from({length: 50}, (_, i) => `https://example.com/item/${i}`);
  
  const results = await Promise.allSettled(
    urls.map(url => fetchWithProxy(url))
  );
  
  const successful = results.filter(r => r.status === 'fulfilled' && r.value).length;
  console.log(`Sukses: ${successful}/${urls.length}`);
})();

2. Puppeteer dengan proxy dan menghindari pembatasan laju

Untuk situs dengan rendering JavaScript dan perlindungan Cloudflare, diperlukan browser headless:

const puppeteer = require('puppeteer');

const proxies = [
  'proxy1.example.com:8080',
  'proxy2.example.com:8080',
];

async function scrapeWithProxy(url, proxyHost) {
  const browser = await puppeteer.launch({
    args: [
      `--proxy-server=${proxyHost}`,
      '--no-sandbox',
      '--disable-setuid-sandbox',
    ],
    headless: true,
  });

  const page = await browser.newPage();
  
  // Otorisasi proxy
  await page.authenticate({
    username: 'user',
    password: 'pass',
  });

  // Mengatur User-Agent yang realistis
  await page.setUserAgent(
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
  );

  try {
    await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
    
    // Memeriksa pembatasan laju
    const status = await page.evaluate(() => document.title);
    if (status.includes('429') || status.includes('Terlalu Banyak')) {
      console.log('Dibatasi, perlu mengganti proxy');
      return null;
    }
    
    const data = await page.evaluate(() => {
      return document.querySelector('.price')?.textContent || null;
    });
    
    return data;
  } finally {
    await browser.close();
  }
}

// Rotasi berdasarkan tugas
(async () => {
  const urls = ['https://example.com/product/1', 'https://example.com/product/2'];
  
  for (let i = 0; i < urls.length; i++) {
    const proxy = proxies[i % proxies.length];
    const result = await scrapeWithProxy(urls[i], proxy);
    console.log(`${urls[i]}: ${result}`);
    await new Promise(r => setTimeout(r, 500)); // penundaan kecil
  }
})();

Teknik lanjutan: header, sidik jari, menghindari Cloudflare

Mengganti IP adalah syarat yang diperlukan, tetapi tidak selalu cukup. Sistem perlindungan modern menganalisis puluhan parameter permintaan. Mari kita bahas apa lagi yang perlu dipertimbangkan.

Header HTTP: set minimum yang wajib

Permintaan tanpa header yang normal terlihat seperti bot bahkan saat mengganti IP. Selalu tambahkan:

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language": "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
    "Accept-Encoding": "gzip, deflate, br",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Cache-Control": "max-age=0",
}

Pengolahan header Retry-After

Saat merespons 429, server sering menunjukkan berapa lama harus menunggu. Pengolahan header ini dengan benar memungkinkan Anda untuk tidak membuang permintaan secara sia-sia:

def handle_rate_limit(response):
    if response.status_code == 429:
        retry_after = response.headers.get("Retry-After")
        if retry_after:
            wait_time = int(retry_after)
            print(f"Dibatasi. Menunggu {wait_time} detik...")
            time.sleep(wait_time + 1)  # +1 detik buffer
        else:
            # Penundaan eksponensial jika header tidak ada
            time.sleep(min(2 ** attempt, 60))
        return True
    return False

Sidik jari TLS dan cara menghindarinya

Sistem canggih (Cloudflare, Akamai, PerimeterX) menganalisis sidik jari TLS โ€” "jejak" unik dari koneksi TLS Anda. Perpustakaan standar requests memiliki sidik jari yang mudah dikenali. Solusi:

  • curl_cffi (Python) โ€” meniru sidik jari Chrome/Firefox pada tingkat TLS
  • tls-client (Go/Python) โ€” alat serupa dengan dukungan untuk berbagai profil browser
  • Playwright/Puppeteer โ€” browser nyata, sidik jari yang ideal secara default
# pip install curl-cffi
from curl_cffi import requests as cffi_requests

response = cffi_requests.get(
    "https://cloudflare-protected-site.com/api/data",
    impersonate="chrome120",  # Meniru Chrome 120
    proxies={"https": "http://user:[email protected]:8080"}
)
print(response.json())

Mengelola cookies dan sesi

Jika situs menggunakan cookies untuk melacak sesi, mengganti IP tanpa mengganti cookies adalah sia-sia. Saat beralih proxy, selalu buat sesi baru:

import requests

def create_fresh_session(proxy_url):
    """Membuat sesi baru dengan cookies bersih untuk setiap proxy"""
    session = requests.Session()
    session.proxies = {"http": proxy_url, "https": proxy_url}
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
    })
    # Cookies tidak dibawa dari sesi sebelumnya
    return session

# Untuk setiap IP baru โ€” sesi baru
for proxy in proxies:
    session = create_fresh_session(proxy)
    response = session.get("https://example.com/protected-page")
    # Mengolah respons...

Kesalahan umum saat bekerja dengan proxy dan pembatasan laju

Bahkan dengan proxy yang dikonfigurasi dengan benar, pengembang sering kali mengalami kesalahan yang sama. Berikut adalah kesalahan yang paling umum dan cara menghindarinya.

Daftar Periksa: apa yang perlu diperiksa sebelum menjalankan pengambil data

  • โ˜ Header HTTP yang realistis ditambahkan (User-Agent, Accept, Accept-Language)
  • โ˜ Saat mengganti proxy, sesi baru dibuat (cookies baru)
  • โ˜ Status 429, 503, 403 diproses dengan logika retry
  • โ˜ Penundaan antara permintaan diimplementasikan (setidaknya 100โ€“500 ms)
  • โ˜ Jumlah proxy sesuai dengan kecepatan target permintaan
  • โ˜ Proxy diuji sebelum memulai (health check)
  • โ˜ Kesalahan dan statistik dicatat untuk setiap proxy
  • โ˜ Timeout untuk permintaan diatur (tidak lebih dari 15โ€“30 detik)

Kesalahan 1: Menggunakan proxy "mati"

Selalu periksa proxy sebelum menambahkannya ke kolam dan secara berkala selama operasi. Satu proxy yang tidak berfungsi dalam siklus โ€” itu adalah permintaan yang hilang dan timeout:

def check_proxy(proxy_url, test_url="https://httpbin.org/ip", timeout=5):
    try:
        r = requests.get(
            test_url,
            proxies={"http": proxy_url, "https": proxy_url},
            timeout=timeout
        )
        return r.status_code == 200
    except:
        return False

# Menyaring proxy yang berfungsi sebelum memulai
working_proxies = [p for p in PROXIES if check_proxy(p)]
print(f"Proxy yang berfungsi: {len(working_proxies)}/{len(PROXIES)}")

Kesalahan 2: Mengabaikan jenis protokol

Proxy HTTP tidak dapat memproksikan lalu lintas HTTPS secara langsung (hanya melalui CONNECT). Proxy SOCKS5 bekerja pada tingkat transportasi dan mendukung semua protokol. Untuk sebagian besar situs modern, gunakan proxy SOCKS5 atau HTTPS:

# Proxy SOCKS5 di requests (memerlukan pip install requests[socks])
proxies = {
    "http": "socks5://user:[email protected]:1080",
    "https": "socks5://user:[email protected]:1080",
}

# Proxy HTTPS
proxies = {
    "http": "https://user:[email protected]:8080",
    "https": "https://user:[email protected]:8080",
}

Kesalahan 3: Tidak adanya penundaan eksponensial

Jika setelah 429 Anda segera mengulangi permintaan โ€” Anda hanya memperburuk situasi. Strategi yang benar adalah penundaan eksponensial dengan jitter (penyimpangan acak):

import random

def exponential_backoff(attempt, base=1, max_wait=60):
    """
    attempt: nomor percobaan (dimulai dari 0)
    base: penundaan dasar dalam detik
    max_wait: penundaan maksimum
    """
    wait = min(base * (2 ** attempt), max_wait)
    # Jitter ยฑ25% untuk mencegah thundering herd
    jitter = wait * 0.25 * random.uniform(-1, 1)
    return wait + jitter

# Penggunaan dalam logika retry
for attempt in range(5):
    response = requests.get(url, proxies=proxy)
    if response.status_code == 429:
        wait = exponential_backoff(attempt)
        print(f"Dibatasi. Menunggu {wait:.1f}s (percobaan {attempt+1})")
        time.sleep(wait)
    else:
        break

Kesalahan 4: Satu thread untuk semua proxy

Jika Anda memiliki 50 proxy, tetapi satu thread eksekusi โ€” Anda hanya menggunakan maksimum 1 proxy pada satu waktu. Gunakan ThreadPoolExecutor atau pendekatan asinkron untuk menggunakan seluruh kolam secara paralel:

from concurrent.futures import ThreadPoolExecutor, as_completed

def fetch_url(args):
    url, proxy = args
    try:
        r = requests.get(url, proxies={"https": proxy}, timeout=10)
        return url, r.status_code, len(r.text)
    except Exception as e:
        return url, None, str(e)

# Menggunakan semua proxy secara paralel
tasks = [(url, proxies[i % len(proxies)]) for i, url in enumerate(urls)]

with ThreadPoolExecutor(max_workers=len(proxies)) as executor:
    futures = {executor.submit(fetch_url, task): task for task in tasks}
    for future in as_completed(futures):
        url, status, size = future.result()
        print(f"{url}: {status} ({size})")

Kesimpulan dan rekomendasi

Pembatasan laju adalah masalah yang dapat diatasi jika ditangani secara sistematis. Kesimpulan kunci dari panduan ini:

  • Kolam proxy, bukan satu proxy โ€” unit minimum untuk pekerjaan serius. Jumlah proxy ditentukan dengan rumus: kecepatan target รท batas server per IP.
  • Strategi rotasi itu penting โ€” per-permintaan untuk permintaan stateless, sesi lengket untuk skenario yang terotorisasi.
  • IP bukan satu-satunya parameter โ€” header, cookies, sidik jari TLS, dan pola perilaku juga dianalisis oleh sistem perlindungan.
  • Proses 429 dengan benar โ€” penundaan eksponensial, header Retry-After, mengganti proxy saat diblokir.
  • Jenis proxy tergantung pada tujuan โ€” proxy pusat data untuk API terbuka, proxy residensial untuk marketplace, proxy seluler untuk perlindungan maksimal.

Jika Anda bekerja dengan pengambilan data dari marketplace (Wildberries, Ozon), pengumpulan data dari API yang dilindungi, atau otomatisasi dengan kecepatan tinggi โ€” kami merekomendasikan untuk mulai dengan proxy residensial: mereka memberikan keseimbangan optimal antara anonimitas dan kecepatan, dan alamat IP mereka hampir tidak pernah masuk dalam daftar blokir. Untuk tugas yang memerlukan ketahanan maksimum terhadap pemblokiran dengan frekuensi permintaan tinggi, pertimbangkan proxy seluler โ€” IP mereka dibagi oleh ribuan pengguna nyata, sehingga pemblokiran sangat tidak diinginkan untuk situs mana pun.

```