# Scrapy proxy middleware cho proxy dân cư quay vòng

> Middleware quay vòng proxy cho Scrapy: quay vòng và cố định IP dân cư theo từng yêu cầu, AutoThrottle và cài đặt retry, scrapy-playwright. Code đã check cú pháp.

- URL: https://crawlrelay.com/vi/docs/scrapy/
- Cập nhật lần cuối: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

Middleware downloader 30 dòng chọn quốc gia và phiên làm việc theo yêu cầu, cộng với cài đặt giữ crawl lịch sự: robots.txt, concurrency theo domain, AutoThrottle và retry.

**Trả lời nhanh — Làm sao để dùng proxy quay vòng với Scrapy?**

Đặt `request.meta["proxy"]` thành `http://USERNAME:PASSWORD@gw.crawlrelay.com:7777` trong middleware downloader nhỏ chạy trước `HttpProxyMiddleware` của Scrapy; Scrapy sẽ gửi chứng chỉ dùm bạn. Với **CrawlRelay** proxy dân cư, quốc gia và session cố định được để trong username, nên một middleware xử lý được cả hai. Lưu lượng tiêu tốn 0,50 US$/GB từ 50 GB. Thanh toán bằng crypto, không cần KYC.

## Thiết lập Scrapy nhìn tổng quan

| Thuộc tính | Giá trị |
| --- | --- |
| Nơi đặt proxy | `request.meta["proxy"]`, do middleware bạn cài đặt |
| Thứ tự middleware | Middleware của bạn phải dưới 750, là độ ưu tiên của `HttpProxyMiddleware` tích hợp |
| Thông tin xác thực | Trong URL proxy; Scrapy chuyển thành header `Proxy-Authorization` |
| SOCKS5 | Không với handler HTTP/1.1 mặc định; Scrapy 2.17+ hỗ trợ SOCKS qua HttpxDownloadHandler tùy chọn. Cổng HTTP 7777 đơn giản nhất |
| Xoay vòng | Exit mới cho mỗi kết nối mới; session ID duy nhất mỗi yêu cầu thường cho IP khác nhau |
| Lịch sự | `ROBOTSTXT_OBEY`, `CONCURRENT_REQUESTS_PER_DOMAIN`, `AUTOTHROTTLE_ENABLED` |
| Retry | 429 và 5xx có trong `RETRY_HTTP_CODES` mặc định; Retry-After không được đọc mặc định |

## Làm sao để viết proxy middleware?

Đặt class trong `middlewares.py`. Nó tạo username từ cài đặt và ba khóa `meta` tùy chọn: `proxy_country`, `proxy_session` và `proxy_ttl`. Yêu cầu không có `proxy_session` sẽ quay vòng; có `proxy_session="new"` sẽ có session mới, thường IP exit khác; có session ID cố định chia sẻ exit với các yêu cầu cùng luồng. Middleware tính lại proxy mỗi lần đi qua, kể cả retry, và không chạm proxy do code khác lập. Nếu thiếu `CRAWLRELAY_USER`, crawl dừng khi khởi chạy thay vì chạy từ IP của bạn.

*middlewares.py*

```python
import uuid
from urllib.parse import quote


class CrawlRelayProxyMiddleware:
    """Sets request.meta["proxy"]; Scrapy's HttpProxyMiddleware then sends the credentials."""

    def __init__(self, user, password, country):
        self.user, self.password, self.country = user, password, country

    @classmethod
    def from_crawler(cls, crawler):
        s = crawler.settings
        if not s.get("CRAWLRELAY_USER"):
            # Fail loudly: NotConfigured would only disable the middleware and crawl from your own IP
            raise ValueError("CRAWLRELAY_USER is not set: refusing to crawl without the proxy")
        return cls(s.get("CRAWLRELAY_USER"), s.get("CRAWLRELAY_PASSWORD", ""), s.get("CRAWLRELAY_COUNTRY", "us"))

    def process_request(self, request, spider=None):
        if "proxy" in request.meta and not request.meta.get("_crawlrelay"):
            return None  # a proxy set by other code: leave it alone
        parts = [self.user, "country", request.meta.get("proxy_country", self.country)]
        session = request.meta.get("proxy_session")
        if session == "new":  # a fresh session for this request (and for each of its retries)
            session = uuid.uuid4().hex[:12]
        if session:
            parts += ["session", session, "ttl", str(request.meta.get("proxy_ttl", 10))]
        username = "-".join(parts)
        request.meta["proxy"] = "http://%s:%s@gw.crawlrelay.com:7777" % (
            quote(username, safe=""), quote(self.password, safe=""))
        request.meta["_crawlrelay"] = True
        return None
```

