Quay lại blog

Cài đặt proxy trong framework Scrapy: hướng dẫn đầy đủ với ví dụ mã

Hướng dẫn đầy đủ về việc tích hợp proxy vào Scrapy: từ cài đặt cơ bản đến các phương pháp nâng cao để xoay vòng địa chỉ IP với ví dụ mã nguồn.

📅14 tháng 2, 2026
```html

Scrapy là một trong những framework Python mạnh mẽ nhất cho việc web scraping, nhưng nếu không có cài đặt proxy đúng cách, các trình phân tích của bạn sẽ bị chặn chỉ sau vài phút hoạt động. Trong hướng dẫn này, tôi sẽ chỉ cho bạn tất cả các cách tích hợp proxy vào Scrapy: từ cài đặt đơn giản nhất đến các phương pháp nâng cao xoay vòng địa chỉ IP với xử lý lỗi tự động.

Tài liệu này dựa trên kinh nghiệm thực tế trong việc phân tích các trang thương mại điện tử lớn và các trang web bảo mật. Bạn sẽ nhận được các ví dụ mã sẵn sàng để sử dụng ngay trong các dự án của mình.

Tại sao Scrapy không có proxy lại bị chặn

Các trang web hiện đại sử dụng nhiều lớp bảo vệ chống lại việc phân tích. Ngay cả khi bạn đã cài đặt User-Agent và thời gian chờ giữa các yêu cầu, địa chỉ IP của bạn sẽ bị phát hiện tự động hóa qua một số dấu hiệu:

  • Tần suất yêu cầu: một IP thực hiện 100+ yêu cầu mỗi phút — dấu hiệu rõ ràng của bot
  • Mô hình hành vi: duyệt các trang liên tục mà không có các chuyển tiếp ngẫu nhiên
  • Thiếu JavaScript: Scrapy không thực thi JS, điều này dễ dàng bị phát hiện
  • Định vị địa lý: truy cập từ trung tâm dữ liệu thay vì mạng gia đình

Kết quả — bị cấm theo IP trong vài giờ hoặc vài ngày. Các trang thương mại điện tử (Amazon, Wildberries, Ozon), mạng xã hội và các trang web với Cloudflare sử dụng bảo vệ rất nghiêm ngặt. Proxy giải quyết vấn đề này bằng cách phân phối yêu cầu giữa nhiều địa chỉ IP.

Quan trọng: Ngay cả với proxy, bạn cũng cần tuân thủ giới hạn tần suất. Tốc độ khuyến nghị: 1-3 yêu cầu mỗi giây cho một IP. Để phân tích tốc độ cao, hãy sử dụng một nhóm hơn 50 proxy với xoay vòng.

Cài đặt cơ bản proxy trong Scrapy

Cách đơn giản nhất là chỉ định proxy trực tiếp trong cài đặt của con nhện. Phương pháp này phù hợp cho việc thử nghiệm hoặc phân tích một lượng dữ liệu nhỏ với một máy chủ proxy.

Phương pháp 1: Qua meta trong Request

import scrapy

class MySpider(scrapy.Spider):
    name = 'example'
    start_urls = ['https://example.com']
    
    def start_requests(self):
        proxy = 'http://username:password@proxy.example.com:8080'
        
        for url in self.start_urls:
            yield scrapy.Request(
                url=url,
                callback=self.parse,
                meta={'proxy': proxy}
            )
    
    def parse(self, response):
        # Logic phân tích của bạn
        self.log(f'Scraped {response.url} qua {response.meta["proxy"]}')

Định dạng proxy phụ thuộc vào giao thức và phương pháp xác thực:

  • http://proxy.example.com:8080 — không có xác thực
  • http://user:pass@proxy.example.com:8080 — có tên đăng nhập/mật khẩu
  • socks5://user:pass@proxy.example.com:1080 — proxy SOCKS5

Phương pháp 2: Cài đặt toàn cầu trong settings.py

# settings.py

# HTTP proxy cho tất cả các yêu cầu
HTTPPROXY_ENABLED = True
HTTPPROXY_AUTH_ENCODING = 'utf-8'

# Cài đặt qua biến môi trường
HTTP_PROXY = 'http://username:password@proxy.example.com:8080'
HTTPS_PROXY = 'http://username:password@proxy.example.com:8080'

Phương pháp này tiện lợi cho việc thử nghiệm nhanh, nhưng không phù hợp cho sản xuất: không có xoay vòng IP, nếu proxy bị hỏng, toàn bộ trình phân tích sẽ dừng lại, không thể sử dụng các proxy khác nhau cho các trang web khác nhau.

Tạo Middleware Proxy tùy chỉnh

Để phân tích sản xuất, cần một middleware riêng, sẽ quản lý nhóm proxy, xử lý lỗi và tự động xoay vòng IP. Đây là một triển khai cơ bản:

# middlewares.py

import random
from scrapy import signals
from scrapy.exceptions import NotConfigured

class RandomProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
    
    @classmethod
    def from_crawler(cls, crawler):
        # Tải danh sách proxy từ cài đặt
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        
        if not proxy_list:
            raise NotConfigured('PROXY_LIST chưa được cấu hình')
        
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Chọn một proxy ngẫu nhiên từ nhóm
        proxy = random.choice(self.proxy_list)
        request.meta['proxy'] = proxy
        
        spider.logger.info(f'Sử dụng proxy: {proxy}')
    
    def process_exception(self, request, exception, spider):
        # Khi có lỗi, thử một proxy khác
        proxy = random.choice(self.proxy_list)
        request.meta['proxy'] = proxy
        
        spider.logger.warning(
            f'Lỗi proxy, chuyển sang: {proxy}'
        )
        
        return request

Bây giờ hãy cấu hình việc sử dụng middleware trong settings.py:

# settings.py

# Danh sách proxy (có thể tải từ tệp hoặc API)
PROXY_LIST = [
    'http://user1:pass1@proxy1.example.com:8080',
    'http://user2:pass2@proxy2.example.com:8080',
    'http://user3:pass3@proxy3.example.com:8080',
    # ... thêm 50+ proxy để xoay vòng hiệu quả
]

# Kết nối middleware
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomProxyMiddleware': 350,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 400,
}

# Thử lại khi có lỗi
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]

Xoay vòng proxy: ba phương pháp hiệu quả

Chọn proxy ngẫu nhiên (như trong ví dụ trên) là phương pháp đơn giản nhất, nhưng không hiệu quả nhất. Hãy xem xét ba chiến lược xoay vòng cho các kịch bản khác nhau.

Phương pháp 1: Round-robin (xoay vòng tuần tự)

Proxy được chọn theo vòng tròn. Phù hợp cho việc phân phối tải đều:

class RoundRobinProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        self.current_index = 0
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Lấy proxy tiếp theo theo vòng tròn
        proxy = self.proxy_list[self.current_index]
        self.current_index = (self.current_index + 1) % len(self.proxy_list)
        
        request.meta['proxy'] = proxy

Phương pháp 2: Xoay vòng thông minh với danh sách đen

Theo dõi các proxy gặp vấn đề và tạm thời loại bỏ chúng khỏi xoay vòng:

import time
from collections import defaultdict

class SmartProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        self.proxy_errors = defaultdict(int)
        self.blacklist = set()
        self.blacklist_timeout = 300  # 5 phút
        self.blacklist_time = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def get_working_proxies(self):
        # Loại bỏ các proxy trong danh sách đen, thời gian đã hết
        current_time = time.time()
        expired = [
            proxy for proxy, ban_time in self.blacklist_time.items()
            if current_time - ban_time > self.blacklist_timeout
        ]
        
        for proxy in expired:
            self.blacklist.discard(proxy)
            self.proxy_errors[proxy] = 0
        
        # Trả về các proxy hoạt động
        return [p for p in self.proxy_list if p not in self.blacklist]
    
    def process_request(self, request, spider):
        working_proxies = self.get_working_proxies()
        
        if not working_proxies:
            spider.logger.error('Tất cả các proxy đều bị đưa vào danh sách đen!')
            return
        
        proxy = random.choice(working_proxies)
        request.meta['proxy'] = proxy
    
    def process_response(self, request, response, spider):
        # Nếu bị chặn — thêm vào danh sách đen
        if response.status in [403, 429, 503]:
            proxy = request.meta.get('proxy')
            self.proxy_errors[proxy] += 1
            
            if self.proxy_errors[proxy] >= 3:
                self.blacklist.add(proxy)
                self.blacklist_time[proxy] = time.time()
                spider.logger.warning(
                    f'Proxy {proxy} đã bị đưa vào danh sách đen trong {self.blacklist_timeout}s'
                )
        
        return response

Phương pháp 3: Xoay vòng qua API của nhà cung cấp

Nhiều nhà cung cấp proxy (bao gồm proxy cư trú) cung cấp endpoint xoay vòng — một URL, tự động thay đổi IP mỗi khi có yêu cầu:

# settings.py

# Một endpoint duy nhất với xoay vòng tự động
ROTATING_PROXY = 'http://username:password@rotating.proxy.com:8080'

# Middleware đơn giản
class RotatingProxyMiddleware:
    def __init__(self, proxy):
        self.proxy = proxy
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy = crawler.settings.get('ROTATING_PROXY')
        return cls(proxy)
    
    def process_request(self, request, spider):
        # Một URL, nhưng mỗi yêu cầu đi với một IP mới
        request.meta['proxy'] = self.proxy

Đây là phương pháp tiện lợi nhất cho sản xuất: không cần quản lý nhóm proxy, nhà cung cấp tự theo dõi chất lượng IP và thay thế các proxy gặp vấn đề. Đặc biệt hiệu quả với proxy cư trú, nơi nhóm IP có thể đạt tới hàng triệu địa chỉ.

Xác thực: tên đăng nhập/mật khẩu so với danh sách trắng IP

Các nhà cung cấp proxy cung cấp hai phương pháp xác thực. Sự lựa chọn ảnh hưởng đến tốc độ kết nối và sự tiện lợi trong cài đặt.

Xác thực User:Pass

Tên đăng nhập và mật khẩu được truyền trong URL của proxy. Scrapy tự động chuyển đổi chúng thành tiêu đề HTTP Proxy-Authorization:

proxy = 'http://username:password@proxy.example.com:8080'
request.meta['proxy'] = proxy

# Scrapy sẽ tự động thêm tiêu đề:
# Proxy-Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Ưu điểm: hoạt động từ bất kỳ IP nào, dễ dàng thay đổi proxy trong mã.
Nhược điểm: một chút overhead cho mỗi yêu cầu (~50-100ms), thông tin xác thực ở dạng rõ trong mã.

Xác thực danh sách trắng IP

Bạn thêm IP của máy chủ của bạn vào danh sách trắng của nhà cung cấp, không cần xác thực:

proxy = 'http://proxy.example.com:8080'  # không có tên đăng nhập/mật khẩu
request.meta['proxy'] = proxy

Ưu điểm: nhanh hơn 50-100ms, an toàn hơn (không có thông tin xác thực trong mã).
Nhược điểm: chỉ hoạt động từ các IP nhất định, cần cập nhật danh sách trắng khi thay đổi máy chủ.

Khuyến nghị cho sản xuất:

Sử dụng danh sách trắng IP cho việc phân tích từ các máy chủ chuyên dụng (AWS, Google Cloud, Hetzner). Đối với phát triển và thử nghiệm từ máy tính cục bộ — sử dụng xác thực user:pass.

Xử lý lỗi và tự động thay đổi IP

Ngay cả với các proxy chất lượng, sẽ có lỗi: timeout, từ chối kết nối, chặn. Xử lý lỗi đúng cách là rất quan trọng cho sự ổn định của trình phân tích.

Xử lý mã trạng thái HTTP

class ProxyMiddleware:
    def process_response(self, request, response, spider):
        # Các mã, khi cần thay đổi proxy và thử lại
        ban_codes = [403, 407, 429, 503]
        
        if response.status in ban_codes:
            proxy = request.meta.get('proxy')
            spider.logger.warning(
                f'Nhận {response.status} từ {proxy}, đang thử lại...'
            )
            
            # Đánh dấu để thử lại với proxy mới
            request.meta['dont_retry'] = False
            request.meta['proxy'] = self.get_new_proxy()
            
            return request
        
        return response

Xử lý ngoại lệ mạng

from twisted.internet.error import TimeoutError, ConnectionRefusedError
from scrapy.exceptions import IgnoreRequest

class ProxyMiddleware:
    def process_exception(self, request, exception, spider):
        # Lỗi kết nối đến proxy
        proxy_errors = (
            TimeoutError,
            ConnectionRefusedError,
            ConnectionLost,
        )
        
        if isinstance(exception, proxy_errors):
            proxy = request.meta.get('proxy')
            spider.logger.error(
                f'Kết nối proxy {proxy} thất bại: {exception}'
            )
            
            # Thay đổi proxy và thử lại
            request.meta['proxy'] = self.get_new_proxy()
            return request
        
        # Đối với các lỗi khác, sử dụng xử lý tiêu chuẩn
        return None

Phát hiện chặn qua nội dung

Một số trang web trả về HTTP 200, nhưng hiển thị captcha hoặc trang chặn:

class ProxyMiddleware:
    def process_response(self, request, response, spider):
        # Dấu hiệu chặn trong nội dung
        ban_indicators = [
            'captcha',
            'truy cập bị từ chối',
            'bị chặn',
            'lưu lượng không bình thường',
            'kiểm tra robot',
        ]
        
        body_text = response.text.lower()
        
        if any(indicator in body_text for indicator in ban_indicators):
            spider.logger.warning(
                f'Trái phép trang bị phát hiện từ {request.meta.get("proxy")}'
            )
            
            # Thay đổi proxy và thử lại
            request.meta['proxy'] = self.get_new_proxy()
            return request
        
        return response

Loại proxy nào nên chọn cho Scrapy

Việc chọn loại proxy phụ thuộc vào trang web mục tiêu, ngân sách và tốc độ phân tích yêu cầu. Dưới đây là so sánh các tùy chọn chính:

Loại proxy Tốc độ Chi phí Khi nào sử dụng
Proxy trung tâm dữ liệu Cao (50-200ms) Thấp ($1-3/IP) Các trang đơn giản không có bảo vệ, API, công cụ nội bộ
Proxy cư trú Trung bình (300-800ms) Trung bình ($5-15/GB) Thương mại điện tử, mạng xã hội, các trang với Cloudflare, nhắm mục tiêu địa lý
Proxy di động Thấp (500-1500ms) Cao ($50-150/IP) Ứng dụng di động, Instagram, TikTok, bảo vệ tối đa

Khuyến nghị về lựa chọn

Đối với việc phân tích các trang thương mại điện tử (Amazon, Wildberries, Ozon, AliExpress) — chỉ sử dụng proxy cư trú. Những trang này thường xuyên chặn các trung tâm dữ liệu. Cần có xoay vòng và nhắm mục tiêu địa lý (ví dụ, IP Nga cho Wildberries).

Đối với việc phân tích các trang tin tức, blog, diễn đàn — proxy trung tâm dữ liệu là đủ. Bảo vệ tối thiểu, tốc độ và chi phí lưu lượng quan trọng.

Đối với việc phân tích các trang với Cloudflare — proxy cư trú là bắt buộc. Các trung tâm dữ liệu bị Cloudflare phát hiện gần như ngay lập tức. Thêm thư viện cloudscraper vào Scrapy để vượt qua các thử thách JS.

Đối với việc phân tích Google Search, công cụ SEO — proxy cư trú với nhắm mục tiêu địa lý. Google hiển thị các kết quả khác nhau cho các quốc gia và thành phố khác nhau.

Lời khuyên: Bắt đầu với một nhóm 10 proxy cư trú để thử nghiệm. Nếu bạn gặp phải các chặn — tăng nhóm lên 50-100 IP. Đối với việc phân tích tốc độ cao (1000+ yêu cầu/phút) hãy sử dụng endpoint xoay vòng với nhóm 10,000+ IP.

Kỹ thuật nâng cao: phiên và IP dính

Khi phân tích một số trang web, cần giữ một IP trong suốt phiên (xác thực, giỏ hàng, các biểu mẫu nhiều bước). Đây là cách thực hiện các phiên dính trong Scrapy.

IP dính cho một miền

from urllib.parse import urlparse

class StickyProxyMiddleware:
    def __init__(self, proxy_list):
        self.proxy_list = proxy_list
        # Từ điển: miền -> proxy
        self.domain_proxy_map = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        return cls(proxy_list)
    
    def process_request(self, request, spider):
        # Trích xuất miền từ URL
        domain = urlparse(request.url).netloc
        
        # Nếu đã có proxy cho miền này — sử dụng nó
        if domain in self.domain_proxy_map:
            proxy = self.domain_proxy_map[domain]
        else:
            # Nếu không, chọn mới và ghi nhớ
            proxy = random.choice(self.proxy_list)
            self.domain_proxy_map[domain] = proxy
            spider.logger.info(f'Gán {proxy} cho {domain}')
        
        request.meta['proxy'] = proxy

IP dính với thời gian phiên

Một biến thể nâng cao hơn: proxy được gán cho miền trong một khoảng thời gian nhất định (ví dụ, 10 phút), sau đó sẽ thay đổi:

import time
from urllib.parse import urlparse

class SessionProxyMiddleware:
    def __init__(self, proxy_list, session_timeout=600):
        self.proxy_list = proxy_list
        self.session_timeout = session_timeout  # 10 phút
        # Từ điển: miền -> (proxy, thời gian tạo)
        self.sessions = {}
    
    @classmethod
    def from_crawler(cls, crawler):
        proxy_list = crawler.settings.getlist('PROXY_LIST')
        timeout = crawler.settings.getint('PROXY_SESSION_TIMEOUT', 600)
        return cls(proxy_list, timeout)
    
    def get_proxy_for_domain(self, domain):
        current_time = time.time()
        
        # Kiểm tra xem có phiên hoạt động không
        if domain in self.sessions:
            proxy, created_at = self.sessions[domain]
            
            # Nếu phiên chưa hết hạn — sử dụng cùng một proxy
            if current_time - created_at < self.session_timeout:
                return proxy
        
        # Tạo một phiên mới với proxy mới
        new_proxy = random.choice(self.proxy_list)
        self.sessions[domain] = (new_proxy, current_time)
        
        return new_proxy
    
    def process_request(self, request, spider):
        domain = urlparse(request.url).netloc
        proxy = self.get_proxy_for_domain(domain)
        request.meta['proxy'] = proxy

Tích hợp với Cookie Middleware

Để có các phiên đầy đủ, cần đồng bộ hóa proxy và cookies. Scrapy lưu trữ cookies riêng cho mỗi miền, nhưng khi thay đổi proxy cần xóa cookies:

# settings.py

# Bật cookie middleware
COOKIES_ENABLED = True
COOKIES_DEBUG = False

# Middleware để đồng bộ hóa proxy và cookies
class ProxyCookieMiddleware:
    def process_request(self, request, spider):
        # Lấy proxy hiện tại
        current_proxy = request.meta.get('proxy')
        
        # Nếu proxy đã thay đổi — xóa cookies
        previous_proxy = request.meta.get('previous_proxy')
        
        if previous_proxy and previous_proxy != current_proxy:
            # Xóa cookies cho miền này
            jar = spider.crawler.engine.downloader.middleware.middlewares[0].jars
            domain = urlparse(request.url).netloc
            
            if domain in jar:
                jar[domain].clear()
                spider.logger.info(f'Đã xóa cookies cho {domain}')
        
        request.meta['previous_proxy'] = current_proxy

Kết luận

Cài đặt proxy đúng cách trong Scrapy là nền tảng cho việc phân tích ổn định mà không bị chặn. Chúng ta đã xem xét tất cả các khía cạnh chính: từ tích hợp cơ bản đến các kỹ thuật nâng cao về xoay vòng và quản lý phiên.

Những điểm chính:

  • Đối với sản xuất, hãy sử dụng middleware tùy chỉnh với xoay vòng thông minh và danh sách đen các IP gặp vấn đề
  • Xử lý tất cả các loại lỗi: mã trạng thái HTTP, ngoại lệ mạng, chặn theo nội dung
  • Chọn loại proxy phù hợp với nhiệm vụ: trung tâm dữ liệu cho các trang đơn giản, cư trú cho các trang bảo mật
  • Đối với các trang yêu cầu xác thực, hãy sử dụng các phiên dính với proxy gán cho miền
  • Bắt đầu với nhóm 10-50 proxy, mở rộng khi tải tăng lên

Nếu bạn dự định phân tích các trang web bảo mật (thương mại điện tử, mạng xã hội, các trang với Cloudflare), tôi khuyên bạn nên sử dụng proxy cư trú — chúng cung cấp tính ẩn danh tối đa và rủi ro bị chặn tối thiểu. Đối với phân tích tốc độ cao, hãy chọn các nhà cung cấp có endpoint xoay vòng và nhóm từ 10,000 địa chỉ IP.

Tất cả các ví dụ mã trong bài viết này đã được kiểm tra trên Scrapy 2.x và sẵn sàng để sử dụng trong sản xuất. Hãy điều chỉnh chúng theo nhu cầu của bạn và mở rộng khi dự án phát triển.

```