Cách Thiết Lập Proxy Tùy Chỉnh trong Scrapy (Sẵn Sàng Cho Môi Trường Sản Xuất)

Cập nhật lần cuối vào August 11, 2026
Hand-drawn Scrapy crawler routing through a proxy pool, retrying a 503 route and completing through a 200 route
Tóm tắt bằng AI
  • Xây dựng middleware proxy tùy chỉnh cho Scrapy để gán route đã được cho phép, thêm xác thực an toàn, ghi nhận lý do lỗi và giữ nguyên metadata của request qua các lần retry.
  • Hiểu thứ tự của downloader middleware, đặc biệt là cách logic proxy custom tương tác với HttpProxyMiddleware, RetryMiddleware, redirect và xử lý exception.
  • Thêm rotation với số lần thử giới hạn, thời gian cool-down, trạng thái sức khỏe proxy và chính sách theo target thay vì chọn proxy ngẫu nhiên cho mọi request.
  • Phân biệt lỗi xác thực proxy, lỗi kết nối, lỗi DNS, phản hồi 403 từ target và rate limit để mỗi tình huống nhận được phản ứng phù hợp.
  • Áp dụng các biện pháp an toàn production cho lưu trữ bí mật, concurrency, observability, tính nhất quán của session và cơ chế fail-closed khi không còn proxy nào được phép sử dụng.

Trình spider Scrapy của bạn chạy mượt trên 100 trang test, rồi bắt đầu “đổ” ở mốc 10.000. Đây không hẳn là lỗi scraping — mà thường là vấn đề mạng, chỉ đang khoác áo lỗi scraping mà thôi. Cách sửa mà ai cũng nghĩ tới là dùng proxy, rồi nhét nó vào request.meta thì chỉ mất khoảng ba mươi giây.

Vấn đề là rất nhiều bài hướng dẫn dừng đúng ở đoạn “ba mươi giây” đó. Họ chỉ cho bạn meta={"proxy": "http://IP:PORT"}, đôi khi thêm một class middleware, rồi coi như xong. Nhưng họ thường bỏ qua chuyện gì xảy ra khi proxy chết giữa lúc crawl, làm sao để credential không bị lộ vào lịch sử Git, hay vì sao retry lại có thể dùng đúng proxy đã hỏng trước đó. Đây mới là những thứ bài viết này sẽ đi sâu: cấu hình fail-closed, quản lý bí mật, chọn proxy có chủ đích khi retry, giới hạn giao thức, và đánh đổi chi phí có thể đo lường được.

Proxy Middleware trong Scrapy là gì?

Proxy middleware là một đoạn code nằm trong downloader pipeline của Scrapy, quyết định request sẽ trông như được gửi từ IP nào trước khi nó rời khỏi hệ thống. Scrapy có sẵn một middleware tích hợp là HttpProxyMiddleware, nó đọc key proxy từ request.meta, gắn xác thực proxy nếu cần, rồi chuyển request cho download handler để mở kết nối thật sự. Custom proxy middleware không thay thế bước transport đó — nó chỉ là một bộ chọn để quyết định proxy nào sẽ được gắn vào trước khi cơ chế tích hợp tiếp quản. Nghe có vẻ nhỏ, nhưng khác biệt này cực kỳ quan trọng, và đó cũng là nguồn gốc của rất nhiều lỗi kiểu “middleware custom của tôi không chạy”.

Vì sao proxy quan trọng với dự án Scrapy ở môi trường production

Proxy thay đổi tuyến mạng và IP nguồn hiển thị. Điều đó có thể hữu ích cho việc test theo khu vực địa lý hợp pháp, phân bổ lưu lượng request được cho phép, và cô lập lỗi mạng. Nhưng nó không tự cấp quyền truy cập, không vượt rate limit, và cũng không đảm bảo truy cập được mọi nơi. Có cần proxy hay không còn phụ thuộc vào target, điều khoản sử dụng, tốc độ request, và mức độ ổn định mà công việc đòi hỏi.

