Xoay vòng proxy Puppeteer: Sticky session, retry và xử lý lỗi
Một job Puppeteer có thể cần giữ cùng IP thoát khi đi qua nhiều trang. Job khác có thể dùng proxy xoay vòng và chấp nhận lối ra mới cho mỗi request đi qua proxy. Đó là các quy trình khác nhau, và không cái nào giống với việc retry một task thất bại.
Hướng dẫn này cho thấy cách gán phiên cho các job scraping, xác minh hành vi IP thoát, phân loại lỗi phổ biến và thêm retry có giới hạn. Ví dụ dùng cấu hình proxy HTTP của KindProxy. Mã dành cho các trang công khai mà bạn được phép truy cập; proxy không ghi đè quy tắc truy cập của site.
1. Xoay vòng proxy, sticky session và retry
Ba cơ chế liên quan:
| Cơ chế | Ai kiểm soát | Việc nó làm |
|---|---|---|
| Proxy xoay vòng | Dịch vụ proxy | Đổi IP thoát theo quy tắc xoay vòng của nhà cung cấp. Với chế độ xoay vòng KindProxy, lối ra thường đổi theo mỗi request đi qua proxy. |
| Sticky session | Dịch vụ proxy, chọn qua tham số phiên | Giữ lối ra ổn định trong thời gian sống của phiên khi có tài nguyên phù hợp. |
| Retry phía client | Chương trình Puppeteer của bạn | Chạy lại task sau lỗi mà mã của bạn coi là tạm thời. Retry bản thân không xoay vòng proxy. |
Từ “request” quan trọng trong trình duyệt. Một lần điều hướng trang có thể kích hoạt request cho tài liệu, script, stylesheet, ảnh và tài nguyên khác. Nếu quy trình cần một lối ra nhất quán qua các bước, hãy dùng sticky session thay vì giả định một lần điều hướng là một request proxy duy nhất.
Session ID mới tạo phiên độc lập, nhưng không đảm bảo IP khác với phiên khác. Nhà cung cấp có thể gán cùng lối ra sẵn có cho cả hai.
2. Lập kế hoạch quan hệ task–phiên
Chọn phiên dựa trên công việc trình duyệt phải hoàn thành:
Kiểm tra một trang: phiên xoay vòng có thể đủ nếu task không phụ thuộc tính liên tục của IP.
Quy trình nhiều bước: dùng một sticky session cho task logic, ví dụ mở trang sản phẩm rồi kiểm tra các biến thể.
Job song song độc lập: cho mỗi job một định danh phiên riêng để các job không vô tình dùng chung phiên.
Giữ bản ghi ánh xạ mỗi task ID tới session ID của nó. Khi phiên hết hạn, request tiếp theo có thể tiếp tục với lối ra mới nếu có tài nguyên khớp. Nếu bộ lọc quốc gia và ASN đã chọn không có tài nguyên khớp, request có thể thất bại.
3. Cấu hình Puppeteer với proxy HTTP
Phần này là nhắc cấu hình ngắn. Để xem hướng dẫn kết nối đầy đủ, xem Cài proxy dân cư trong Puppeteer .
Cài Puppeteer và cung cấp endpoint proxy cùng thông tin đăng nhập qua biến môi trường:
npm install puppeteer
export KP_PROXY_HOST="gw.kindproxy.com"
export KP_PROXY_PORT="12000"
export KP_PROXY_USER_BASE="YOUR_DASHBOARD_USERNAME__cr.us"
export KP_PROXY_USER="$KP_PROXY_USER_BASE"
export KP_PROXY_PASSWORD="YOUR_PASSWORD"
Tên người dùng ở trên là placeholder. Dùng giá trị nhắm quốc gia và ASN được tạo cho tài khoản của bạn trên dashboard. Không commit thông tin đăng nhập vào Git.
Puppeteer khởi chạy Chromium với endpoint proxy, rồi cung cấp xác thực HTTP cho mỗi trang:
import puppeteer from "puppeteer";
const browser = await puppeteer.launch({
args: [
`--proxy-server=http://${process.env.KP_PROXY_HOST}:${process.env.KP_PROXY_PORT}`,
],
});
const page = await browser.newPage();
await page.authenticate({
username: process.env.KP_PROXY_USER,
password: process.env.KP_PROXY_PASSWORD,
});
try {
const response = await page.goto("https://api.ipify.org?format=json", {
waitUntil: "domcontentloaded",
timeout: 30_000,
});
console.log("HTTP status:", response?.status());
console.log("IP response:", await page.$eval("body", el => el.innerText));
} finally {
await browser.close();
}
Kiểm tra IP thoát trước khi đưa trình duyệt tới trang đích. Kiểm tra IP thành công xác nhận tuyến proxy; nó không đảm bảo site đích sẽ trả về trang mong đợi.
4. Quản lý sticky session ID và thời gian sống
KindProxy thêm tham số phiên vào tên người dùng do dashboard tạo. Định dạng sau là hợp lệ:
YOUR_DASHBOARD_USERNAME__cr.us;sessid.product-check-42;sessttl.10
sessttl tính bằng phút và hỗ trợ giá trị từ 1 đến 1440. Trong ví dụ này, sessttl.10 yêu cầu phiên 10 phút. Tên người dùng phải giữ quốc gia và mọi trường nhắm mục tiêu khác được tạo cho tài khoản; hãy nối tham số phiên vào tên người dùng đó thay vì thay thế cài đặt nhắm mục tiêu.
Trong mã, xây dựng tên người dùng phiên cho mỗi task logic:
function usernameForTask(taskId) {
const baseUsername = process.env.KP_PROXY_USER_BASE;
if (!baseUsername) throw new Error("KP_PROXY_USER_BASE is required");
// Keep taskId to a simple, URL-safe set of characters.*
const sessionId = `job-${taskId}`;
return `${baseUsername};sessid.${sessionId};sessttl.10`;
}
// Use this username when authenticating the page for this task.*
await page.authenticate({
username: usernameForTask("product-check-42"),
password: process.env.KP_PROXY_PASSWORD,
});
Ở đây, KP_PROXY_USER_BASE nên chứa tên người dùng do dashboard tạo và các giá trị nhắm mục tiêu, nhưng không có trường sessid hoặc sessttl. Xác nhận thành phần này với tên người dùng hiển thị trên dashboard trước khi chạy.
Khi sticky session đạt giới hạn thời gian, hoặc tài nguyên hiện tại ngoại tuyến, request tiếp tục bằng lối ra mới nếu có tài nguyên phù hợp. Nếu không có tài nguyên khớp bộ lọc quốc gia và ASN đã chọn, request thất bại. Đừng mô tả sticky session như lời hứa tuyệt đối rằng IP không bao giờ đổi.
5. Thêm logic retry có giới hạn
Quyết định retry nên theo danh mục lỗi. Lặp lại mọi thất bại với phiên mới có thể lãng phí traffic và làm chẩn đoán khó hơn.
| Thất bại | Ví dụ | Hành động khuyến nghị |
|---|---|---|
| Xác thực/cấu hình proxy | Proxy trả 407 hoặc Chromium báo lỗi xác thực | Dừng và kiểm tra endpoint, tên người dùng, mật khẩu và phương thức xác thực. |
| Lỗi kết nối tạm thời | Connection reset hoặc lỗi mạng thoáng qua | Retry số lần giới hạn kèm độ trễ; điều tra tuyến proxy nếu vẫn tiếp diễn. |
| Timeout điều hướng | TimeoutError của Puppeteer | Kiểm tra kết nối, phản hồi đích và điều kiện chờ đã chọn có phù hợp không. |
| Giới hạn tốc độ đích | HTTP 429 | Giảm tần suất request và tôn trọng Retry-After khi có. Không tự động xoay vòng để né giới hạn. |
| Lỗi máy chủ đích | HTTP 5xx | Retry thận trọng với giới hạn và backoff. |
| Từ chối xác thực hoặc truy cập đích | HTTP 401 hoặc 403 | Kiểm tra ủy quyền và quy tắc của site. Đừng coi đây là lỗi kết nối proxy hay xoay vòng để vượt qua. |
| Lỗi trích xuất | Selector hoặc giá mong đợi bị thiếu | Kiểm tra trang đã render và parser. Trường thiếu không phải bằng chứng lỗi proxy. |
Một helper retry nhỏ có thể làm rõ giới hạn:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
async function withRetries(task, { maxAttempts = 3, baseDelayMs = 1_000 } = {}) {
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
try {
return await task(attempt);
} catch (error) {
if (attempt === maxAttempts || !isRetryable(error)) throw error;
const exponentialDelay = baseDelayMs * 2 ** (attempt - 1);
const jitter = Math.floor(Math.random() * 250);
console.warn(`Attempt ${attempt} failed; retrying in ${exponentialDelay + jitter} ms`);
await sleep(exponentialDelay + jitter);
}
}
}
function isRetryable(error) {
// Keep this list narrow and adapt it to the errors your job actually observes.*
const message = String(error?.message ?? error);
return error?.name === "TimeoutError" || /ECONNRESET|ETIMEDOUT|ERR_PROXY_CONNECTION_FAILED/.test(message);
}
Helper này chỉ xử lý lỗi được throw. Phản hồi HTTP như 429, 403 hoặc 503 không nhất thiết bị page.goto() throw. Kiểm tra status phản hồi trả về riêng và áp dụng chính sách ở trên. Với 429, dùng độ trễ retry mà đích nêu khi có và giữ tốc độ request trong quy tắc của site.
Việc retry có nên tái sử dụng cùng phiên phụ thuộc vào lỗi đã chẩn đoán. Giữ phiên cho task nhiều bước khi cần tính liên tục. Nếu tài nguyên của phiên không khả dụng, gateway có thể tiếp tục qua lối ra mới; nếu quốc gia/ASN được yêu cầu không còn tài nguyên, hãy dừng và báo lỗi đó thay vì retry vô hạn.
6. Phân loại lỗi và ghi log đúng chi tiết
Chẩn đoán hữu ích tách trình duyệt, proxy, đích và parser:
Khởi động trình duyệt: Chromium đã launch chưa?
Kết nối/xác thực proxy: Có kết nối được gateway và xác thực không? 407 phía proxy thuộc đây.
Phản hồi đích: Target trả HTTP status nào? 401, 403 hoặc 429 phía target khác với 407 phía proxy.
Điều hướng: Trang đã tải trước timeout chưa?
Trích xuất: Dữ liệu mong đợi đã xuất hiện trên trang đã render chưa?
Ghi đủ để tái hiện lỗi mà không lộ thông tin đăng nhập:
{
"task_id": "product-check-42",
"session_id": "job-product-check-42",
"attempt": 2,
"exit_ip": "RECORD_OBSERVED_IP",
"http_status": 200,
"duration_ms": 1840,
"error_category": null,
"outcome": "success"
}
Các giá trị trên chỉ minh họa định dạng log; chúng không phải kết quả kiểm thử KindProxy. Không bao giờ ghi mật khẩu proxy hoặc tên người dùng đầy đủ nếu nó chứa định danh tài khoản.
7. Kiểm thử và xác minh xoay vòng proxy
Chạy kiểm tra có kiểm soát trên endpoint kiểm tra IP trước khi thử trang đích.
Xác minh sticky session
Gửi nhiều lần kiểm tra bằng cùng tên người dùng phiên và ghi lại timestamp cùng IP thoát quan sát được. Trong điều kiện bình thường, phiên nên giữ lối ra trong thời gian sống đã cấu hình. Nếu tài nguyên hết hạn hoặc ngoại tuyến, có thể dùng lối ra mới; nếu không có tài nguyên khớp quốc gia và ASN đã cấu hình, request có thể thất bại.
Quan sát chế độ xoay vòng
Thực hiện kiểm tra lặp lại không dùng sticky session và so sánh lối ra quan sát được với hành vi xoay vòng của nhà cung cấp. Trong ví dụ này, mở trình duyệt mới cho mỗi lần kiểm tra để giảm tái sử dụng kết nối proxy. Vì xoay vòng xảy ra theo mỗi request đi qua proxy, một trang trình duyệt có thể phát nhiều subrequest. Đừng giả định mỗi page.goto() tương ứng đúng một request proxy.
Kết quả kiểm thử quan sát được (1 tháng 11, 2026)
Các kết quả sau được ghi trong các bài kiểm thử có kiểm soát với các script Puppeteer đi kèm. Địa chỉ IP được che có chủ đích trong minh họa đã xuất bản. Các kiểm tra xoay vòng và sticky session dùng kết nối proxy thật; lỗi retry được mô phỏng cục bộ.

