Nếu bạn đang phân tích các trang web hoặc tự động hóa các yêu cầu qua proxy trên Python, sớm hay muộn bạn sẽ gặp phải ba
lỗi cổ điển: ProxyError,
SSLError: WRONG_VERSION_NUMBER và HTTP
407 Proxy Authentication Required. Trong bài viết này, chúng ta sẽ phân tích
nguyên nhân của từng lỗi và cung cấp mã hoạt động để sửa chữa.
Cách requests hoạt động với proxy: cú pháp cơ bản
Thư viện requests nhận proxy thông qua từ điển
proxies, trong đó các khóa là sơ đồ yêu cầu
(http và https),
và các giá trị là URL của máy chủ proxy với xác thực tùy chọn.
import requests
proxies = {
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())
Lưu ý: ngay cả đối với các yêu cầu HTTPS, sơ đồ proxy thường được chỉ định là
http://, vì hầu hết các máy chủ proxy chấp nhận
đường hầm CONNECT qua giao thức HTTP, chứ không phải bọc nó trong SSL. Nếu bạn chỉ định
https:// cho proxy không hỗ trợ điều này — bạn
sẽ nhận được SSLError, phân tích sẽ được trình bày dưới đây.
Nếu bạn làm việc với proxy SOCKS5, bạn sẽ cần một gói bổ sung:
pip install requests[socks]
# Và cú pháp proxies sẽ thay đổi:
proxies = {
"http": "socks5://user:password@proxy_host:port",
"https": "socks5://user:password@proxy_host:port",
}
ProxyError: nguyên nhân và giải pháp
requests.exceptions.ProxyError — là một lớp bao bọc trên các
ngoại lệ mạng cấp thấp xảy ra khi cố gắng thiết lập kết nối với máy chủ proxy. Thường gặp nhất là
bốn kịch bản.
1. Proxy không khả dụng hoặc địa chỉ/port được chỉ định không chính xác
requests.exceptions.ProxyError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.',
ConnectionResetError(104, 'Connection reset by peer')))
Kiểm tra xem máy chủ và cổng đã được nhập chính xác và máy chủ proxy thực sự đang hoạt động. Cách nhanh nhất để chẩn đoán —
thử kết nối qua curl:
curl -x http://user:password@proxy_host:port https://httpbin.org/ip -v
2. Proxy đã chặn yêu cầu của bạn (cấm theo IP hoặc User-Agent)
Các proxy trung tâm dữ liệu thường bị các trang web mục tiêu cấm vì các dải IP của chúng được biết đến bởi các hệ thống chống gian lận. Nếu lỗi xuất hiện ổn định khi truy cập một trang web cụ thể (ví dụ: Wildberries, Ozon hoặc mạng xã hội), chứ không phải tất cả các trang web cùng một lúc — vấn đề không phải ở chính proxy, mà ở danh tiếng của nó. Trong trường hợp này, chuyển sang proxy cư trú — chúng sử dụng IP của người dùng thực và ít bị cấm bởi các hệ thống chống gian lận hơn.
3. Giới hạn kết nối đồng thời đã bị vượt quá
Nếu bạn đang chạy phân tích đa luồng và mở hàng trăm kết nối qua cùng một proxy, máy chủ có thể
bắt đầu từ chối các kết nối dư thừa. Giải pháp — sử dụng một nhóm kết nối qua
requests.adapters.HTTPAdapter với giới hạn
pool_maxsize và
max_retries:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retries = Retry(total=3, backoff_factor=1, status_forcelist=[502, 503, 504])
adapter = HTTPAdapter(max_retries=retries, pool_connections=20, pool_maxsize=20)
session.mount("http://", adapter)
session.mount("https://", adapter)
response = session.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
4. Thời gian chờ ở phía máy chủ proxy
Nếu proxy "treo" lâu hơn thời gian chờ đã thiết lập, bạn sẽ thấy
ProxyError, được bao bọc trong
ReadTimeoutError. Tăng thời gian chờ lên một giá trị hợp lý
(thường 15-30 giây là đủ) và thêm xử lý ngoại lệ riêng biệt:
try:
response = requests.get(url, proxies=proxies, timeout=20)
except requests.exceptions.ProxyError as e:
print(f"Vấn đề với proxy: {e}")
except requests.exceptions.Timeout:
print("Proxy không phản hồi trong thời gian quy định")
SSLError WRONG_VERSION_NUMBER: phân tích vấn đề
Lỗi này — một trong những lỗi khó hiểu nhất ở cái nhìn đầu tiên, nhưng thực tế nó gần như luôn có một nguyên nhân.
requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL:
WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)')))
WRONG_VERSION_NUMBER có nghĩa là khách hàng đã cố gắng thực hiện
SSL/TLS handshake ở nơi mà máy chủ đang chờ đợi lưu lượng HTTP thông thường (không mã hóa). Thường thì nguyên nhân nằm ở sơ đồ proxy không chính xác
được chỉ định trong từ điển proxies.
Lỗi thường gặp: chỉ định https:// thay vì
http:// trong địa chỉ máy chủ proxy. Hầu hết các proxy HTTP(S)
chấp nhận kết nối qua HTTP thông thường, ngay cả khi chúng đang proxy hóa lưu lượng HTTPS thông qua cơ chế CONNECT.
# KHÔNG ĐÚNG — sẽ gây ra WRONG_VERSION_NUMBER
proxies = {
"http": "https://user:password@proxy_host:port",
"https": "https://user:password@proxy_host:port",
}
# ĐÚNG
proxies = {
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
}
Nguyên nhân thứ hai — cổng mà về mặt vật lý không hỗ trợ TLS. Ví dụ, nếu nhà cung cấp proxy đã chỉ định cổng 8080 chỉ cho lưu lượng HTTP, và bạn đang cố gắng thiết lập kết nối TLS trực tiếp đến nó (bỏ qua CONNECT), máy chủ sẽ phản hồi bằng văn bản thông thường thay vì TLS handshake, và thư viện khách OpenSSL sẽ đưa ra lỗi này. Kiểm tra tài liệu của nhà cung cấp proxy của bạn để biết các cổng và giao thức (HTTP/HTTPS/SOCKS5) nào có sẵn trên mỗi cổng riêng lẻ.
Biến thể thứ ba — bạn đang sử dụng socket hoặc các
thư viện bên ngoài để kết nối TCP trực tiếp với proxy, bỏ qua cơ chế tiêu chuẩn của requests. Trong trường hợp này, hãy đảm bảo rằng
lớp bao bọc TLS chỉ được áp dụng cho trang web mục tiêu thực sự, chứ không phải cho chính kết nối với proxy.
407 Proxy Authentication Required: xác thực
Mã phản hồi 407 — tương tự như 401 tiêu chuẩn, nhưng dành cho các máy chủ proxy. Nó có nghĩa là máy chủ yêu cầu xác thực, và bạn đã không cung cấp hoặc cung cấp ở định dạng không chính xác.
Cách 1: đăng nhập và mật khẩu trực tiếp trong URL
proxies = {
"http": "http://login:password@proxy_host:port",
"https": "http://login:password@proxy_host:port",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
Phương pháp này là đơn giản nhất, nhưng nếu trong tên đăng nhập hoặc mật khẩu có ký tự đặc biệt (ví dụ: @,
:, /),
chúng cần được mã hóa thông qua urllib.parse.quote:
from urllib.parse import quote
login = quote("user@domain")
password = quote("p@ssw:ord!")
proxy_url = f"http://{login}:{password}@proxy_host:port"
proxies = {"http": proxy_url, "https": proxy_url}
Cách 2: tiêu đề Proxy-Authorization thủ công
Nếu nhà cung cấp proxy sử dụng sơ đồ xác thực không chuẩn hoặc bạn cần nhiều quyền kiểm soát hơn, bạn có thể tạo tiêu đề thủ công thông qua mã hóa base64:
import base64
import requests
login = "user"
password = "password"
credentials = base64.b64encode(f"{login}:{password}".encode()).decode()
headers = {
"Proxy-Authorization": f"Basic {credentials}"
}
proxies = {
"http": "http://proxy_host:port",
"https": "http://proxy_host:port",
}
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
headers=headers,
timeout=10
)
Quan trọng: một số nhà cung cấp proxy liên kết xác thực không với tên đăng nhập/mật khẩu, mà với một IP tĩnh cụ thể của khách hàng (whitelist). Nếu bạn thay đổi IP (ví dụ: làm việc qua VPN hoặc IP động của nhà cung cấp), lỗi 407 có thể xuất hiện ngay cả khi thông tin xác thực đúng — vì IP bên ngoài hiện tại của bạn không được đưa vào danh sách trắng ở phía proxy. Giải pháp — truy cập vào tài khoản cá nhân của nhà cung cấp và cập nhật danh sách IP được phép, hoặc chuyển sang xác thực bằng tên đăng nhập/mật khẩu thay vì IP-whitelist.
Logic Retry và xử lý ngoại lệ
Trong thực tế, các lỗi proxy hiếm khi là một lần — đặc biệt là khi phân tích hàng loạt. Kiến trúc mã đúng nên bao gồm các thử lại với độ trễ và ghi lại từng lỗi riêng biệt.
import time
import requests
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def fetch_with_retry(url, proxies, max_retries=3, backoff=2):
for attempt in range(1, max_retries + 1):
try:
response = requests.get(url, proxies=proxies, timeout=15)
response.raise_for_status()
return response
except requests.exceptions.ProxyError as e:
logger.warning(f"[Cố gắng {attempt}] ProxyError: {e}")
except requests.exceptions.SSLError as e:
logger.warning(f"[Cố gắng {attempt}] SSLError: {e}")
except requests.exceptions.HTTPError as e:
if response.status_code == 407:
logger.error("407: kiểm tra tên đăng nhập/mật khẩu hoặc whitelist IP proxy")
break
logger.warning(f"[Cố gắng {attempt}] HTTPError: {e}")
except requests.exceptions.Timeout:
logger.warning(f"[Cố gắng {attempt}] Thời gian chờ kết nối")
time.sleep(backoff * attempt)
raise RuntimeError(f"Không thể lấy {url} sau {max_retries} lần thử")
Cách tiếp cận này tiết kiệm hàng giờ gỡ lỗi: thay vì làm rơi kịch bản vào lần gặp sự cố mạng đầu tiên, bạn nhận được một nhật ký rõ ràng chỉ ra loại lỗi cụ thể trong mỗi lần thử.
Xoay vòng proxy và chống lại việc bị cấm
Nếu ProxyError và 407 thường liên quan đến cài đặt mã, thì việc bị cấm hàng loạt và từ chối kết nối ổn định trên các miền cụ thể gần như luôn chỉ ra vấn đề với chất lượng của chính IP pool. Đối với các nhiệm vụ như phân tích các thị trường (Wildberries, Ozon, Avito) hoặc làm việc với mạng xã hội, nên xoay vòng IP cho mỗi yêu cầu hoặc qua khoảng thời gian nhất định.
import random
proxy_pool = [
"http://user:[email protected]:8000",
"http://user:[email protected]:8000",
"http://user:[email protected]:8000",
]
def get_random_proxy():
proxy = random.choice(proxy_pool)
return {"http": proxy, "https": proxy}
response = requests.get(url, proxies=get_random_proxy(), timeout=15)
Đối với các trình phân tích sản xuất, thường thuận tiện hơn khi lấy proxy với việc tự động xoay vòng từ phía nhà cung cấp — như vậy không cần lưu trữ danh sách IP một cách riêng biệt, việc xoay vòng diễn ra một cách minh bạch qua một endpoint. Trong trường hợp này, proxy cư trú với việc xoay vòng đặc biệt phù hợp để vượt qua bảo vệ chống bot trên các trang web có hệ thống phát hiện nghiêm ngặt, và proxy trung tâm dữ liệu — là lựa chọn tốt, nếu tốc độ yêu cầu là quan trọng và trang web mục tiêu không chặn các dải trung tâm dữ liệu một cách quyết liệt.
| Loại proxy | Tốc độ | Rủi ro bị cấm | Nhiệm vụ |
|---|---|---|---|
| Trung tâm dữ liệu | Cao | Trung bình-cao | Phân tích tốc độ API mở |
| Cư trú | Trung bình | Thấp | Phân tích các thị trường, mạng xã hội |
| Di động | Trung bình-thấp | Tối thiểu | Làm việc với các hệ thống chống bot nghiêm ngặt |
Session, thời gian chờ và xác thực SSL
Đối với các kịch bản dài hạn (ví dụ: các trình phân tích vòng lặp), bạn nên sử dụng
requests.Session() thay vì các cuộc gọi riêng lẻ
requests.get(). Session tái sử dụng các kết nối TCP,
điều này giảm tải cho máy chủ proxy và giảm số lượng ProxyError trong các khối lượng yêu cầu lớn.
session = requests.Session()
session.proxies.update({
"http": "http://user:password@proxy_host:port",
"https": "http://user:password@proxy_host:port",
})
for url in urls_list:
try:
resp = session.get(url, timeout=15)
print(resp.status_code, url)
except requests.exceptions.RequestException as e:
print(f"Lỗi tại {url}: {e}")
Riêng biệt cần đề cập đến tham số verify. Nếu bạn làm việc
với proxy, mà tự thay thế chứng chỉ SSL (proxy MITM để gỡ lỗi hoặc một số proxy doanh nghiệp),
requests có thể cảnh báo về sự không khớp của chứng chỉ. Tắt xác thực hoàn toàn
(verify=False) chỉ nên thực hiện trong môi trường thử nghiệm — trong sản xuất
điều này tạo ra rủi ro cho các cuộc tấn công "người đàn ông ở giữa":
# Chỉ dành cho gỡ lỗi cục bộ!
response = requests.get(url, proxies=proxies, verify=False, timeout=15)
# Cách đúng — chỉ định đường dẫn đến chứng chỉ tin cậy của proxy
response = requests.get(url, proxies=proxies, verify="/path/to/proxy_cert.pem")
Danh sách kiểm tra chẩn đoán lỗi
Nếu kịch bản bị rơi với lỗi proxy, hãy kiểm tra danh sách này từ trên xuống dưới — trong 90% trường hợp, nguyên nhân sẽ được tìm thấy trong ba điểm đầu tiên.
- Kiểm tra sơ đồ trong từ điển
proxies— hầu như luôn phải làhttp://, ngay cả đối với các yêu cầu HTTPS. - Kiểm tra proxy trực tiếp qua
curl -x, để loại trừ vấn đề ở phía mã. - Đảm bảo rằng tên đăng nhập và mật khẩu không chứa các ký tự đặc biệt không được mã hóa trong URL.
- Kiểm tra xem proxy có bị ràng buộc với một IP cụ thể (whitelist) thay vì xác thực bằng tên đăng nhập/mật khẩu hay không.
- Tăng
timeoutlên 15-30 giây trước khi coi proxy không hoạt động. - Nếu lỗi chỉ xuất hiện trên một trang web cụ thể — vấn đề nằm ở danh tiếng IP, không phải ở mã.
- Thêm logic retry với độ trễ mũi tên thay vì cố gắng một lần.
- Trong phân tích hàng loạt, sử dụng xoay vòng IP, chứ không phải một proxy tĩnh cho tất cả các yêu cầu.
Kết luận
Hầu hết các lỗi proxy trong Python requests đều liên quan đến ba điều: sơ đồ không chính xác trong URL proxy
(http thay vì https),
thiếu hoặc định dạng xác thực không chính xác, và chất lượng của chính IP pool. ProxyError thường chỉ ra vấn đề
kết nối hoặc bị cấm IP, SSLError WRONG_VERSION_NUMBER gần như luôn chỉ ra sơ đồ proxy không chính xác, còn 407 —
chỉ ra vấn đề với tên đăng nhập/mật khẩu hoặc whitelist.
Nếu bạn thường xuyên gặp phải việc bị cấm và lỗi 407 khi phân tích các thị trường hoặc mạng xã hội, bạn nên xem xét lại nguồn proxy, không chỉ mã. Để phân tích ổn định với tỷ lệ chặn thấp, chúng tôi khuyên bạn nên thử proxy cư trú — chúng sử dụng các địa chỉ IP thực và ít gây ra ProxyError trên các trang web bảo vệ. Nếu tốc độ và khối lượng yêu cầu là ưu tiên, thay vì vượt qua các hệ thống chống bot phức tạp, hãy xem xét đến proxy trung tâm dữ liệu — chúng rẻ hơn và nhanh hơn cho các nhiệm vụ phân tích đơn giản.