Cấu hình proxy Playwright Puppeteer nghe đơn giản, nhưng có một quyết định quan trọng ngay từ đầu: đặt proxy cho cả trình duyệt khi khởi chạy, hay cho từng browser context. Puppeteer hoạt động gần giống Playwright nhưng xử lý thông tin đăng nhập theo cách khác. Chọn đúng cấp độ sẽ quyết định một script có thể chạy song song nhiều quốc gia, hay mọi tài khoản cuối cùng lại dùng chung một IP. Bài này đưa ra cấu hình chạy được cho cả hai công cụ với proxy riêng có xác thực, sau đó nói về SOCKS5, cách kiểm tra và những lỗi bạn dễ gặp nhất.
Cấu hình proxy Playwright Puppeteer: đặt ở launch, context hay page
Trước khi viết code, hãy xác định một script cần bao nhiêu "danh tính".
| Cấp độ | Playwright | Puppeteer | Dùng khi |
|---|---|---|---|
| Khởi chạy trình duyệt | launch({ proxy }) | Tham số --proxy-server khi launch | Một IP cho cả lần chạy |
| Browser context | newContext({ proxy }) | createBrowserContext({ proxyServer }) | Nhiều IP hoặc nhiều tài khoản trong một tiến trình trình duyệt |
| Thông tin đăng nhập theo page | Không cần, đã khai báo trong proxy | page.authenticate() | Chỉ với Puppeteer, cho từng page |
Browser context là một phiên tách biệt, có cookie, storage và cache riêng. Ghép mỗi context với một IP riêng là cách gọn gàng nhất để giữ các danh tính tách bạch, cũng chính là nguyên tắc trong bài hướng dẫn proxy riêng: mỗi IP cho một mục đích.
Trong các ví dụ dưới đây, hãy thay HOST, PORT, USERNAME và PASSWORD bằng thông tin hiển thị cho từng proxy trong console Cloudj.
Proxy Playwright khi khởi chạy (Node.js)
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
proxy: {
server: 'http://HOST:PORT',
username: 'USERNAME',
password: 'PASSWORD',
},
});
const page = await browser.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.textContent('body'));
await browser.close();
})();
Playwright tự xử lý yêu cầu xác thực 407 Proxy Authentication Required, nên bạn không phải làm thêm bước nào cho thông tin đăng nhập. IP in ra phải là địa chỉ của proxy, không phải IP của bạn.
Mỗi context một proxy
Đây là điểm mạnh nhất của Playwright. Mỗi context có proxy, ngôn ngữ và múi giờ riêng, nên một phiên Nhật Bản và một phiên Mỹ có thể chạy chung một trình duyệt mà không chia sẻ gì với nhau:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const jp = await browser.newContext({
proxy: { server: 'http://JP_HOST:PORT', username: 'USERNAME', password: 'PASSWORD' },
locale: 'ja-JP',
timezoneId: 'Asia/Tokyo',
});
const us = await browser.newContext({
proxy: { server: 'http://US_HOST:PORT', username: 'USERNAME', password: 'PASSWORD' },
locale: 'en-US',
timezoneId: 'America/New_York',
});
for (const context of [jp, us]) {
const page = await context.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.textContent('body'));
}
await browser.close();
})();
Hãy đặt locale và timezoneId khớp với vị trí của IP. Một IP Tokyo mà báo múi giờ Việt Nam là điểm bất thường lộ rõ với bất kỳ website nào có kiểm tra. Bài proxy kiểm tra quảng cáo dùng đúng mô hình này để soát các landing page đã bản địa hóa.
Phiên bản Python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context(
proxy={"server": "http://HOST:PORT", "username": "USERNAME", "password": "PASSWORD"},
locale="ja-JP",
timezone_id="Asia/Tokyo",
)
page = context.new_page()
page.goto("https://api.ipify.org?format=json")
print(page.inner_text("body"))
browser.close()
Danh sách đầy đủ các tùy chọn proxy có trong tài liệu network của Playwright.
Proxy Puppeteer có xác thực
Chromium không nhận username và password nằm trong cờ --proxy-server, nên Puppeteer chia việc làm hai phần: địa chỉ server đặt trong tham số launch, còn thông tin đăng nhập truyền qua page.authenticate(). Lưu file dưới tên check.mjs để dùng được await ở cấp cao nhất:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
args: ['--proxy-server=http://HOST:PORT'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'USERNAME', password: 'PASSWORD' });
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
Hãy gọi authenticate() trên mọi page mới trước lần điều hướng đầu tiên. Quên bước này ở tab thứ hai là nguyên nhân phổ biến nhất khiến lỗi 407 đột ngột xuất hiện.
Proxy theo từng context trong Puppeteer
Các phiên bản Puppeteer gần đây cho phép đặt proxy cho từng browser context:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const context = await browser.createBrowserContext({ proxyServer: 'http://HOST:PORT' });
const page = await context.newPage();
await page.authenticate({ username: 'USERNAME', password: 'PASSWORD' });
await page.emulateTimezone('Asia/Tokyo');
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
Code cũ thường dùng createIncognitoBrowserContext(), hàm này đã được đổi tên thành createBrowserContext() từ Puppeteer v22.
SOCKS5 và IP whitelist
Cả hai công cụ đều nhận socks5://HOST:PORT làm địa chỉ proxy. Vướng mắc nằm ở xác thực: Chromium không hỗ trợ username và password cho proxy SOCKS5, nên một proxy SOCKS5 có mật khẩu sẽ không chạy được với Puppeteer. Bạn có hai cách gọn gàng:
- Giữ đơn hàng ở giao thức HTTP và dùng thông tin đăng nhập như hướng dẫn ở trên.
- Dùng SOCKS5 kết hợp IP whitelist. Thêm IP công khai của máy chạy trình duyệt (tối đa 20 địa chỉ), khi đó whitelist sẽ thay cho đăng nhập bằng mật khẩu, và chỉ cần chuỗi
socks5://HOST:PORTlà đủ.
Bạn có thể đổi giao thức của đơn hàng trong console nếu vị trí đó hỗ trợ. Cách dùng whitelist cũng rất hợp với CI runner hoặc VPS có IP cố định.
Xử lý lỗi thường gặp
ERR_PROXY_CONNECTION_FAILED: sai host hoặc port, gõ nhầm scheme, hoặc giao thức trong console không khớp với scheme trong code.- Lỗi
407hoặc hộp đăng nhập hiện liên tục: thiếupage.authenticate()trong Puppeteer, sai thông tin đăng nhập, hoặc mật khẩu vừa được đổi trong console nên mật khẩu cũ không còn dùng được. - Chạy được ở máy local, lỗi trên CI: đơn hàng đang dùng whitelist và IP của runner chưa được thêm vào.
- IP đúng nhưng nội dung vẫn sai ngôn ngữ: đặt
locale,timezoneId(hoặcemulateTimezone) và headerAccept-Languagekhớp với quốc gia.
Tự động hóa trong khuôn khổ quy định
Điều khiển trình duyệt tự động qua proxy rất mạnh khi kiểm thử website của chính bạn, soát các trang bản địa hóa hoặc chạy QA định kỳ. Với website bên thứ ba, hãy đọc điều khoản sử dụng và robots.txt, giữ tốc độ request thấp và ưu tiên API chính thức nếu có. Proxy không ngăn được việc bị chặn hay bị khóa tài khoản; hành xử như một người dùng bình thường mới là điều quan trọng. Nếu bạn quản lý tài khoản mạng xã hội hoặc gian hàng, hãy đọc thêm các bài proxy TikTok tại Mỹ, proxy cho Facebook Ads và proxy cho người bán hàng thương mại điện tử, đồng thời tuân thủ quy định về tự động hóa của từng nền tảng như Shopee, Lazada hay TikTok Shop.
Câu hỏi thường gặp
Có đổi được proxy của một context Playwright đang chạy không?
Không. Proxy được cố định khi tạo context. Hãy đóng context đó và tạo context mới với proxy mới.
Có cần mỗi context một IP riêng không?
Nếu mỗi context đại diện cho một tài khoản hoặc một thị trường riêng thì có. Dùng chung IP giữa các context khiến website thấy chúng có liên hệ với nhau.
Chế độ headless có làm proxy hoạt động khác không?
Không. Trình duyệt headless và có giao diện dùng proxy theo cùng một cách.
Nên chọn giao thức nào cho Puppeteer?
HTTP với username và password, hoặc SOCKS5 với IP whitelist. SOCKS5 có mật khẩu không chạy được trên Chromium.
Chạy với IP của riêng bạn
Cloudj cung cấp proxy IPv4 riêng tại bảy quốc gia, trong đó có proxy Mỹ với tùy chọn Static ISP; danh sách đầy đủ nằm ở trang tổng quan proxy. Hãy đặt đủ số IP cho các context của bạn trong console, nạp tiền bằng chuyển khoản VND nếu cần, và xem giá theo từng IP trên trang bảng giá.