## Các cài đặt Scrapy nào giữ cho crawl qua proxy lịch sự và nhanh?

Proxy phân bố yêu cầu của bạn qua nhiều IP, nhưng mục tiêu vẫn tính tổng tần suất. Các cài đặt này giữ mỗi domain với tốc độ phù hợp và để AutoThrottle tự giảm tốc khi phản hồi chậm lại. Giữ `ROBOTSTXT_OBEY = True`, Scrapy mới đã có mặc định.

*settings.py (trích)*

```python
import os

BOT_NAME = "myproject"
ROBOTSTXT_OBEY = True
USER_AGENT = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"

CRAWLRELAY_USER = os.environ.get("CRAWLRELAY_USER")  # e.g. the username from your dashboard
CRAWLRELAY_PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD")
CRAWLRELAY_COUNTRY = "us"

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.CrawlRelayProxyMiddleware": 350,  # runs before HttpProxyMiddleware (750)
}

CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 4
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 30.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0

RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 522, 524, 408]
DOWNLOAD_TIMEOUT = 45
```

## Làm sao để giữ một IP cho phân trang và xoay IP cho các trang mục?

Truyền một `proxy_session` cố định vào các yêu cầu trong một luồng phụ thuộc vào trang trước đó, và không dùng cho các trang độc lập. Spider dưới đây chạy trên books.toscrape.com, một trang sandbox được tạo ra để luyện tập scraping.

*spiders/catalog.py*

```python
import scrapy


class CatalogSpider(scrapy.Spider):
    name = "catalog"
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for href in response.css("article.product_pod h3 a::attr(href)").getall():
            # Product pages are independent: rotating exits
            yield response.follow(href, callback=self.parse_item)
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            # The listing flow keeps one sticky exit for 30 minutes
            yield response.follow(next_page, callback=self.parse,
                                  meta={"proxy_session": "listing1", "proxy_ttl": 30})

    def parse_item(self, response):
        yield {
            "title": response.css("div.product_main h1::text").get(),
            "price": response.css("p.price_color::text").get(),
            "url": response.url,
        }
```

## Scrapy có tái sử dụng kết nối không, và điều đó có làm dừng việc xoay IP không?

Có, Scrapy giữ kết nối luôn hoạt động và tái sử dụng theo proxy và host mục tiêu, nên các yêu cầu liên tiếp trên một kết nối dùng chung một điểm ra. Điều này thường ổn với hầu hết các crawl: Scrapy mở tới `CONCURRENT_REQUESTS_PER_DOMAIN` kết nối mỗi site, và mỗi kết nối mới thường có một IP proxy dân cư khác.