Không phải proxy nào cũng giống nhau, và chọn sai phân khúc có thể trở thành một sự cố production đúng nghĩa:

Loại ProxyĐộ tin cậyTốc độRủi ro bị phát hiệnChi phí thường gặp
Proxy công khai miễn phíRất thất thườngThất thườngThường caoKhông mất phí, nhưng rủi ro bảo mật/vận hành rất lớn
Datacenter proxyPhụ thuộc nhà cung cấp và targetThường nhanhPhụ thuộc targetThường tính theo GB hoặc theo IP
Residential proxyPhụ thuộc nhà cung cấp và targetThất thườngPhụ thuộc targetThường tính theo GB
ISP proxyPhụ thuộc nhà cung cấp và targetThất thườngPhụ thuộc targetTùy nhà cung cấp

Proxy miễn phí cần được cảnh báo riêng vì rủi ro đo được là rất lớn. Nghiên cứu 30 tháng Free Proxies Unmasked đã theo dõi hơn 640.000 địa chỉ từ 11 nhà cung cấp: 34,5% từng hoạt động ít nhất một lần, và 16.923 địa chỉ đã can thiệp nội dung. Con số này đủ để phát đi cảnh báo mạnh về bảo mật khi dùng danh sách công khai; nó không phải là tỷ lệ lỗi có thể áp dụng máy móc cho mọi danh sách hiện tại, mọi pool trả phí, mọi target, hay mọi workload.

Proxy cũng không “phá” được các cơ chế bot detection hiện đại một cách kỳ diệu. Các dịch vụ như Cloudflare chấm điểm request dựa trên hàng chục tín hiệu — fingerprint TLS, tính nhất quán của header, thực thi JavaScript, mẫu hành vi — trong đó IP chỉ là một đầu vào. Một residential proxy “sạch” nhưng request lại có header không đồng nhất và không có cookie jar vẫn có thể bị đánh dấu. Vì vậy đừng vội xây cả kiến trúc xoay quanh kiểu nghĩ “chỉ cần đổi IP là xong”.

Trước khi bắt đầu

  • Mức độ: Trung cấp
  • Thời gian cần có: khoảng 30–45 phút cho toàn bộ setup production, khoảng 5 phút cho bài test nhanh
  • Bạn cần chuẩn bị: Python 3.9+, Scrapy đã cài (bài này được kiểm thử với Scrapy 2.17.0, phát hành tháng 7/2026), một spider đang chạy từ scrapy startproject, và ít nhất một proxy endpoint (trial miễn phí từ bất kỳ nhà cung cấp datacenter proxy nào cũng đủ để test)

Bước 1: Kiểm tra proxy bằng tham số Request Meta

Cách nhanh nhất để xác nhận proxy hoạt động là bỏ qua toàn bộ kiến trúc middleware và thử trực tiếp.

HttpProxyMiddleware tích hợp của Scrapy sẽ đọc key proxy thẳng từ request.meta và định tuyến request qua đó. Không cần sửa settings, không cần class middleware — chỉ một tham số.

import scrapy

class ProxyTestSpider(scrapy.Spider):
    name = "proxy_test"
    start_urls = ["https://httpbin.org/ip"]

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url,
                meta={"proxy": "http://203.0.113.10:8080"},
                callback=self.parse,
            )

    def parse(self, response):
        self.logger.info(response.text)

Chạy bằng scrapy runspider proxy_test.py. Nếu proxy hoạt động, httpbin.org/ip sẽ trả về IP của proxy thay vì IP của bạn — đó là dấu hiệu xác nhận. Nếu response bị treo rồi cuối cùng báo lỗi TCP connection timed out, thì proxy đã chết hoặc không truy cập được, và chuyện này xảy ra thường xuyên hơn các nhà cung cấp proxy muốn thừa nhận.

Cách này ổn cho spider dùng một lần hoặc test nhanh. Nhưng nó sẽ nhanh chóng bất tiện khi bạn có nhiều hơn một spider, vì lúc đó bạn đang hardcode cùng một chuỗi proxy trong năm file khác nhau.

