# Dùng proxy trong Python: hướng dẫn requests, httpx và Scrapy

URL: https://cloudj.net/vi/blog/python-scraping-proxy
Published: 2026-11-11 · Updated: 2026-11-11

> Cách dùng proxy trong Python với requests, httpx và Scrapy: xác thực, SOCKS5, timeout, retry và xử lý lỗi đủ chắc chắn cho các job thu thập dữ liệu thật.

Dùng proxy trong Python chỉ cần một dictionary, nhưng để chạy ổn trong một job thật thì cần thêm vài thứ: mật khẩu có ký tự đặc biệt, hỗ trợ SOCKS5, timeout, retry và thông báo lỗi rõ ràng khi chính proxy mới là nguyên nhân. Bài này đi qua các ví dụ chạy được cho `requests`, `httpx` và Scrapy với một proxy riêng (dedicated proxy) xác thực bằng username và password. Mọi đoạn code đều dùng giá trị giữ chỗ (`HOST`, `PORT`, `USERNAME`, `PASSWORD`) để bạn thay bằng thông tin từ nhà cung cấp proxy.

## Trước khi viết code: thu thập dữ liệu có trách nhiệm

Proxy chỉ thay đổi nơi request của bạn xuất phát. Nó không thay đổi những gì bạn được phép làm. Trước khi cho script chạy vào bất kỳ website nào, hãy:

- **Đọc điều khoản sử dụng (terms of service).** Nhiều trang hạn chế truy cập tự động. Nếu trang có API hoặc data feed, hãy dùng chúng.
- **Kiểm tra `robots.txt`** và tôn trọng các đường dẫn bị chặn với user agent của bạn.
- **Tự giới hạn tốc độ.** Giữ số kết nối đồng thời trên mỗi domain ở mức thấp, thêm độ trễ giữa các request và lùi lại khi server trả về `429 Too Many Requests` hoặc `503`.
- **Tự giới thiệu** bằng một `User-Agent` trung thực, kèm cách liên hệ khi phù hợp.
- **Tránh dữ liệu cá nhân** nếu bạn không có cơ sở pháp lý để thu thập.

