Middleware proxy trong Scrapy: hướng dẫn cấu hình đầy đủ (2026)

Đăng ngày 4 tháng 6 2026 · ≈11 phút đọc

Scrapy vẫn là con ngựa thồ cho các lần crawl production trong năm 2026 — và vẫn là framework khiến người ta bối rối nhất về thiết lập proxy, bởi có tới bốn chỗ khác nhau để cắm proxy vào và ba trong số đó sai với hầu hết dự án. Hướng dẫn này cho bạn cái đúng: một middleware tùy chỉnh nhỏ với định tuyến theo từng request, sticky session, phát hiện ban, và hành vi retry hợp lý.

Nếu bạn đang dùng requests/httpx/aiohttp thuần thay vì vậy, hãy xem Cách Luân chuyển Proxy trong Python. Bài viết này dành riêng cho Scrapy.

Phiên bản 30 Giây

Với một gateway residential luân chuyển, thiết lập tối thiểu khả dụng là một dòng cho mỗi request — không cần middleware:

def start_requests(self):
    for url in self.urls:
        yield scrapy.Request(
            url,
            meta={"proxy": "http://USERNAME:[email protected]:913"},
        )

HttpProxyMiddleware tích hợp sẵn của Scrapy đọc request.meta["proxy"] và xử lý xác thực từ URL. Gateway luân chuyển exit IP giúp bạn. Nếu đó là tất cả những gì bạn cần, hãy dừng ở đây. Phần còn lại của hướng dẫn này dành cho khi bạn cần kiểm soát: sticky session, định tuyến theo quốc gia, luân chuyển nhận biết ban, và tinh chỉnh độ đồng thời (concurrency).

Một Proxy Middleware cho Production

Bỏ đoạn này vào middlewares.py. Nó gán sticky session theo từng domain, luân chuyển khi bị ban, và gắn thẻ mọi request để bạn có thể gỡ lỗi xem session nào đã fetch cái gì:

import random
import string

GATEWAY = "us.jibaoproxy.com:913"
USERNAME = "USERNAME"          # move to settings.py / env in real projects
PASSWORD = "PASSWORD"

def _new_session(n=8):
    return "".join(random.choices(string.ascii_lowercase + string.digits, k=n))

class JibaoProxyMiddleware:
    """Sticky session per domain; rotate session on ban."""

    def __init__(self):
        self.sessions = {}          # domain -> session id

    def _proxy_url(self, session_id):
        user = f"{USERNAME}-session-{session_id}"
        return f"http://{user}:{PASSWORD}@{GATEWAY}"

    def process_request(self, request, spider):
        domain = request.url.split("/")[2]
        session = self.sessions.setdefault(domain, _new_session())
        request.meta["proxy"] = self._proxy_url(session)
        request.meta["proxy_session"] = session

    def rotate(self, domain):
        """Call when a session is burned."""
        self.sessions[domain] = _new_session()

Và một downloader middleware đi kèm để phát hiện ban và thử lại trên một session mới:

from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

BAN_CODES = {403, 429}
BAN_MARKERS = (b"captcha", b"access denied", b"unusual traffic")

class BanAwareRetryMiddleware(RetryMiddleware):

    def process_response(self, request, response, spider):
        banned = (
            response.status in BAN_CODES
            or any(m in response.body[:2048].lower() for m in BAN_MARKERS)
        )
        if banned:
            domain = request.url.split("/")[2]
            proxy_mw = spider.crawler.engine.downloader.middleware.middlewares
            for mw in proxy_mw:
                if hasattr(mw, "rotate"):
                    mw.rotate(domain)        # burn the session
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        return super().process_response(request, response, spider)

Khai báo cả hai trong settings.py:

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.JibaoProxyMiddleware": 350,
    "scrapy.downloadermiddlewares.retry.RetryMiddleware": None,   # replace stock retry
    "myproject.middlewares.BanAwareRetryMiddleware": 550,
}
RETRY_TIMES = 2

Thứ tự ưu tiên quan trọng: proxy middleware phải chạy trước HttpProxyMiddleware (750) của Scrapy, nên bất kỳ giá trị nào dưới 750 đều hoạt động; 350 giữ nó chạy sớm và dễ dự đoán.

Sticky so với Rotating: Chế độ Nào cho Spider Nào

