Câu trả lời ngắn: cấu hình SOCKS5 ở HTTP client hoặc trình duyệt để lấy trang, còn request tới CaptchaAI đi thẳng — trừ khi bạn cần token giải từ đúng IP, lúc đó thêm proxy và proxytype=SOCKS5 vào in.php. Nhầm hai đường đi này là lý do phổ biến nhất khiến token hợp lệ mà form vẫn bị từ chối.
Bài này đi qua bốn tình huống thực tế khi làm QA hoặc thu thập dữ liệu web (scraping): requests, aiohttp, Selenium có xác thực proxy, và Puppeteer. Ví dụ dùng reCAPTCHA v2; khuôn mẫu tương tự áp dụng cho Cloudflare Turnstile.
Hai đường đi của request: trang đích và CaptchaAI
Luồng chuẩn gồm bốn bước:
- Gọi trang đích qua SOCKS5 để lấy HTML và
sitekey. - Gửi task tới
in.php— mặc định không qua proxy. - Polling
res.phpcho đến khi có token. - Submit form qua đúng proxy đã dùng ở bước 1.
Bước 4 là nơi phần lớn tích hợp hỏng: tải trang qua proxy Singapore rồi submit từ IP Frankfurt thì token đúng cũng vô ích.
SOCKS5 khác proxy HTTP ở đâu
Proxy HTTP đọc và đôi khi viết lại request; SOCKS5 chỉ chuyển tiếp byte ở tầng TCP/UDP:
| Tiêu chí | Proxy HTTP/HTTPS | Proxy SOCKS5 |
|---|---|---|
| Giao thức hỗ trợ | Chỉ HTTP/HTTPS | Mọi luồng TCP/UDP |
| Can thiệp header | Có thể thêm X-Forwarded-For |
Không sửa đổi |
| Dấu vết trong request | Rõ hơn (header rò rỉ) | Ít hơn |
| Tốc độ | Nhanh | Chậm hơn không đáng kể |
| Xác thực | Basic/Digest | Username/password |
| Phân giải DNS | Phía client | Phía máy chủ proxy (socks5h) |
| Hỗ trợ WebSocket | Hạn chế | Đầy đủ |
Hai dòng đáng quan tâm nhất là DNS và WebSocket. Widget CAPTCHA hiện đại hay mở WebSocket, thứ proxy HTTP xử lý nửa vời. Còn socks5:// phân giải tên miền trên máy bạn, socks5h:// đẩy sang proxy — chữ h cuối là chỗ sai âm thầm nhất.
Thiết lập trong Python
requests + PySocks
Cài phần mở rộng SOCKS cho requests trước:
pip install requests[socks] pysocks
Đoạn dưới gom cả bốn bước: lấy trang qua proxy, trích sitekey, gửi task, polling res.php, submit token.
import requests
import time
SOCKS5_HOST = "proxy.example.com"
SOCKS5_PORT = 1080
SOCKS5_USER = "proxyuser"
SOCKS5_PASS = "proxypass"
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
# SOCKS5 proxy configuration
proxies = {
"http": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
"https": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
}
# socks5h = DNS resolved by proxy server (recommended)
# socks5 = DNS resolved locally
def fetch_through_socks(url):
"""Fetch URL through SOCKS5 proxy."""
return requests.get(
url,
proxies=proxies,
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36"
},
timeout=30,
)
def solve_captcha(site_url, sitekey):
"""Solve CAPTCHA via CaptchaAI (direct, no proxy needed)."""
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
"key": CAPTCHAAI_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": site_url,
"json": 1,
})
data = resp.json()
if data["status"] != 1:
raise Exception(f"Submit: {data['request']}")
task_id = data["request"]
for _ in range(60):
time.sleep(5)
resp = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY,
"action": "get",
"id": task_id,
"json": 1,
})
data = resp.json()
if data["request"] == "CAPCHA_NOT_READY":
continue
if data["status"] == 1:
return data["request"]
raise Exception(f"Solve: {data['request']}")
raise TimeoutError("Timeout")
# Full workflow
resp = fetch_through_socks("https://staging.example.com/qa-form")
import re
match = re.search(r'data-sitekey="([^"]+)"', resp.text)
if match:
token = solve_captcha("https://staging.example.com/qa-form", match.group(1))
# Submit with token through same proxy
resp = requests.post(
"https://target.com/submit",
data={"g-recaptcha-response": token},
proxies=proxies,
)
Vòng polling giới hạn 60 lần × 5 giây, tối đa 300 giây — đủ cho reCAPTCHA v2; với Turnstile có thể siết ngắn hơn.
aiohttp cho luồng bất đồng bộ
Khi chạy nhiều phiên song song, aiohttp_socks cắm connector SOCKS5 vào session:
import aiohttp
import aiohttp_socks
import asyncio
async def fetch_async(url):
connector = aiohttp_socks.ProxyConnector.from_url(
f"socks5://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}"
)
async with aiohttp.ClientSession(connector=connector) as session:
async with session.get(url) as resp:
return await resp.text()
asyncio.run(fetch_async("https://staging.example.com/qa-form"))
Số phiên song song nên bám theo số thread trong gói bạn dùng: BASIC ($15/tháng, 5 thread) cho phép 5 CAPTCHA giải cùng lúc, nên mở 50 coroutine chỉ khiến task xếp hàng chờ.
Selenium: chỗ xác thực proxy gây khó
Chrome không nhận username/password SOCKS5 qua cờ dòng lệnh. Vì vậy đoạn dưới có hai hàm: một cho proxy mở, một dùng Selenium Wire khi proxy cần xác thực.
from selenium import webdriver
from selenium.webdriver.common.by import By
def create_socks5_driver(host, port, username=None, password=None):
options = webdriver.ChromeOptions()
# SOCKS5 proxy (no auth via command line)
options.add_argument(f"--proxy-server=socks5://{host}:{port}")
# DNS through proxy
options.add_argument("--host-resolver-rules=MAP * ~NOTFOUND, EXCLUDE 127.0.0.1")
options.add_argument("")
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome(options=options)
return driver
# For authenticated SOCKS5, use seleniumwire
from seleniumwire import webdriver as sw_webdriver
def create_auth_socks5_driver():
options = {
"proxy": {
"http": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
"https": f"socks5h://{SOCKS5_USER}:{SOCKS5_PASS}@{SOCKS5_HOST}:{SOCKS5_PORT}",
}
}
chrome_options = sw_webdriver.ChromeOptions()
chrome_options.add_argument("")
return sw_webdriver.Chrome(
seleniumwire_options=options,
options=chrome_options,
)
# Usage
driver = create_auth_socks5_driver()
driver.get("https://staging.example.com/qa-form")
time.sleep(3)
sitekey = driver.execute_script(
"return document.querySelector('[data-sitekey]')?.getAttribute('data-sitekey')"
)
if sitekey:
token = solve_captcha("https://staging.example.com/qa-form", sitekey)
driver.execute_script(f"""
document.querySelector('#g-recaptcha-response').value = '{token}';
""")
driver.find_element(By.CSS_SELECTOR, "form").submit()
driver.quit()
Nếu nhà cung cấp cho whitelist IP máy chủ, hãy chọn cách đó — bỏ hẳn được Selenium Wire.
Node.js với socks-proxy-agent
SocksProxyAgent gắn vào cả httpAgent và httpsAgent của axios. Chú ý comment trong code: request tới CaptchaAI cố ý không qua agent này.
const { SocksProxyAgent } = require("socks-proxy-agent");
const axios = require("axios");
const CAPTCHAAI_KEY = "YOUR_API_KEY";
const socksAgent = new SocksProxyAgent(
"socks5h://proxyuser:[email protected]:1080"
);
async function fetchViaSocks(url) {
return axios.get(url, {
httpsAgent: socksAgent,
httpAgent: socksAgent,
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0.0.0",
},
});
}
async function solveCaptcha(siteUrl, sitekey) {
// CaptchaAI calls don't go through SOCKS proxy
const submit = await axios.post(
"https://ocr.captchaai.com/in.php",
null,
{
params: {
key: CAPTCHAAI_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: siteUrl,
json: 1,
},
}
);
const taskId = submit.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: CAPTCHAAI_KEY, action: "get", id: taskId, json: 1 },
});
if (result.data.request === "CAPCHA_NOT_READY") continue;
if (result.data.status === 1) return result.data.request;
}
throw new Error("Timeout");
}
Puppeteer
Puppeteer nhận proxy qua tham số khởi chạy, xác thực đi qua page.authenticate():
const puppeteer = require("puppeteer");
async function launchWithSocks5() {
const browser = await puppeteer.launch({
args: [
"--proxy-server=socks5://proxy.example.com:1080",
"--no-sandbox",
"--window-size=1920,1080",
],
});
const page = await browser.newPage();
// Authenticate if needed
await page.authenticate({
username: "proxyuser",
password: "proxypass",
});
await page.goto("https://staging.example.com/qa-form", { waitUntil: "networkidle0" });
const sitekey = await page.evaluate(() =>
document.querySelector("[data-sitekey]")?.getAttribute("data-sitekey")
);
if (sitekey) {
const token = await solveCaptcha(page.url(), sitekey);
await page.evaluate((t) => {
document.querySelector("#g-recaptcha-response").value = t;
}, token);
}
await browser.close();
}
Một profile trình duyệt chỉ nên gắn một proxy trong suốt vòng đời. Muốn đổi lối ra mạng, đóng browser và mở phiên mới.
Truyền proxy SOCKS5 vào chính CaptchaAI
Với một số trang, token cần sinh ra từ cùng dải IP với phiên của bạn. CaptchaAI nhận proxy kèm task để giải khớp IP:
def solve_with_proxy(site_url, sitekey, proxy_url):
"""Pass proxy to CaptchaAI for IP-matched solving."""
# Format: type:host:port:user:pass
proxy_param = f"socks5:{SOCKS5_HOST}:{SOCKS5_PORT}:{SOCKS5_USER}:{SOCKS5_PASS}"
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
"key": CAPTCHAAI_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": site_url,
"proxy": proxy_param,
"proxytype": "SOCKS5",
"json": 1,
})
data = resp.json()
if data["status"] != 1:
raise Exception(f"Submit: {data['request']}")
task_id = data["request"]
for _ in range(60):
time.sleep(5)
resp = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY, "action": "get",
"id": task_id, "json": 1,
})
data = resp.json()
if data["request"] != "CAPCHA_NOT_READY":
return data["request"]
raise TimeoutError("Timeout")
Định dạng là type:host:port:user:pass, proxytype viết hoa. Chỉ bật khi thật sự cần: proxy của bạn thành điểm nghẽn cho cả bước giải.
Ví dụ thực tế: đội QA ở TP.HCM kiểm thử form đăng ký đa vùng
Một công ty outsourcing tại TP.HCM chạy test hồi quy cho form đăng ký của khách hàng, form này hiện reCAPTCHA v2 khác nhau tùy vùng:
- Mỗi vùng kiểm thử là một endpoint SOCKS5 riêng, khai báo trong biến môi trường của CI.
- Test lấy trang qua
socks5h://, tríchsitekey, gửi task, rồi submit qua cùng proxy. - Chạy 5 kịch bản song song trên BASIC ($15/tháng, 5 thread), ghi thời gian giải theo vùng vào log.
- Log chỉ lưu ID task, vùng và thời gian — không lưu dữ liệu cá nhân trong form. Cách này cũng khớp tinh thần Nghị định 13/2023/NĐ-CP: ghi đủ để truy vết, không ghi thừa.
Các đội theo dõi giá trên Shopee, Lazada, Tiki dùng đúng khuôn này, với rate limit đặt ở phía bạn.
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Connection refused | Sai host hoặc cổng | Kiểm tra máy chủ SOCKS5 còn chạy và cổng 1080 mở |
| Rò rỉ DNS | Dùng socks5:// thay vì socks5h:// |
Đổi sang socks5h:// để proxy phân giải DNS |
| Xác thực thất bại | Sai thông tin đăng nhập | Kiểm tra nhanh bằng curl --socks5-hostname |
| Kết nối chậm | Proxy ở quá xa | Chọn điểm ra gần khu vực kiểm thử hơn |
| WebSocket lỗi | Máy chủ SOCKS5 không hỗ trợ UDP | Dùng máy chủ SOCKS5 có hỗ trợ UDP |
| Token hợp lệ nhưng form bị từ chối | IP submit khác IP tải trang | Submit qua đúng proxy đã dùng ở bước lấy trang |
Câu hỏi thường gặp
socks5 và socks5h khác nhau thế nào?
socks5h để máy chủ proxy phân giải DNS, socks5 phân giải trên máy bạn. Hầu hết trường hợp hãy dùng socks5h để resolver nội bộ không lộ ra ngoài.
CaptchaAI có nhận proxy SOCKS5 trong request gửi task không?
Có. Truyền proxytype=SOCKS5 cùng proxy=socks5:host:port:user:pass vào in.php. Mặc định cứ để request tới CaptchaAI đi thẳng cho nhanh.
Dùng proxy có bị tính thêm phí không?
Không. CaptchaAI tính giá theo thread (số CAPTCHA giải đồng thời), không tính theo từng lần giải và không phụ thu theo loại CAPTCHA. Chi phí proxy là khoản riêng.
Bao nhiêu thread là đủ cho crawler của tôi?
Lấy số phiên chạy song song thực tế làm mốc: 5 phiên vừa với BASIC ($15/tháng, 5 thread), còn crawler chạy 30–50 luồng thì ADVANCE ($90/tháng, 50 thread) hợp hơn. Mỗi thread giải không giới hạn số CAPTCHA trong tháng.
Selenium báo lỗi xác thực proxy dù thông tin đăng nhập đúng?
Chrome không nhận username/password SOCKS5 qua --proxy-server. Dùng Selenium Wire như ví dụ ở trên, hoặc nhờ nhà cung cấp whitelist IP máy chủ.
Đọc tiếp
- Các cách xác thực proxy với CaptchaAI
- Đa dạng nguồn yêu cầu trong môi trường QA
- Chất lượng proxy ảnh hưởng thế nào tới tỷ lệ giải thành công
Lấy API key tại captchaai.com và chạy ví dụ Python trong bài với proxy của bạn.