Bước 2: Xây dựng custom proxy middleware

Nếu không chỉ có một spider, bạn nên gom logic proxy về một nơi duy nhất. Tạo class ProxyMiddleware trong middlewares.py của dự án:

from scrapy.exceptions import NotConfigured

class ProxyMiddleware:
    def __init__(self, proxy_url):
        self.proxy_url = proxy_url

    @classmethod
    def from_crawler(cls, crawler):
        proxy_url = crawler.settings.get("PROXY_URL")
        if not proxy_url:
            raise NotConfigured("PROXY_URL là bắt buộc; không cho phép fallback sang direct một cách âm thầm")
        return cls(proxy_url=proxy_url)

    def process_request(self, request, spider):
        if "proxy" not in request.meta:
            request.meta["proxy"] = self.proxy_url

Rồi đăng ký nó trong settings.py:

import os

PROXY_URL = os.environ.get("PROXY_URL")

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.ProxyMiddleware": 350,
}

Lưu ý from_crawler thay vì __init__ thuần. Đây mới là pattern Scrapy khuyến nghị để đọc settings, và cũng là hook mà chúng ta sẽ dùng lại ở Bước 4 khi nói về bí mật. Ưu điểm rất rõ: chỉ cần đổi một setting là toàn bộ spider trong project đều dùng proxy mới. Không cần lục từng file để thay một IP đã chết.

Bước 3: Hiểu thứ tự thực thi middleware để proxy không “âm thầm vô hiệu”

Đây là phần mà hầu hết các bài khác bỏ qua bằng câu “chỉ cần đặt priority là 350, tin tôi đi.” Downloader middleware trong Scrapy chạy theo một thứ tự cụ thể và ổn định; nếu bạn không hiểu nó, setup proxy của bạn sẽ sinh ra những lỗi trông chẳng liên quan gì đến proxy.

Các hook phía request (process_request) chạy theo thứ tự priority tăng dần — số nhỏ chạy trước. Các hook phía response (process_response, process_exception) chạy theo thứ tự giảm dần — số lớn chạy trước rồi đi ngược lại chuỗi.