Proxy riêng hữu ích ở đây vì những lý do rất thực tế: một IP đầu ra cố định để đối tác đưa vào allowlist, xem trang công khai theo góc nhìn của một quốc gia cụ thể, hoặc tách IP của server khỏi traffic thu thập dữ liệu. Bài [hướng dẫn proxy riêng](https://cloudj.net/vi/blog/dedicated-proxy-guide) giải thích IP riêng khác gì so với proxy dùng chung và proxy xoay vòng.

## Bước 1: cài thư viện

```bash
pip install "requests[socks]" "httpx[socks]" scrapy
```

Phần `[socks]` cài thêm thư viện hỗ trợ SOCKS mà mỗi gói cần. Bạn chỉ cần chúng khi định dùng SOCKS5; proxy HTTP chạy được mà không cần.

## Bước 2: dùng proxy trong Python với requests và xác thực

`requests` nhận một dictionary `proxies` với key là scheme của URL đích, value là URL của proxy. Lưu ý URL proxy bắt đầu bằng `http://` kể cả khi trang đích là HTTPS: client mở một đường hầm tới proxy bằng phương thức `CONNECT`, và TLS chạy trọn vẹn bên trong đường hầm đó.

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

USERNAME = "USERNAME"
PASSWORD = quote("PASSWORD", safe="")  # encode characters such as @ : /
PROXY = f"http://{USERNAME}:{PASSWORD}@HOST:PORT"

proxies = {"http": PROXY, "https": PROXY}

resp = requests.get(
    "https://api.ipify.org?format=json",
    proxies=proxies,
    timeout=(5, 20),  # (connect, read) seconds
)
resp.raise_for_status()
print(resp.json())  # should print the proxy IP, not yours
```

Hai chi tiết giúp bạn tiết kiệm hàng giờ debug. Thứ nhất, hãy URL-encode mật khẩu; một ký tự `@` hay `:` trong mật khẩu sẽ làm hỏng URL mà không báo lỗi rõ ràng. Thứ hai, luôn đặt timeout. Không có timeout, `requests` có thể chờ vô thời hạn trên một kết nối bị treo.

### Dùng Session

Khi gửi nhiều request, hãy dùng lại một `Session` để giữ kết nối:

```python
session = requests.Session()
session.trust_env = False  # ignore HTTP_PROXY / HTTPS_PROXY from the environment
session.proxies.update(proxies)
session.headers["User-Agent"] = "example-bot/1.0 (+https://example.com/contact)"

resp = session.get("https://example.com/", timeout=(5, 20))
```

Đặt `trust_env = False` quan trọng vì biến môi trường về proxy có thể được ưu tiên hơn `session.proxies`. Tùy chọn này cũng khiến `requests` bỏ qua `.netrc` và các biến về CA bundle, nên chỉ bật khi bạn không phụ thuộc vào chúng.

### SOCKS5

Nếu đơn proxy của bạn đang để giao thức SOCKS5, chỉ cần đổi scheme. Dùng `socks5h` để tên miền được phân giải ở phía proxy thay vì trên máy bạn:

```python
PROXY = f"socks5h://{USERNAME}:{PASSWORD}@HOST:PORT"
proxies = {"http": PROXY, "https": PROXY}
```

Ưu nhược điểm của hai giao thức được so sánh kỹ trong bài [SOCKS5 và HTTP proxy](https://cloudj.net/vi/blog/socks5-vs-http-proxy).

## Bước 3: xử lý lỗi và retry lịch sự

Hãy tách lỗi do proxy khỏi lỗi do website. `ProxyError` thường có nghĩa là sai thông tin đăng nhập, IP máy bạn chưa nằm trong whitelist, hoặc đơn hàng đã hết hạn; retry không giải quyết được. Timeout và lỗi `5xx` thường chỉ tạm thời, đáng để thử lại vài lần với thời gian chờ tăng dần.

```python
import time
import requests

def fetch(url, proxies, attempts=3):
    for attempt in range(1, attempts + 1):
        try:
            resp = requests.get(url, proxies=proxies, timeout=(5, 20))
        except requests.exceptions.ProxyError as exc:
            raise RuntimeError("Proxy rejected the connection: check credentials or whitelist") from exc
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError):
            time.sleep(2 ** attempt)
            continue

        if resp.status_code == 429 or resp.status_code >= 500:
            retry_after = resp.headers.get("Retry-After", "")
            time.sleep(int(retry_after) if retry_after.isdigit() else 2 ** attempt)
            continue

        resp.raise_for_status()
        return resp

    raise RuntimeError(f"Giving up on {url} after {attempts} attempts")
```

`ProxyError` là lớp con của `ConnectionError`, nên phải bắt nó trước. Hãy tôn trọng header `Retry-After` khi server gửi về, và dừng sau vài lần thử thay vì tiếp tục dội request vào một trang đang yêu cầu bạn chậm lại.

## Bước 4: cấu hình tương tự với httpx

`httpx` nhận một tham số `proxy` duy nhất trên client (tham số `proxies` cũ đã bị bỏ ở các phiên bản gần đây). Thư viện này hỗ trợ async, tiện khi bạn cần vài request song song.

```python
import asyncio
import httpx

PROXY = "http://USERNAME:PASSWORD@HOST:PORT"

async def main(urls):
    limits = httpx.Limits(max_connections=2)
    async with httpx.AsyncClient(proxy=PROXY, timeout=20.0, limits=limits) as client:
        for url in urls:
            try:
                r = await client.get(url)
                r.raise_for_status()
                print(url, len(r.text))
            except httpx.ProxyError as exc:
                print("Proxy problem:", exc)
                break
            except httpx.HTTPError as exc:
                print("Request failed:", url, exc)
            await asyncio.sleep(1)

asyncio.run(main(["https://api.ipify.org?format=json"]))
```

Với SOCKS5, cài `httpx[socks]` và dùng URL proxy dạng `socks5://`. Hãy giữ `max_connections` ở mức nhỏ; async khiến bạn rất dễ gửi lượng traffic vượt xa mức một website chấp nhận.

## Bước 5: Scrapy