Khi một yêu cầu cần đi ra qua một IP mới, ví dụ khi thử lại sau lỗi [502](https://crawlrelay.com/vi/docs/errors/502-bad-gateway/) do exit bị lỗi, hãy gửi lại kèm `response.request.replace(meta={**response.meta, "proxy_session": "new"}, dont_filter=True)`. Middleware sẽ tạo một session username mới, username khác nghĩa là pool kết nối khác, vậy Scrapy mở một kết nối mới và gateway thường gán exit khác.

## Làm sao để tôn trọng Retry-After trên các phản hồi 429 trong Scrapy?

Scrapy có sẵn `RetryMiddleware` để thử lại với 429 nhưng không chờ delay `Retry-After`. AutoThrottle sẽ giảm tốc độ domain dần theo thời gian, thường là đủ. Với API gửi giá trị `Retry-After` dài, bạn cần thêm middleware nhỏ đọc header và lên lịch thử lại có delay, hoặc giảm `CONCURRENT_REQUESTS_PER_DOMAIN` cho domain đó. Trang [429](https://crawlrelay.com/vi/docs/errors/429-too-many-requests/) giải thích ý nghĩa header này.

## Làm thế nào để dùng proxy với scrapy-playwright?

Với các trang cần JavaScript, scrapy-playwright truyền các tuỳ chọn launch của Playwright qua thiết lập. Cung cấp thông tin đăng nhập riêng biệt: Chromium bỏ qua thông tin trong URL server. Dùng một context cho mỗi session cố định nếu bạn render luồng đăng xuất trải qua nhiều trang.

*settings.py cho scrapy-playwright*

```python
import os

DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

PLAYWRIGHT_LAUNCH_OPTIONS = {
    "proxy": {
        "server": "http://gw.crawlrelay.com:7777",
        "username": os.environ.get("CRAWLRELAY_USER", "USERNAME") + "-country-us",
        "password": os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD"),
    },
}
```

## Scrapy và proxy: các câu hỏi

### Tôi có cần scrapy-rotating-proxies hoặc danh sách proxy không?

Không. Các package đó xoay danh sách các địa chỉ proxy riêng lẻ và chặn địa chỉ bị lỗi. Với gateway proxy dân cư, việc xoay IP diễn ra phía sau một địa chỉ duy nhất: mỗi kết nối mới thường có IP proxy dân cư khác. Chỉ cần một middleware cài `request.meta["proxy"]` là đủ.

### Tại sao Scrapy báo TunnelError với proxy của tôi?

`TunnelError: Could not open CONNECT tunnel` có nghĩa proxy từ chối hoặc thất bại khi kết nối CONNECT với site HTTPS. Trạng thái trong thông báo cho biết lí do: 407 là lỗi xác thực, 502 hoặc 504 là lỗi exit hoặc server đích. Xem các trang [407](https://crawlrelay.com/vi/docs/errors/407-proxy-authentication-required/) và [502](https://crawlrelay.com/vi/docs/errors/502-bad-gateway/).

### Tôi nên dùng bao nhiêu yêu cầu đồng thời trên mỗi domain?

Bắt đầu với 2 đến 4 yêu cầu mỗi domain và để AutoThrottle điều chỉnh. Tăng số lượng khi tỉ lệ 429, 403 và 5xx giữ ổn định, đồng thời theo dõi thời gian phản hồi site. Site lớn chịu đựng tốt hơn site nhỏ; site nhỏ có thể cần thời gian chờ giữa các yêu cầu và chỉ một kết nối.

### Tôi có nên tắt ROBOTSTXT_OBEY để lấy nhiều dữ liệu hơn?

Không. robots.txt là cách site xác định các đường dẫn mà các client tự động được phép lấy, và RFC 9309 đã chuẩn hóa định dạng này từ 2022. Bỏ qua nó gây rủi ro pháp lý và đạo đức, đồng thời vi phạm [Chính sách Sử dụng Hợp lệ](https://crawlrelay.com/vi/acceptable-use/) của chúng tôi khi dùng để truy cập nội dung site đã loại trừ.

### Bao nhiêu lưu lượng crawl Scrapy sử dụng?

Scrapy chỉ tải HTML nên mỗi trang thường tốn vài chục KB, đã nén. Kiểm tra giá trị `downloader/response_bytes` trong thống kê mà Scrapy in ra cuối quá trình rồi chia cho số lượng phản hồi. Hướng dẫn [băng thông](https://crawlrelay.com/vi/guides/bandwidth-calculator/) giúp bạn ước tính chi phí.

**Cắm IP proxy dân cư vào dự án Scrapy của bạn** 5,94 US$ cho 10 GB để thử, 0,50 US$/GB từ 50 GB. [Xem giá](https://crawlrelay.com/vi/pricing/) · [Hướng dẫn concurrency](https://crawlrelay.com/vi/docs/concurrency/)

---

CrawlRelay: CrawlRelay là dịch vụ proxy dân cư xoay vòng và proxy di động 4G/5G dành cho nhà phát triển, hỗ trợ scrapers và pipeline dữ liệu AI: traffic trả tiền theo nhu cầu không hết hạn từ 0,50 US$/GB, thanh toán bằng crypto, không cần tải lên ID. Không cần KYC — không cần ID, không chụp selfie, không giấy tờ.