Loại crawlChế độCách triển khai
Thu thập trang không trạng tháiRotatingUsername trần, gateway luân chuyển theo từng request
Đăng nhập + crawl sau xác thựcSticky theo tài khoản-session-{account_id}, không bao giờ luân chuyển giữa chừng khi đăng nhập
Danh sách nhiều trang (pagination)Sticky theo domain, luân chuyển khi bị banMiddleware ở trên
Giá theo khu vực địa lýRotating + ghim quốc giaTham số kiểu USERNAME-country-de

Phân tích sâu hơn về sự đánh đổi này: Sticky so với Rotating Proxy Session.

Cài đặt Concurrency Không Khiến Bạn Bị Ban

Các giá trị mặc định của Scrapy được tinh chỉnh cho việc crawl lịch sự bằng một IP. Đứng sau một pool luân chuyển, bạn có thể đẩy mạnh hơn nhiều — nhưng giới hạn theo từng domain vẫn quan trọng vì mục tiêu nhìn thấy hành vi tổng hợp:

# settings.py - sane starting point behind a residential pool
CONCURRENT_REQUESTS = 64
CONCURRENT_REQUESTS_PER_DOMAIN = 8     # what the target experiences
DOWNLOAD_DELAY = 0.25                  # jitter applied per slot
RANDOMIZE_DOWNLOAD_DELAY = True        # 0.5x-1.5x the delay
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 6.0
DOWNLOAD_TIMEOUT = 30
ROBOTSTXT_OBEY = True

Chỉ tăng CONCURRENT_REQUESTS_PER_DOMAIN sau khi đã theo dõi tỷ lệ 403 ở mức hiện tại trong vài nghìn request. Nhảy 8 → 32 vì "đằng nào proxy cũng luân chuyển" chính là cách người ta đốt sạch GB vào việc retry.

Kỷ luật Băng thông (GB Residential Là Tiền)

Những Lỗi Thường gặp và Ý nghĩa Thực sự của Chúng

407 Proxy Authentication Required

Thông tin đăng nhập chưa tới được proxy. Hãy đặt chúng trong URL (http://user:pass@host:port) ở meta["proxy"] — Scrapy phân tích và đặt Proxy-Authorization cho bạn. Việc đặt header thủ công đồng thời dùng thông tin đăng nhập trong URL gây ra hiện tượng xác thực kép kỳ lạ; hãy chọn một cách.

TunnelError: Could not open CONNECT tunnel

Hầu như luôn là gõ sai host/port, hoặc mục tiêu HTTPS qua một endpoint không cho phép CONNECT trên cổng đó. Hãy kiểm chứng bằng curl -x bên ngoài Scrapy trước.

Spider chạy được 10 phút, rồi mọi thứ đều 403

Sticky session của bạn đã sống quá lâu so với mức hoan nghênh, hoặc tốc độ theo từng domain của bạn quá nóng. Middleware nhận biết ban ở trên xử lý trường hợp đầu; hạ CONCURRENT_REQUESTS_PER_DOMAIN cho trường hợp sau. Nếu đó là một mục tiêu kiểm tra JA4, bản thân TLS stack của Scrapy có thể chính là dấu hiệu tố giác — xem giải thích JA3/JA4 để hiểu vì sao không proxy nào sửa được điều đó.

Công cụ miễn phí · không cần đăng ký

Xác thực danh sách proxy của bạn trước khi crawl

Dán các endpoint vào Proxy Checker của chúng tôi: nó kiểm tra khả năng kết nối, độ trễ, mức độ ẩn danh và loại exit-IP hàng loạt — bắt được những proxy chết hoặc bị gán nhãn sai trước khi Scrapy lãng phí retry vào chúng.

Kiểm tra proxy của tôi →

Mệt mỏi vì phải trông chừng các danh sách miễn phí? Một gateway residential thay thế tất cả — nhận 500MB lưu lượng miễn phí →

Tóm tắt

Hướng Spider của Bạn vào Một Pool Thực sự

Session residential rotating và sticky trên cùng một gateway. 500MB lưu lượng miễn phí để crawl.

Bắt đầu Dùng thử Miễn phí

Cho mọi sản phẩm IP · một kho node khổng lồ luôn sẵn sàng bất cứ lúc nào

Đăng ký ngay và nhận tới 100% hoàn tiền cho lần nạp của bạn

Người dùng mới nhận 500MB khi đăng ký, cùng tiền thưởng cho lần nạp đầu tiên. Ưu đãi có thời hạn.