Hình: Tóm tắt đã làm sạch các quan sát IP thoát thực tế và các minh họa retry có kiểm soát. Địa chỉ IP được che; lỗi retry là mô phỏng, không phải sự cố proxy thật.
| Kịch bản | Kết quả quan sát | Loại kiểm thử |
|---|---|---|
| Chế độ xoay vòng | Năm lần kiểm tra HTTP 200, năm IP thoát quan sát khác nhau, mỗi lần mở trình duyệt mới | Quan sát proxy thật |
| Sticky session | Năm lần kiểm tra HTTP 200 dùng cùng IP thoát trong khoảng bảy giây với sessid.product-check-42 và TTL 10 phút đã cấu hình | Quan sát proxy thật |
| Xử lý timeout | Hai timeout điều hướng mô phỏng; backoff mũ 1.093 ms và 2.154 ms; lần thử thứ ba trả HTTP 200 qua proxy | Lỗi mô phỏng, request cuối thật |
| Xử lý HTTP 503 | Hai phản hồi 503 mô phỏng; backoff 1.119 ms và 2.112 ms; lần thử thứ ba trả HTTP 200 qua proxy | Lỗi mô phỏng, request cuối thật |
| Xử lý HTTP 429 | Một phản hồi 429 mô phỏng; parse Retry-After: 5 thành 5.000 ms và dừng mà không đổi phiên | Chỉ mô phỏng |
Bài kiểm tra chế độ xoay vòng đầu tiên tái sử dụng một kết nối trình duyệt và quan sát một lối ra qua năm lần kiểm tra. Lặp lại với trình duyệt mới mỗi lần cho năm lối ra khác nhau. Điều này gợi ý tái sử dụng kết nối có thể quan trọng với cấu hình này, nhưng không thiết lập quy tắc xoay vòng theo kết nối phổ quát hay đảm bảo IP khác nhau.
Tương tự, bài kiểm tra sticky session xác nhận tính nhất quán trong cửa sổ quan sát ngắn, không phải suốt toàn bộ TTL đã cấu hình. Các session ID khác nhau cũng có thể được gán cùng lối ra.
Tự chạy các ví dụ
Kho đi kèm dự kiến xuất bản tại kindproxy-puppeteer-proxy-rotation
. Kho chứa rotating-session.js, sticky-session.js và retry-handler.js, kèm hướng dẫn cài đặt và cấu hình môi trường trong README. Hãy xuất bản kho trước khi công khai bài viết này.
npm run rotate
npm run sticky
RETRY_TEST_MODE=timeout npm run retry
RETRY_TEST_MODE=http503 npm run retry
RETRY_TEST_MODE=http429 npm run retry
8. Sai lầm phổ biến và thực hành tốt
Coi retry phía client như thể nó tự động đổi IP proxy.
Giả định session ID mới đảm bảo IP thoát duy nhất.
Giả định mỗi lần điều hướng trình duyệt bằng đúng một request đi qua proxy.
Xoay vòng phiên ngay sau 429 hoặc 403 thay vì kiểm tra phản hồi và quy tắc truy cập của đích.
Retry lỗi xác thực hoặc lỗi parser như thể chúng là sự cố mạng tạm thời.
Chạy retry không giới hạn hoặc đồng thời cao mà không nhịp request.
Đưa thông tin đăng nhập hoặc tên người dùng tài khoản đầy đủ vào source control hoặc log.
Không phân biệt lỗi phía target với lỗi kết nối phía proxy.
Kết luận
Puppeteer không quyết định khi nào proxy dân cư xoay vòng. Dịch vụ proxy quản lý lối ra xoay vòng, tham số sticky session yêu cầu tính liên tục, và mã của bạn quyết định task thất bại có nên được retry. Hãy tách các trách nhiệm đó, phân loại lỗi trước khi retry, và xác minh hành vi IP quan sát được theo quy tắc thực tế của nhà cung cấp.
Nếu bạn vẫn cần các bước kết nối trình duyệt cơ bản, xem Cài proxy dân cư trong Puppeteer . Để có ví dụ chạy được, xem dự án GitHub đi kèm (xuất bản kho trước khi phát hành bài viết này). Khi sẵn sàng chọn gói, khám phá KindProxy Residential Proxies .
