Câu trả lời ngắn: luồng nhiều bước trên cùng một trạng thái đăng nhập thì dùng phiên cố định; luồng chỉ đọc từng trang độc lập thì dùng luân phiên. Chọn sai không hỏng request ngay — nó âm thầm đẩy tỷ lệ CAPTCHA lên và khiến token bị từ chối ở bước cuối.
Thứ tự bên dưới đúng như lúc ra quyết định:
- chọn chế độ theo loại luồng;
- hiểu vì sao token gắn với IP;
- ráp code Python, Node.js;
- chỉnh TTL, xử lý sự cố.
Ma trận quyết định: chọn chế độ trong 30 giây
| Luồng xử lý | Nên dùng | Lý do |
|---|---|---|
| Đăng nhập / đăng ký | Cố định | Token gắn với IP đã giải |
| Kiểm thử checkout | Cố định | Cookie cần cùng IP |
| Gửi biểu mẫu có CAPTCHA | Cố định | Ràng buộc token – IP |
| Thu thập danh mục sản phẩm | Luân phiên | Phân tán tải request |
| Đọc kết quả tìm kiếm | Luân phiên | Tránh giới hạn tần suất |
Dấu hiệu chọn sai:
- token giải xong vẫn bị từ chối ở bước submit;
- cookie đăng nhập mất giữa hai bước;
- job chỉ đọc trang công khai mà tỷ lệ CAPTCHA vẫn leo thang.
Pipeline thật thường cần cả hai — xem chiến lược lai.
Hai chế độ khác nhau ở chỗ nào
Phiên cố định giữ một IP trong cửa sổ TTL bạn đặt (thường 1–30 phút); luân phiên cấp IP mới mỗi request.
STICKY SESSION:
Request 1 ──▶ IP: 192.168.1.50 ──▶ site.com/login
Request 2 ──▶ IP: 192.168.1.50 ──▶ site.com/dashboard
Request 3 ──▶ IP: 192.168.1.50 ──▶ site.com/account
(Same IP for all requests in the session window)
ROTATING SESSION:
Request 1 ──▶ IP: 192.168.1.50 ──▶ site.com/page1
Request 2 ──▶ IP: 10.0.0.77 ──▶ site.com/page2
Request 3 ──▶ IP: 172.16.5.22 ──▶ site.com/page3
(Different IP per request)
Khác biệt nhỏ, nhưng quyết định token còn hợp lệ hay không:
| Yếu tố | Phiên cố định | Phiên luân phiên |
|---|---|---|
| IP mỗi request | Giữ nguyên 1–30 phút | Mới mỗi lần |
| Nhất quán phiên | Cao | Không có |
| Tỷ lệ CAPTCHA (nhiều bước) | Thấp (5–10%) | Cao (30–50%) |
| Tỷ lệ CAPTCHA (trang đơn) | Trung bình (10–20%) | Thấp (5–10%) |
| Độ tin cậy token | Cao hơn — cùng IP | Nguy cơ lệch IP |
| Tốc độ | Nhanh (tái dùng kết nối) | Chậm hơn |
| Rủi ro bị chặn | Cao hơn nếu IP bị gắn cờ | Thấp |
| Phù hợp với | Đăng nhập, checkout | Thu thập dữ liệu |
Khi nào phiên cố định thắng
Luồng nhiều bước: token gắn với IP
Token CAPTCHA gắn với ngữ cảnh phiên. IP đổi giữa lúc giải và lúc submit thì server có thể từ chối token — mất cả thời gian lẫn thread.
import requests
import time
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
# Sticky session: same IP for the entire login flow
STICKY_PROXY = {
"http": "http://user-session-abc123:[email protected]:5000",
"https": "http://user-session-abc123:[email protected]:5000",
}
session = requests.Session()
session.proxies = STICKY_PROXY
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
})
def login_with_captcha(url, sitekey, username, password):
"""Full login flow — MUST use same IP throughout."""
# Step 1: Load login page (IP: 192.168.1.50)
session.get(url)
# Step 2: Solve CAPTCHA (solved against IP: 192.168.1.50)
token = solve_recaptcha(sitekey, url)
# Step 3: Submit login (MUST be IP: 192.168.1.50)
resp = session.post(url, data={
"username": username,
"password": password,
"g-recaptcha-response": token,
})
return resp.status_code == 200
def solve_recaptcha(sitekey, pageurl):
resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
"key": CAPTCHAAI_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1,
})
task_id = resp.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
"key": CAPTCHAAI_KEY, "action": "get",
"id": task_id, "json": 1,
})
data = result.json()
if data["request"] != "CAPCHA_NOT_READY":
return data["request"]
raise TimeoutError("CAPTCHA solve timeout")
Cả ba bước — mở trang, giải, submit — phải qua một IP.
Vì sao tính nhất quán IP lại quan trọng
Sticky session:
1. Browser loads page → IP: 5.5.5.5
2. CAPTCHA solved → Token bound to session
3. Form submitted → IP: 5.5.5.5 ✅ Token accepted
Rotating session:
1. Browser loads page → IP: 5.5.5.5
2. CAPTCHA solved → Token bound to session
3. Form submitted → IP: 9.9.9.9 ❌ Token may be rejected
Khi nào phiên luân phiên thắng
Thu thập dữ liệu số lượng lớn
Mỗi trang là request độc lập, không có trạng thái cần giữ; trải qua nhiều IP giúp giảm mật độ truy cập mỗi IP.
Ví dụ quen ở Việt Nam: một team product tại TP.HCM theo dõi giá niêm yết công khai cho chính danh mục hàng của họ trên Shopee, Lazada và Tiki. Vài trăm URL, mỗi URL đọc một lần mỗi giờ, không cần đăng nhập — đúng việc mà luân phiên xử lý gọn nhất. Ghi log tối thiểu, không giữ dữ liệu cá nhân thừa; Nghị định 13/2023/NĐ-CP là lý do thực tế.
import concurrent.futures
ROTATING_PROXY = {
"http": "http://user:[email protected]:5000",
"https": "http://user:[email protected]:5000",
}
def scrape_page(url):
"""Each request gets a fresh IP automatically."""
resp = requests.get(
url,
proxies=ROTATING_PROXY,
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"},
timeout=30,
)
if resp.status_code == 200:
return url, resp.text
return url, None
urls = [f"https://example.com/products?page={i}" for i in range(1, 501)]
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as pool:
results = list(pool.map(scrape_page, urls))
success = sum(1 for _, html in results if html)
print(f"Scraped: {success}/{len(urls)}")
Đọc kết quả tìm kiếm cũng vậy: công cụ tìm kiếm đếm truy vấn theo IP, luân phiên tránh trần giới hạn tần suất.
def scrape_search(query, pages=10):
results = []
for page in range(pages):
# Each request → new IP
resp = requests.get(
"https://search-engine.example.com/search",
params={"q": query, "start": page * 10},
proxies=ROTATING_PROXY,
timeout=20,
)
results.append(resp.text)
time.sleep(2)
return results
Chiến lược lai: dùng cả hai trong một pipeline
Để lớp quản lý proxy tự chọn: có đăng nhập thì cấp phiên cố định kèm session ID, còn lại luân phiên.
class HybridProxyManager:
"""Sticky for multi-step flows, rotating for single requests."""
def __init__(self, provider_host, username, password, port=5000):
self.host = provider_host
self.username = username
self.password = password
self.port = port
def get_rotating_proxy(self):
return {
"http": f"http://{self.username}:{self.password}@{self.host}:{self.port}",
"https": f"http://{self.username}:{self.password}@{self.host}:{self.port}",
}
def get_sticky_proxy(self, session_id, duration_min=10):
sticky_user = f"{self.username}-session-{session_id}-ttl-{duration_min}"
return {
"http": f"http://{sticky_user}:{self.password}@{self.host}:{self.port}",
"https": f"http://{sticky_user}:{self.password}@{self.host}:{self.port}",
}
proxy_mgr = HybridProxyManager("proxy.example.com", "user", "pass")
def scrape_with_auto_strategy(url, needs_login=False):
if needs_login:
# Multi-step → sticky
import uuid
session_id = uuid.uuid4().hex[:8]
proxy = proxy_mgr.get_sticky_proxy(session_id)
else:
# Single page → rotating
proxy = proxy_mgr.get_rotating_proxy()
return requests.get(url, proxies=proxy, timeout=30)
Mọi quyết định nằm một chỗ, không rải rẽ nhánh khắp code.
Triển khai bằng Node.js
Cùng logic bằng Node.js: chọn chế độ, giữ IP xuyên suốt luồng đăng nhập, gửi task tới in.php rồi polling res.php.
const axios = require("axios");
const CAPTCHAAI_KEY = "YOUR_API_KEY";
const PROXY_HOST = "proxy.example.com";
const PROXY_PORT = 5000;
function getProxy(mode, sessionId = null) {
const user =
mode === "sticky" ? `user-session-${sessionId}` : "user";
return {
proxy: {
host: PROXY_HOST,
port: PROXY_PORT,
auth: { username: user, password: "pass" },
},
};
}
// Rotating: bulk scrape
async function scrapePages(urls) {
const results = [];
for (const url of urls) {
const config = getProxy("rotating");
const resp = await axios.get(url, { ...config, timeout: 30000 });
results.push({ url, data: resp.data });
}
return results;
}
// Sticky: login flow
async function loginFlow(loginUrl, sitekey, credentials) {
const sessionId = Date.now().toString(36);
const config = getProxy("sticky", sessionId);
// Step 1: Load page
await axios.get(loginUrl, config);
// Step 2: Solve CAPTCHA
const submitResp = await axios.post(
"https://ocr.captchaai.com/in.php",
null,
{
params: {
key: CAPTCHAAI_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: loginUrl,
json: 1,
},
}
);
const taskId = submitResp.data.request;
let token;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const res = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: CAPTCHAAI_KEY, action: "get", id: taskId, json: 1 },
});
if (res.data.request !== "CAPCHA_NOT_READY") {
token = res.data.request;
break;
}
}
// Step 3: Submit with same IP
return axios.post(
loginUrl,
{
...credentials,
"g-recaptcha-response": token,
},
config
);
}
Đặt TTL cho phiên cố định
TTL ngắn quá thì phiên hết hạn giữa chừng; dài quá thì request dồn vào một IP.
| Thời lượng | Phù hợp với | Đánh đổi |
|---|---|---|
| 1 phút | Gửi một biểu mẫu | Dễ hết hạn giữa luồng |
| 5 phút | Đăng nhập + vài thao tác | Cân bằng tốt nhất |
| 10 phút | Luồng nhiều trang | Rủi ro chặn mức vừa |
| 30–60 phút | Phiên kéo dài | Rủi ro chặn cao nhất |
Cách chọn:
- Đo thời gian thật của luồng, tính cả lúc giải CAPTCHA.
- Nhân đôi, làm tròn lên mức TTL proxy hỗ trợ.
- Luồng đăng nhập 40 giây thì TTL 5 phút là dư.
Chi phí thật sự nằm ở tỷ lệ CAPTCHA
Proxy tính theo GB hoặc số request, nên hai chế độ gần như cùng giá. Chênh lệch nằm chỗ khác: sai chế độ làm tỷ lệ CAPTCHA tăng, kéo theo nhu cầu thread.
CaptchaAI tính phí theo thread (luồng giải đồng thời), không theo lần giải; số lần giải trong tháng không giới hạn. Giá bằng USD:
- BASIC ($15/tháng, 5 thread) — vài luồng đăng nhập QA;
- STANDARD ($30/tháng, 15 thread) — khoảng 15 worker song song;
- ADVANCE ($90/tháng, 50 thread) — quy mô agency.
CaptchaAI giải reCAPTCHA v2/v3 (kể cả Enterprise), Cloudflare Turnstile và Challenge, GeeTest v3, CAPTCHA ảnh/OCR, grid image, BLS. hCaptcha và FunCaptcha (Arkose Labs) chưa được hỗ trợ; GeeTest v4 sắp ra mắt; CaptchaFox, Friendly Captcha, Lemin ở giai đoạn beta.
Khắc phục sự cố thường gặp
Bốn triệu chứng hay gặp khi đặt sai chế độ:
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Token bị từ chối dù đã giải xong | IP đổi giữa lúc giải và submit | Chuyển luồng sang phiên cố định |
| Tỷ lệ CAPTCHA cao ở trang đơn | Một IP gánh quá nhiều trang | Chuyển sang phiên luân phiên |
| Mất cookie phiên giữa chừng | TTL hết hạn sớm | Tăng TTL hoặc rút ngắn luồng |
| IP cố định bị chặn | Request dồn vào một IP bị gắn cờ | Giảm TTL, giãn thời gian request |
Câu hỏi thường gặp
Nên bắt đầu bằng chế độ nào khi chưa đo được gì?
Phiên luân phiên, trừ khi luồng có đăng nhập. Chuyển sang cố định khi thấy một trong hai dấu hiệu:
- token bị từ chối ở bước submit;
- cookie phiên không sống qua bước thứ hai.
TTL bao nhiêu là hợp lý cho luồng đăng nhập có CAPTCHA?
5 phút, rồi đo luồng thật và chỉnh lại.
CaptchaAI tính tiền theo số CAPTCHA đã giải hay theo thread?
Theo thread, số lần giải không giới hạn. Đổi chế độ proxy chỉ ảnh hưởng số thread bạn cần, không phải sửa code tích hợp: phần gửi task tới in.php và polling res.php giữ nguyên.
Cấu hình phiên cố định với nhà cung cấp proxy như thế nào?
Phần lớn nhúng session ID vào username, dạng user-session-abc123. Định dạng chính xác xem tài liệu từng bên:
Đọc tiếp
Ba bài bổ trợ cho quyết định ở trên:
- Nhóm IP luân phiên khi giải CAPTCHA
- Chất lượng proxy ảnh hưởng tỷ lệ giải thành công
- Giữ phiên trình duyệt xuyên suốt luồng CAPTCHA
Chọn chế độ phiên theo luồng, còn phần giải CAPTCHA để API lo — lấy API key CaptchaAI và chạy thử ví dụ Python ở trên.