`HttpProxyMiddleware` có sẵn trong Scrapy đọc proxy từ `request.meta["proxy"]` và chuyển thông tin đăng nhập trong URL thành header `Proxy-Authorization`. Kết hợp nó với các thiết lập "lịch sự" của Scrapy:

```python
import scrapy

PROXY = "http://USERNAME:PASSWORD@HOST:PORT"

class CatalogSpider(scrapy.Spider):
    name = "catalog"
    custom_settings = {
        "ROBOTSTXT_OBEY": True,
        "DOWNLOAD_DELAY": 2,
        "CONCURRENT_REQUESTS_PER_DOMAIN": 1,
        "AUTOTHROTTLE_ENABLED": True,
        "USER_AGENT": "example-bot/1.0 (+https://example.com/contact)",
    }

    def start_requests(self):
        yield scrapy.Request("https://example.com/catalog", meta={"proxy": PROXY})

    def parse(self, response):
        for href in response.css("a.product::attr(href)").getall():
            yield response.follow(href, callback=self.parse_item, meta={"proxy": PROXY})

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

Gán `meta["proxy"]` chỉ áp dụng cho các request do bạn tạo. Muốn mọi request đều đi qua proxy mà không phải sửa từng cái, `HttpProxyMiddleware` cũng đọc các biến môi trường chuẩn `http_proxy` và `https_proxy`. Scrapy không hỗ trợ sẵn proxy SOCKS, nên hãy dùng proxy HTTP khi chạy Scrapy.

## Bảng tra cứu lỗi nhanh

| Triệu chứng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| `407 Proxy Authentication Required` hoặc `ProxyError` | Sai username, password hoặc ký tự đặc biệt chưa được encode | Sao chép lại thông tin đăng nhập; URL-encode mật khẩu |
| Chạy được trên laptop, lỗi trên server | Đơn hàng dùng IP whitelist và IP server chưa được thêm | Thêm IP của server vào whitelist |
| `Missing dependencies for SOCKS support` | Chưa cài gói SOCKS đi kèm | Cài `requests[socks]` hoặc `httpx[socks]` |
| IP đầu ra vẫn là IP của bạn | Biến môi trường ghi đè proxy của session | Truyền `proxies=` cho từng request hoặc đặt `trust_env = False` |
| Nhận nhiều phản hồi `429` | Thu thập quá nhanh | Giảm số kết nối đồng thời, thêm độ trễ, tôn trọng `Retry-After` |

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

### URL proxy nên bắt đầu bằng http hay https khi truy cập trang HTTPS?

Dùng `http://` cho proxy HTTP thông thường. Kết nối HTTPS tới website được tạo đường hầm qua proxy bằng `CONNECT`, nên dữ liệu của bạn vẫn được mã hóa đầu cuối.

### Làm sao dùng IP whitelist thay cho mật khẩu?

Thêm IP công khai của máy chạy script vào whitelist trong console (tối đa 20 địa chỉ), rồi bỏ phần đăng nhập khỏi URL proxy: `http://HOST:PORT`.

### Scrapy có dùng được SOCKS5 không?

Không hỗ trợ sẵn. Khi chạy Scrapy, hãy để đơn proxy ở giao thức HTTP.

### Proxy có giúp tôi không bị chặn không?

Không. Proxy không ngăn được việc bị chặn. Tốc độ hợp lý, tự giới thiệu trung thực và tuân thủ quy định của website quan trọng hơn nhiều.

## Bước tiếp theo

Cloudj cung cấp proxy IPv4 riêng tại bảy quốc gia, xác thực bằng username và password hoặc IP whitelist; xem [trang tổng quan proxy](https://cloudj.net/vi/proxy-ipv4) và giá cập nhật trên [trang bảng giá](https://cloudj.net/vi/pricing), bạn có thể nạp tiền bằng chuyển khoản VND. Nếu bạn còn quản lý gian hàng hay tài khoản quảng cáo, các bài [proxy cho người bán hàng thương mại điện tử](https://cloudj.net/vi/blog/ecommerce-seller-proxy-vietnam) và [proxy cho Facebook Ads](https://cloudj.net/vi/blog/facebook-ads-proxy) sẽ hữu ích, còn IP thì bạn đặt trực tiếp trong [console](https://app.cloudj.net).