Luồng request (tăng dần):
  Spider → [100] RobotsTxt → [300] HttpAuth → [350] YOUR PROXY MIDDLEWARE
         → [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
         → Downloader

Luồng response (giảm dần):
  Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
             → [500] UserAgent → [350] YOUR PROXY MIDDLEWARE → [300] HttpAuth
             → [100] RobotsTxt → Spider

Đây là bảng priority mặc định, hiện tại, của các middleware tích hợp trong Scrapy (đã xác minh với 2.17.0):

MiddlewarePriority mặc định
RobotsTxtMiddleware100
HttpAuthMiddleware300
DownloadTimeoutMiddleware350
DefaultHeadersMiddleware400
UserAgentMiddleware500
RetryMiddleware550
RedirectMiddleware600
CookiesMiddleware700
HttpProxyMiddleware750
DownloaderStats850
HttpCacheMiddleware900

Khi RetryMiddleware lên lịch retry, nó sẽ copy request đã lỗi — bao gồm cả metadata — và request mới này đi vào lại chuỗi downloader middleware. Quan hệ số giữa selector và priority 550 không tự động tạo ra xoay vòng proxy. Việc rotation chỉ xảy ra khi code selector nhận ra đây là retry và chủ động ghi đè meta["proxy"] đã được copy. Priority 350 chỉ là vị trí thuận tiện cho selector vì nó chạy trước bước transport tích hợp ở 750, chứ bản thân nó không phải cơ chế rotation.

Scrapy request and response paths through priorities 350, 550, and 750, with a 503 retry re-entering the middleware chain

Những lỗi thường gặp khi sắp xếp middleware

  • Đặt middleware cùng priority với HttpProxyMiddleware (750): Điều này tạo ra tình trạng race condition, nơi Scrapy (do thứ tự của dict, chứ không phải logic của bạn) quyết định middleware nào xử lý request trước. Triệu chứng: hành vi proxy lúc được lúc không, rất khó giải thích.
  • Dùng setdefault() hoặc if "proxy" not in request.meta trong bộ chọn xoay vòng: retry copy sẽ giữ nguyên proxy cũ. Triệu chứng: mỗi lần retry lại đi đúng route đã hỏng trước đó. Cách sửa: phát hiện retry (ví dụ retry_times > 0) và chủ động thay thế giá trị proxy thuộc quyền quản lý của selector.
  • Tắt hẳn HttpProxyMiddleware: Một số hướng dẫn bảo bạn đặt "scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": None vì “custom middleware sẽ lo phần đó”. Thực tế không phải vậy — custom middleware chỉ là selector, không phải transport layer. Nếu tắt middleware tích hợp, header xác thực proxy sẽ không bao giờ được gắn, và request sẽ đi ra ngoài mà không xác thực (hoặc chẳng đi đâu cả).

Bước 4: Đừng hardcode credential proxy

Mỗi bài tutorial Scrapy proxy top đầu mà tôi tìm thấy đều viết thẳng http://username:password@proxy.example.com:8080 vào file Python. Đó là credential nằm vĩnh viễn trong lịch sử Git, xuất hiện ở mọi clone, mọi fork, và cả log dump nếu ai đó lỡ tay dùng print().

Có ba tầng thực hiện việc này:

Cách làmBảo mậtLinh hoạtPhù hợp nhất cho
Hardcode trong spider/settings.pyKém — bí mật nằm trong repoThấpChỉ test local nhanh
Biến môi trường http_proxy (chuẩn của Scrapy)Tốt hơn — nằm ngoài codeThấp (một proxy)CI/CD, Docker
.env + python-dotenv + from_crawlerTốt nhất — ngoài code, theo từng môi trườngCao (nhiều proxy, rotation)Scraper production

Tùy chọn thứ ba rất đáng để làm đúng cách. Cài python-dotenv, tạo file .env (và lập tức thêm nó vào .gitignore — tôi nói thật đấy, làm ngay bây giờ):

PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080

Load nó ở đầu settings.py:

from dotenv import load_dotenv
import os

load_dotenv()

PROXY_USER = os.getenv("PROXY_USER")
PROXY_PASSWORD = os.getenv("PROXY_PASSWORD")
PROXY_HOST = os.getenv("PROXY_HOST")
PROXY_PORT = os.getenv("PROXY_PORT")

Sau đó đọc an toàn bên trong middleware bằng from_crawler — đây chính là pattern giải quyết câu hỏi mà tôi từng thấy trên forum kiểu “làm sao set trước khi chạy scrapy crawl nếu credential thay đổi theo từng lần chạy?”:

import os
from urllib.parse import quote

class SecureProxyMiddleware:
    def __init__(self, user, password, host, port):
        self.user = quote(user, safe="")
        self.password = quote(password, safe="")
        self.host = host
        self.port = port

    @classmethod
    def from_crawler(cls, crawler):
        settings = crawler.settings
        return cls(
            user=settings.get("PROXY_USER"),
            password=settings.get("PROXY_PASSWORD"),
            host=settings.get("PROXY_HOST"),
            port=settings.get("PROXY_PORT"),
        )

    def process_request(self, request, spider):
        proxy_url = f"http://{self.user}:{self.password}@{self.host}:{self.port}"
        request.meta["proxy"] = proxy_url

Hãy chú ý đến urllib.parse.quote() bọc quanh credential. Nếu mật khẩu có ký tự như @, :, hoặc / (và các trình tạo mật khẩu rất hay nhét chúng vào), URL sẽ hỏng nếu không được mã hóa phần trăm trước. Đây là một dòng code nhỏ nhưng có thể cứu bạn khỏi hàng giờ debug đầy xấu hổ với lỗi “invalid proxy URL” mà thực ra chẳng liên quan gì đến proxy.

Hãy giữ credential ngoài log và kiểm tra percent-encoding bằng các giá trị đại diện — nhưng là giả lập. HTTPS tunneling, SOCKS5, và credential không phải Latin đều có ranh giới riêng theo handler; đừng mặc định rằng một mẫu xác thực có thể áp dụng cho mọi trường hợp nếu chưa có integration test được ghim cố định.

Secure flow from a locked environment file through percent-encoded proxy settings into Scrapy request metadata

Bước 5: Thêm proxy rotation

Một proxy duy nhất — dù tốt đến đâu — nếu phải thực hiện 5.000 request tới cùng một site thì sớm muộn cũng sẽ bị chú ý. Bạn cần một pool.

Phương án A — tự xây. Thật ra rất đơn giản:

import random

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

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

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

Cách này dùng được cho nhu cầu cơ bản, nhưng hoàn toàn không biết proxy nào còn sống. Mỗi request đều là tung xúc xắc.

Phương án B — dùng scrapy-rotating-proxies. Gói bên thứ ba này có sẵn cơ chế phát hiện ban và backoff tự động:

pip install scrapy-rotating-proxies

Nói thật ở đây: bản phát hành mới nhất trên PyPI là 0.6.2, từ năm 2019, và dự án được gắn nhãn Alpha. Nó chưa chắc đã hỏng trên Scrapy hiện đại, nhưng “không được duy trì tích cực từ 2019” không đồng nghĩa với “đã battle-tested cho traffic production năm 2026.” Hãy pin version, test trên chính target thật của bạn, và đừng mặc định nó xử lý tốt các proxy endpoint có xác thực — thực tế là phần lớn không xử lý được.

Bước 6: Xây dựng proxy middleware chịu lỗi tốt hơn (phát hiện proxy chết)

Đây là phần mà hầu hết bài viết đối thủ bỏ qua hoàn toàn, và cũng là điểm khác biệt giữa một demo và một hệ thống có thể sống sót sau crawl 6 tiếng mà không ai ngồi canh.

Tình huốngPhần lớn tutorial chỉ làmMiddleware này bổ sung
Proxy trả về 407Không đề cậpXem đây là lỗi xác thực proxy
Target trả về 403/429Thường gom chungTách riêng phản hồi policy/rate limit với tình trạng của proxy
Proxy timeoutKhông đề cậpNgưỡng timeout có thể cấu hình, giảm health score
Tất cả proxy đều chếtKhông đề cậpFallback có kiểm soát hoặc tạm dừng crawl kèm cảnh báo log
Proxy chập chờnKhông đề cậpCó thời gian cool-down trước khi đưa lại vào pool
import time
import random
from scrapy.exceptions import IgnoreRequest

class FaultTolerantProxyMiddleware:
    MAX_FAILURES = 3
    COOLDOWN_SECONDS = 300

    def __init__(self, proxy_pool):
        self.pool = {p: {"failures": 0, "banned_until": 0} for p in proxy_pool}

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

    def _healthy_proxies(self):
        now = time.time()
        return [p for p, s in self.pool.items() if s["banned_until"] < now]

    def process_request(self, request, spider):
        healthy = self._healthy_proxies()
        if not healthy:
            spider.logger.warning("Tất cả proxy đều không khỏe — đang tạm dừng crawl")
            raise IgnoreRequest("Không có proxy nào còn khỏe")
        request.meta["proxy"] = random.choice(healthy)

    def process_response(self, request, response, spider):
        proxy = request.meta.get("proxy")
        if proxy and response.status == 407:
            self._mark_failure(proxy)
        return response

    def process_exception(self, request, exception, spider):
        proxy = request.meta.get("proxy")
        if proxy:
            self._mark_failure(proxy)

    def _mark_failure(self, proxy):
        state = self.pool[proxy]
        state["failures"] += 1
        if state["failures"] >= self.MAX_FAILURES:
            state["banned_until"] = time.time() + self.COOLDOWN_SECONDS
            state["failures"] = 0

Một vài lưu ý từ thực tế triển khai: đừng coi mọi 403 là bằng chứng proxy đã chết — RFC 9110 định nghĩa 403 là “server hiểu nhưng từ chối”, điều này hoàn toàn có thể do header hoặc session của bạn trông đáng ngờ, chứ không nhất thiết do proxy. Ngược lại, 407 là proxy đang từ chối xác thực của bạn — tín hiệu này cụ thể và mạnh hơn nhiều. Trộn lẫn các tín hiệu này vào một nhóm là cách khiến người ta đốt proxy khỏe vô ích.

Hãy giữ mặc định retry tạm thời của Scrapy ở production cho rõ ràng để người review có thể thấy chính sách:

RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2

Đây là mặc định của Scrapy 2.17. Đừng thêm bừa 403 hoặc 407: 403 là target từ chối với nhiều nguyên nhân khác nhau, còn 407 là vấn đề xác thực proxy mà retry request chung chung không sửa được. Nếu một hợp đồng target cụ thể có lý do chính đáng để retry một status khác, hãy ghi rõ lý do đó và test riêng.

Distinct handling for 407 authentication failure, 429 rate limiting, 503 retry, 200 success, and a closed gate when proxies are unavailable

Bước 7: Mẫu proxy-on-retry có đo lường

Một số target được phép có thể trả nội dung hợp lệ trực tiếp và chỉ cần proxy sau khi gặp phản hồi tạm thời đã được ghi nhận. Cách này có thể giảm lượng byte đi qua proxy, nhưng không có con số tiết kiệm phổ quát nào có thể bảo vệ được, cũng không có tỷ lệ thành công direct-request nào có thể áp dụng cho mọi nơi. Hãy tự đo workload của bạn trước khi dùng pattern này.

Dùng helper công khai get_retry_request() của Scrapy và chỉ mở rộng escalation cho các status riêng của target mà bạn đã phân loại rõ ràng. Ví dụ dưới đây coi 429 và 503 là tín hiệu áp lực có thể retry; cố ý loại trừ 403 và 407.

from scrapy.downloadermiddlewares.retry import get_retry_request

class CostAwareEscalationMiddleware:
    def process_response(self, request, response, spider):
        if response.status not in {429, 503}:
            return response

        retry = get_retry_request(
            request,
            spider=spider,
            reason=f"proxy_escalation_{response.status}",
            max_retry_times=2,
        )
        if retry is None:
            return response

        current_tier = request.meta.get("proxy_tier", "direct")
        if current_tier == "direct":
            retry.meta["proxy"] = spider.settings["DATACENTER_PROXY"]
            retry.meta["proxy_tier"] = "datacenter"
        elif current_tier == "datacenter":
            retry.meta["proxy"] = spider.settings["RESIDENTIAL_PROXY"]
            retry.meta["proxy_tier"] = "residential"
        else:
            return response
        return retry

Hãy ghi log tỷ lệ direct trả về nội dung hợp lệ, tỷ lệ proxied trả về nội dung hợp lệ, số byte trên mỗi record thành công, số retry trên mỗi lần thành công, và độ trễ tăng thêm. Thiết kế direct-first chỉ chấp nhận được khi việc truy cập trực tiếp đã được phép và hệ thống luôn fail-closed mỗi khi proxy là bắt buộc. Phần tiết kiệm là mức giảm đo được của traffic đi qua proxy — không phải một con số giả định.

Khi nào tự quản lý proxy không còn đáng công

Tôi muốn nói thẳng thay vì giả vờ như toàn bộ phần còn lại của bài này là vô nghĩa: tất cả những gì ở trên đều là kỹ thuật thực tế và hữu ích, và với nhiều dự án — crawl khối lượng lớn, pipeline tùy biến, bất kỳ thứ gì cần kiểm soát hoàn toàn lịch request — thì đó là lựa chọn đúng.

Nhưng nếu mục tiêu thực sự của bạn là “lấy dữ liệu có cấu trúc từ trang này” chứ không phải “vận hành hạ tầng proxy,” thì một extraction API có thể là ranh giới tốt hơn. Tài liệu hiện tại của Thunderbit POST /extract chấp nhận URL của trang và một JSON Schema tùy chọn; khi không cung cấp schema, dịch vụ có thể tự sinh schema từ nội dung trang. Endpoint này cũng hỗ trợ các chế độ render none, basic, và full, cùng các điều khiển timeout và thời gian chờ sau khi tải xong. Trích xuất chỉ bằng prompt không nằm trong bề mặt request được hỗ trợ hiện tại, vì vậy hãy xây dựng tích hợp production dựa trên hợp đồng schema đã được tài liệu hóa. Cách này đưa giao diện trích xuất vào sau một request; nó không phải là lời hứa phổ quát cho mọi target anti-bot hay CAPTCHA.

Yếu tốScrapy + proxy tự xâyTrích xuất bằng API (ví dụ Thunderbit)
Công sức thiết lậpCao — middleware, rotation, retry logicThấp — một API call với schema
Hành vi mạng/renderBạn tự cấu hình handler, proxy, header và delayĐiều khiển qua các tuỳ chọn API đã tài liệu hóa
Bảo trìBạn tự chịu trách nhiệm selector, sức khỏe pool, thay đổi targetBạn chịu trách nhiệm chất lượng schema, validation và hành vi tích hợp
Mô hình chi phíPhí proxy + compute + thời gian kỹ sưTài liệu hiện tại ghi 20 units mỗi trang (kiểm tra 2026-08-10)
Mức độ kiểm soátToàn bộ — pipeline tùy biến, chuỗi middlewareBị giới hạn trong năng lực của API
Phù hợp nhất choCrawl phức tạp, khối lượng lớn, logic tùy biếnTrích xuất có mục tiêu, prototype, enrichment

Nếu bạn chủ yếu đang trích xuất các trang có cấu trúc cho danh sách lead, dữ liệu sản phẩm hoặc nghiên cứu, hãy chạy một pilot đại diện bằng Thunderbit Chrome Extension hoặc API, rồi so sánh số record hợp lệ, độ trễ, units và thời gian bảo trì. Nếu dự án của bạn cần graph crawl tùy biến và quyền kiểm soát pipeline, Scrapy vẫn là lựa chọn mạnh hơn.

Tìm hiểu thêm

Mẹo & lỗi thường gặp

  • Mẹo: Hãy test proxy với httpbin.org/ip trước khi dùng nó cho target thật. Đây là cách nhanh nhất để xác nhận routing hoạt động trước khi thêm độ phức tạp lên trên.
  • Lỗi thường gặp: Đặt proxy trong request.headers thay vì request.meta. Đây là lỗi rất hay gặp vì trông gần giống nhau — HttpProxyMiddleware chỉ đọc meta["proxy"], còn cách đặt qua header sẽ thất bại âm thầm, không có lỗi rõ ràng.
  • Lỗi thường gặp: Giả sử HTTP và HTTPS proxy được cấu hình giống hệt nhau. URL proxy HTTP trỏ đến đích HTTPS thường hoạt động thông qua CONNECT tunneling khi handler tương thích, nhưng dùng scheme https:// cho chính proxy endpoint là một cấu hình khác, ít được hỗ trợ hơn — đừng nhầm lẫn hai thứ này.
  • Mẹo: Nếu bạn cần hỗ trợ SOCKS5, hãy kiểm tra khả năng của download handler trong phiên bản Scrapy bạn đang dùng trước. HttpxDownloadHandler experimental của Scrapy 2.17 có hỗ trợ SOCKS5 thông qua httpx[socks], nhưng nó vẫn được gắn nhãn experimental — đừng phụ thuộc vào nó cho production nếu chưa tự test kỹ.

Phương pháp thay thế

Ngoài scrapy-rotating-proxies, một số team đẩy toàn bộ logic proxy qua gateway của nhà cung cấp — một URL proxy duy nhất, nơi vendor xử lý rotation, session stickiness và geo-targeting ở phía sau. Cách này đổi một chút khả năng kiểm soát lấy rất nhiều lợi ích về việc giảm code middleware, và đáng để tính toán chi phí so với tự quản lý pool trước khi bạn tự xây từ đầu.

Kết luận

Thiết lập proxy trong Scrapy chỉ cần một dòng. Nhưng để setup đó sống được qua một crawl production thật sự thì bạn cần chính sách fail rõ ràng, credential được bảo vệ, boundary handler đã được kiểm thử, và code selector có chủ đích ghi đè metadata proxy đã được copy khi retry. Nếu bạn nhớ hai thói quen sau, hãy nhớ chúng: fail closed khi proxy là điều bắt buộc, và tuyệt đối không commit mật khẩu proxy vào file Python.

Câu hỏi thường gặp

Làm sao thiết lập custom proxy trong Scrapy có xác thực?
Dùng định dạng URL protocol://username:password@host:port, nhưng nếu username và password có ký tự đặc biệt thì hãy mã hóa phần trăm bằng urllib.parse.quote() trước. Với production, hãy đọc credential qua classmethod from_crawler lấy từ biến môi trường thay vì hardcode.

Nên dùng số priority nào cho custom proxy middleware trong Scrapy?
350 là priority selector phổ biến vì nó chạy trước HttpProxyMiddleware ở 750. Nhưng con số này không đảm bảo rotation. Retry chỉ nhận proxy mới khi selector nhận ra request retry đã được copy và ghi đè giá trị cũ trong meta["proxy"].

Làm sao tự động xử lý proxy chết trong Scrapy?
Xây middleware theo dõi số lần lỗi cho từng proxy trong process_responseprocess_exception, loại proxy khỏi pool đang dùng sau khi vượt ngưỡng lỗi, rồi đưa lại sau thời gian cool-down thay vì ban vĩnh viễn.

Scrapy có dùng được SOCKS5 proxy không?
HttpxDownloadHandler experimental của Scrapy 2.17 có tài liệu hỗ trợ SOCKS5 khi đã cài httpx[socks]. Handler HTTP/1.1 mặc định không hỗ trợ SOCKS proxy. Hãy pin handler/version và chạy integration test trước khi coi đây là đường đi production.

Mẫu proxy-on-retry thực sự tiết kiệm được bao nhiêu chi phí proxy?
Không có tỷ lệ phần trăm nào áp dụng được cho mọi trường hợp. Hãy đo tỷ lệ request được phép và trả nội dung hợp lệ trực tiếp, lượng byte đi qua từng tầng proxy, số retry trên mỗi record thành công, và độ trễ tăng thêm. Mức giảm traffic qua proxy quan sát được chính là phần tiết kiệm của bạn; nếu direct-first không được phép hoặc không hợp lệ, đừng dùng pattern này.

Ke
Ke
CTO tại Thunderbit | Chuyên gia Khoa học Dữ liệu cấp cao & ML Với gần một thập kỷ kinh nghiệm trong học máy và khoa học dữ liệu, Ke Shen là cựu sinh viên Đại học Columbia và từng là Chuyên gia Khoa học Dữ liệu cấp cao tại Walmart Labs. Sở hữu chuyên môn sâu về Python, R, Java và Thống kê, được đồng nghiệp công nhận, anh chia sẻ những góc nhìn thực chiến về cách đưa các thuật toán AI phức tạp từ lý thuyết vào kiến trúc sẵn sàng cho môi trường sản xuất.
Topics
Scrapy proxy middlewarePython web scrapingProxy rotation
Mục lục
Thunderbit · Tác nhân dữ liệu web AI

Trích xuất dữ liệu từ bất kỳ trang nào trong 1 lần nhấp

Được hơn 250.000+ người dùng tin tưởng
có gói miễn phí
Từ trang web đến bảng tính
Mô tả điều bạn cần — AI Agent của Thunderbit sẽ scrape và xuất sang Excel, Google Sheets, Airtable hoặc Notion. Bắt đầu miễn phí.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week