Quay lại blog

Cách vượt qua giới hạn tỷ lệ bằng proxy: kiến trúc, xoay vòng IP và ví dụ mã cho lập trình viên

Giới hạn tỷ lệ chặn trình phân tích hoặc khách hàng API của bạn? Chúng ta sẽ xem xét cách vượt qua giới hạn số lượng yêu cầu bằng cách sử dụng proxy - với các ví dụ mã và giải pháp kiến trúc.

📅12 tháng 5, 2026
```html

Giới hạn tỷ lệ (rate limiting) là một trong những lý do phổ biến nhất khiến các trình phân tích bị sập, các tích hợp API bị ngắt quãng, và các kịch bản tự động nhận được trạng thái 429 Too Many Requests. Máy chủ thấy quá nhiều yêu cầu từ một IP - và đơn giản là ngừng phản hồi. Trong bài viết này, chúng ta sẽ xem xét cách xây dựng cơ sở hạ tầng trên proxy một cách chính xác để vượt qua giới hạn yêu cầu mà không bị cấm và không bị gián đoạn - với các ví dụ mã thực tế bằng Python và Node.js.

Giới hạn tỷ lệ là gì và tại sao các độ trễ thông thường không giúp ích

Giới hạn tỷ lệ (rate limiting) là cơ chế bảo vệ máy chủ, giới hạn số lượng yêu cầu từ một nguồn trong một khoảng thời gian nhất định. Nguồn thường là địa chỉ IP, nhưng các hệ thống tiên tiến cũng xem xét các mã thông báo xác thực, User-Agent, cookies và thậm chí là các mẫu hành vi.

Khi kịch bản của bạn vượt quá giới hạn, máy chủ sẽ trả về một trong các phản hồi sau:

  • 429 Too Many Requests — trạng thái HTTP tiêu chuẩn cho giới hạn tỷ lệ
  • 503 Service Unavailable — đôi khi được sử dụng thay cho 429
  • 403 Forbidden — nếu IP đã bị đưa vào danh sách đen
  • Phản hồi trống hoặc timeout — khi bị chặn một cách quyết liệt

Suy nghĩ đầu tiên của hầu hết các nhà phát triển là thêm time.sleep(1) giữa các yêu cầu. Điều này chỉ hoạt động khi giới hạn rất nhẹ (ví dụ: 60 yêu cầu mỗi phút). Nhưng các kịch bản thực tế phức tạp hơn:

Giới hạn thực tế của các nền tảng phổ biến:

  • Twitter/X API (miễn phí): 500.000 tweet mỗi tháng, nhưng không quá 15 yêu cầu mỗi 15 phút
  • Tìm kiếm Google: ~100 yêu cầu mỗi ngày từ một IP mà không cần xác thực
  • Wildberries, Ozon: giới hạn tỷ lệ quyết liệt — chặn sau 30–50 yêu cầu mỗi phút
  • GitHub API: 60 yêu cầu/giờ không có mã thông báo, 5000/giờ với mã thông báo
  • Các trang web được bảo vệ bởi Cloudflare: có thể chặn ngay sau 10–20 yêu cầu mỗi phút

Nếu bạn cần thu thập 100.000 thẻ sản phẩm từ một thị trường hoặc theo dõi giá theo thời gian thực — các độ trễ đơn giản không giúp ích. Cần một kiến trúc khác. Và chính tại đây, proxy trở thành một điều cần thiết, không phải là một tùy chọn.

Quan trọng là phải hiểu: giới hạn tỷ lệ gắn liền với địa chỉ IP. Nếu bạn có 100 IP khác nhau — bạn thực sự có 100 "quota" độc lập. Đây là nguyên tắc chính để vượt qua các giới hạn thông qua proxy.

Proxy giải quyết vấn đề giới hạn yêu cầu như thế nào

Cơ chế rất đơn giản: mỗi yêu cầu đến máy chủ mục tiêu được gửi từ một địa chỉ IP khác nhau. Từ góc độ của máy chủ — đây là những người dùng khác nhau. Quota của mỗi người trong số họ gần như không bị tiêu hao, vì vậy việc chặn không xảy ra.

Hãy xem sự khác biệt giữa việc làm việc không có proxy và với một nhóm proxy trong một ví dụ cụ thể. Giả sử máy chủ cho phép 10 yêu cầu mỗi phút từ một IP:

Kịch bản Yêu cầu mỗi phút Chặn Thời gian cho 10.000 yêu cầu
Một IP, không có proxy 10 Có, sau 10 yêu cầu ~16 giờ
10 proxy, xoay vòng 100 Không ~1.7 giờ
100 proxy, xoay vòng 1000 Không ~10 phút

Ngoài việc mở rộng băng thông, proxy còn mang lại một số lợi ích khác khi làm việc với giới hạn tỷ lệ:

  • Cách ly phiên — nếu một IP bị cấm, các IP khác vẫn tiếp tục hoạt động
  • Phân phối địa lý — các yêu cầu đến từ các khu vực khác nhau, điều này giảm thiểu sự nghi ngờ
  • Sticky sessions — khả năng "dính" vào một IP cho các kịch bản nhiều bước (xác thực + hành động)
  • Kiểm soát tải — có thể chính xác điều chỉnh số lượng yêu cầu cho mỗi IP mà không vượt quá giới hạn

Loại proxy nào nên chọn cho nhiệm vụ của bạn

Không phải tất cả các proxy đều hiệu quả như nhau đối với giới hạn tỷ lệ. Việc chọn loại phụ thuộc vào trang web mục tiêu, khối lượng yêu cầu và ngân sách. Chúng ta sẽ xem xét ba loại chính:

Proxy cư trú

Đây là các địa chỉ IP của người dùng thực tế tại nhà. Chúng trông giống như lưu lượng internet thông thường và rất hiếm khi bị chặn. Proxy cư trú là lựa chọn tối ưu cho các trang web có bảo vệ quyết liệt: thị trường (Wildberries, Ozon), mạng xã hội, các tài nguyên được bảo vệ bởi Cloudflare. Nhược điểm chính là giá cao hơn so với proxy từ trung tâm dữ liệu.

Proxy di động

Các địa chỉ IP của các nhà mạng di động (3G/4G/5G). Đặc điểm của chúng là một IP có thể được sử dụng bởi hàng nghìn người dùng thực tế cùng một lúc, vì vậy các trang web rất không muốn chặn địa chỉ này. Proxy di động cho kết quả tốt nhất ở những nơi mà proxy cư trú đã bắt đầu bị chặn — ví dụ, khi phân tích Instagram với tần suất cao hoặc làm việc với API của các nền tảng phân tích loại kết nối.

Proxy từ trung tâm dữ liệu

Các IP nhanh và rẻ từ các trung tâm dữ liệu máy chủ. Chúng rất phù hợp cho việc phân tích các trang web không có bảo vệ nghiêm ngặt: API mở, các tập hợp tin tức, cơ sở dữ liệu công khai. Đối với các nhiệm vụ có giới hạn tỷ lệ, cần nhiều proxy hơn (vì chúng thường bị đưa vào danh sách đen), nhưng với việc xoay vòng đúng cách, chúng có thể xử lý khối lượng yêu cầu lớn một cách xuất sắc. Tìm hiểu thêm trên trang proxy từ trung tâm dữ liệu.

Loại proxy Ẩn danh Tốc độ Giá Kịch bản tốt nhất
Cư trú Rất cao Trung bình $$ Thị trường, mạng xã hội, Cloudflare
Di động Tối đa Trung bình $$$ Instagram API, phân tích tần suất cao
Trung tâm dữ liệu Trung bình Cao $ API mở, dữ liệu công khai

Chiến lược xoay vòng IP: per-request, sticky sessions, round-robin

Việc có proxy chưa đủ để giải quyết vấn đề — quan trọng là phải quản lý chúng một cách chính xác. Có một số chiến lược xoay vòng, mỗi chiến lược phù hợp với các kịch bản riêng.

Xoay vòng per-request (IP mới cho mỗi yêu cầu)

Mỗi yêu cầu HTTP được gửi qua một địa chỉ IP mới. Đây là chiến lược tối đa để vượt qua giới hạn tỷ lệ — máy chủ không kịp tích lũy bộ đếm cho một IP. Phù hợp cho:

  • Phân tích thẻ sản phẩm (mỗi thẻ là một yêu cầu riêng)
  • Thu thập dữ liệu từ các công cụ tìm kiếm
  • Bất kỳ yêu cầu stateless nào không yêu cầu phiên

Sticky sessions (IP cố định cho một phiên)

Một IP được sử dụng trong suốt phiên (thường là 1–30 phút). Điều này rất quan trọng cho các kịch bản cần xác thực: đăng nhập vào tài khoản, thực hiện hành động, đăng xuất. Nếu IP thay đổi giữa các bước — máy chủ có thể chặn phiên như một phiên đáng ngờ.

Round-robin với giới hạn yêu cầu trên IP

Chiến lược chính xác nhất. Bạn biết giới hạn của máy chủ (ví dụ: 10 yêu cầu mỗi phút) và phân phối các yêu cầu qua nhóm proxy sao cho mỗi IP không bao giờ vượt quá ngưỡng này. Cần thực hiện hàng đợi với thời gian của yêu cầu cuối cùng cho mỗi IP.

Công thức tính số lượng proxy cần thiết:

N proxy = (Tốc độ yêu cầu mục tiêu/phút) ÷ (Giới hạn của máy chủ/phút trên IP)
Ví dụ: cần 500 yêu cầu/phút, giới hạn của máy chủ — 10/phút → cần tối thiểu 50 proxy. Thêm 20% dự phòng cho trường hợp bị chặn: tổng cộng 60 proxy.

Ví dụ mã bằng Python: requests, aiohttp, Scrapy

Chúng ta hãy chuyển sang thực hành. Dưới đây là các mẫu mã sẵn có cho ba công cụ Python phổ biến nhất.

1. requests + xoay vòng proxy thủ công

Phiên bản đơn giản nhất — danh sách proxy và chọn ngẫu nhiên cho mỗi yêu cầu:

import requests
import random
import time

PROXIES = [
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    "http://user:[email protected]:8080",
    # ... thêm số lượng cần thiết
]

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"Bị giới hạn tỷ lệ trên {proxy}, chuyển đổi...")
                time.sleep(1)
                continue
            return response
        except requests.RequestException as e:
            print(f"Cố gắng {attempt+1} thất bại: {e}")
            time.sleep(2)
    return None

# Sử dụng
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. Nhóm proxy thông minh với giới hạn tỷ lệ

Phiên bản nâng cao hơn — lớp ProxyPool, theo dõi thời gian sử dụng cuối cùng của mỗi IP và không vượt quá giới hạn đã đặt:

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

class ProxyPool:
    def __init__(self, proxies, rate_limit=10, window=60):
        """
        proxies: danh sách chuỗi dạng 'http://user:pass@host:port'
        rate_limit: số lượng yêu cầu tối đa từ một IP trong một khoảng thời gian
        window: khoảng thời gian tính bằng giây
        """
        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:
                # Xóa các nhãn đã hết hạn
                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  # Tất cả các proxy đã hết giới hạn

    def fetch(self, url, **kwargs):
        proxy = self.get_available_proxy()
        if proxy is None:
            print("Tất cả các proxy đều bị giới hạn tỷ lệ, đang chờ...")
            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"Yêu cầu thất bại: {e}")
            return None

# Sử dụng
pool = ProxyPool(
    proxies=[
        "http://user:[email protected]:8080",
        "http://user:[email protected]:8080",
    ],
    rate_limit=10,  # 10 yêu cầu mỗi phút trên IP
    window=60
)

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

3. aiohttp cho phân tích bất đồng bộ

Cách tiếp cận bất đồng bộ cho phép sử dụng hàng chục proxy song song mà không chặn các luồng:

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"Lỗi: {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"Đã thu thập: {sum(1 for r in results if r is not None)} trang")

4. Scrapy với xoay vòng qua middleware

Đối với Scrapy, có một giải pháp sẵn có — scrapy-rotating-proxies. Nhưng bạn cũng có thể viết middleware riêng của mình:

# 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"Bị giới hạn tỷ lệ, proxy: {request.meta.get('proxy')}")
            # Có thể thêm logic để loại bỏ proxy gặp vấn đề
        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

Ví dụ mã bằng Node.js: axios, got, Puppeteer

Node.js là lựa chọn phổ biến cho việc tự động hóa trình duyệt và làm việc với API. Dưới đây là các mẫu mã sẵn có để làm việc với proxy.

1. axios với xoay vòng 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(`Bị giới hạn tỷ lệ, chuyển đổi proxy...`);
        await new Promise(r => setTimeout(r, 1000));
        continue;
      }
      console.error(`Cố gắng ${i + 1} thất bại:`, error.message);
    }
  }
  return null;
}

// Sử dụng
(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(`Thành công: ${successful}/${urls.length}`);
})();

2. Puppeteer với proxy và vượt qua giới hạn tỷ lệ

Đối với các trang web có kết xuất JavaScript và bảo vệ Cloudflare, cần một trình duyệt 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();
  
  // Xác thực proxy
  await page.authenticate({
    username: 'user',
    password: 'pass',
  });

  // Thiết lập User-Agent thực tế
  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 });
    
    // Kiểm tra giới hạn tỷ lệ
    const status = await page.evaluate(() => document.title);
    if (status.includes('429') || status.includes('Too Many')) {
      console.log('Bị giới hạn tỷ lệ, cần chuyển đổi proxy');
      return null;
    }
    
    const data = await page.evaluate(() => {
      return document.querySelector('.price')?.textContent || null;
    });
    
    return data;
  } finally {
    await browser.close();
  }
}

// Xoay vòng theo nhiệm vụ
(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)); // độ trễ nhỏ
  }
})();

Kỹ thuật nâng cao: tiêu đề, fingerprint, vượt qua Cloudflare

Thay đổi IP là điều cần thiết, nhưng không phải lúc nào cũng đủ. Các hệ thống bảo vệ hiện đại phân tích hàng chục tham số của yêu cầu. Chúng ta sẽ xem xét những gì cần lưu ý thêm.

Tiêu đề HTTP: bộ tối thiểu cần thiết

Yêu cầu không có các tiêu đề bình thường trông giống như bot ngay cả khi thay đổi IP. Luôn luôn thêm:

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",
}

Xử lý tiêu đề Retry-After

Khi phản hồi 429, máy chủ thường chỉ định thời gian cần chờ. Việc xử lý đúng tiêu đề này cho phép không lãng phí yêu cầu:

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"Bị giới hạn tỷ lệ. Đang chờ {wait_time} giây...")
            time.sleep(wait_time + 1)  # +1 giây dự phòng
        else:
            # Đợi theo kiểu tăng dần nếu không có tiêu đề
            time.sleep(min(2 ** attempt, 60))
        return True
    return False

TLS fingerprinting và cách vượt qua nó

Các hệ thống tiên tiến (Cloudflare, Akamai, PerimeterX) phân tích fingerprint TLS — "dấu vân tay" độc nhất của kết nối TLS của bạn. Thư viện tiêu chuẩn requests có fingerprint dễ nhận biết. Các giải pháp:

  • curl_cffi (Python) — mô phỏng fingerprint Chrome/Firefox ở cấp độ TLS
  • tls-client (Go/Python) — công cụ tương tự với hỗ trợ nhiều hồ sơ trình duyệt khác nhau
  • Playwright/Puppeteer — trình duyệt thực, fingerprint lý tưởng theo mặc định
# 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",  # Mô phỏng Chrome 120
    proxies={"https": "http://user:[email protected]:8080"}
)
print(response.json())

Quản lý cookies và phiên

Nếu trang web sử dụng cookies để theo dõi các phiên, việc thay đổi IP mà không thay đổi cookies là vô nghĩa. Khi chuyển đổi proxy, luôn tạo một phiên mới:

import requests

def create_fresh_session(proxy_url):
    """Tạo một phiên mới với cookies sạch cho mỗi 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 không được chuyển từ phiên trước
    return session

# Đối với mỗi IP mới — một phiên mới
for proxy in proxies:
    session = create_fresh_session(proxy)
    response = session.get("https://example.com/protected-page")
    # Xử lý phản hồi...

Những sai lầm phổ biến khi làm việc với proxy và giới hạn tỷ lệ

Ngay cả khi đã cấu hình đúng proxy, các nhà phát triển vẫn thường mắc phải những sai lầm giống nhau. Dưới đây là những sai lầm phổ biến nhất và cách tránh chúng.

Danh sách kiểm tra: những gì cần kiểm tra trước khi khởi động trình phân tích

  • ☐ Đã thêm các tiêu đề HTTP thực tế (User-Agent, Accept, Accept-Language)
  • ☐ Khi thay đổi proxy, tạo một phiên mới (cookies mới)
  • ☐ Xử lý các trạng thái 429, 503, 403 với logic retry
  • ☐ Thực hiện độ trễ giữa các yêu cầu (ít nhất 100–500 ms)
  • ☐ Số lượng proxy tương ứng với tốc độ yêu cầu mục tiêu
  • ☐ Kiểm tra hoạt động của proxy trước khi khởi động (kiểm tra sức khỏe)
  • ☐ Ghi lại lỗi và thống kê cho mỗi proxy
  • ☐ Đã thiết lập timeout cho các yêu cầu (không quá 15–30 giây)

Sai lầm 1: Sử dụng proxy "chết"

Luôn kiểm tra proxy trước khi thêm vào nhóm và định kỳ trong quá trình làm việc. Một proxy không hoạt động trong vòng lặp — đó là các yêu cầu bị mất và 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

# Lọc các proxy hoạt động trước khi khởi động
working_proxies = [p for p in PROXIES if check_proxy(p)]
print(f"Các proxy hoạt động: {len(working_proxies)}/{len(PROXIES)}")

Sai lầm 2: Bỏ qua loại giao thức

Proxy HTTP không thể chuyển tiếp lưu lượng HTTPS trực tiếp (chỉ qua CONNECT). Proxy SOCKS5 hoạt động ở cấp độ vận chuyển và hỗ trợ bất kỳ giao thức nào. Đối với hầu hết các trang web hiện đại, hãy sử dụng proxy SOCKS5 hoặc HTTPS:

# Proxy SOCKS5 trong requests (cần 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",
}

Sai lầm 3: Thiếu backoff theo kiểu tăng dần

Nếu sau 429 bạn ngay lập tức lặp lại yêu cầu — bạn chỉ làm tình hình tồi tệ hơn. Chiến lược đúng là độ trễ theo kiểu tăng dần với jitter (biến động ngẫu nhiên):

import random

def exponential_backoff(attempt, base=1, max_wait=60):
    """
    attempt: số lần thử (bắt đầu từ 0)
    base: độ trễ cơ bản tính bằng giây
    max_wait: độ trễ tối đa
    """
    wait = min(base * (2 ** attempt), max_wait)
    # Jitter ±25% để ngăn chặn thundering herd
    jitter = wait * 0.25 * random.uniform(-1, 1)
    return wait + jitter

# Sử dụng trong logic retry
for attempt in range(5):
    response = requests.get(url, proxies=proxy)
    if response.status_code == 429:
        wait = exponential_backoff(attempt)
        print(f"Bị giới hạn tỷ lệ. Đang chờ {wait:.1f}s (cố gắng {attempt+1})")
        time.sleep(wait)
    else:
        break

Sai lầm 4: Một luồng cho tất cả các proxy

Nếu bạn có 50 proxy, nhưng chỉ một luồng thực thi — bạn chỉ sử dụng tối đa 1 proxy cùng một lúc. Sử dụng ThreadPoolExecutor hoặc cách tiếp cận bất đồng bộ để sử dụng đồng thời toàn bộ nhóm:

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)

# Sử dụng đồng thời tất cả các proxy
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})")

Kết luận và khuyến nghị

Giới hạn tỷ lệ là một vấn đề có thể giải quyết, nếu tiếp cận một cách hệ thống. Những điểm chính từ hướng dẫn này:

  • Nhóm proxy, không phải một proxy — đơn vị tối thiểu cho công việc nghiêm túc. Số lượng proxy được xác định theo công thức: tốc độ mục tiêu ÷ giới hạn của máy chủ trên IP.
  • Chiến lược xoay vòng là quan trọng — per-request cho các yêu cầu stateless, sticky sessions cho các kịch bản đã xác thực.
  • IP không phải là tham số duy nhất — tiêu đề, cookies, fingerprint TLS và các mẫu hành vi cũng được các hệ thống bảo vệ phân tích.
  • Xử lý 429 một cách chính xác — backoff theo kiểu tăng dần, tiêu đề Retry-After, thay đổi proxy khi bị chặn.
  • Loại proxy phụ thuộc vào mục tiêu — proxy từ trung tâm dữ liệu cho API mở, proxy cư trú cho các thị trường, proxy di động cho bảo vệ tối đa.

Nếu bạn làm việc với việc phân tích các thị trường (Wildberries, Ozon), thu thập dữ liệu từ các API được bảo vệ hoặc tự động hóa với tốc độ cao — chúng tôi khuyên bạn nên bắt đầu với proxy cư trú: chúng cung cấp sự cân bằng tối ưu giữa ẩn danh và tốc độ, và địa chỉ IP của chúng hầu như không bị đưa vào danh sách đen. Đối với các nhiệm vụ cần độ bền tối đa trước các chặn với tần suất yêu cầu cao, hãy xem xét proxy di động — địa chỉ IP của chúng được chia sẻ bởi hàng ngàn người dùng thực tế, điều này làm cho việc chặn trở nên cực kỳ không mong muốn cho bất kỳ trang web nào.

```