Tới nội dung
Cloudj

Ứng dụng proxy

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

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.

Cập nhật · 4 phút đọc

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 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

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 đó.

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:

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:

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.

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.

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.

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:

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ứngNguyên nhân thường gặpCách xử lý
407 Proxy Authentication Required hoặc ProxyErrorSai username, password hoặc ký tự đặc biệt chưa được encodeSao 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êmThêm IP của server vào whitelist
Missing dependencies for SOCKS supportChưa cài gói SOCKS đi kèmCài requests[socks] hoặc httpx[socks]
IP đầu ra vẫn là IP của bạnBiến môi trường ghi đè proxy của sessionTruyền proxies= cho từng request hoặc đặt trust_env = False
Nhận nhiều phản hồi 429Thu thập quá nhanhGiả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 và giá cập nhật trên trang bảng giá, 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ử và proxy cho Facebook Ads sẽ hữu ích, còn IP thì bạn đặt trực tiếp trong console.

Xem tiếp

Triển khai máy chủ hoặc mua proxy

Chọn vị trí trong console, xem giá trực tiếp và thanh toán từ số dư tài khoản.