Xác thực proxy trong Selenium và Playwright: tất cả các phương pháp hoạt động (2026)

Đăng ngày 4 tháng 6 2026 · ≈10 phút đọc

Đây là cái bẫy mà mọi lập trình viên tự động hóa trình duyệt đều mắc đúng một lần: bạn thêm --proxy-server=http://user:pass@host:port vào cờ khởi chạy của Chrome, trình duyệt khởi động bình thường, rồi mọi trang đều treo lại ở một popup xác thực mà script của bạn không thể nhìn thấy hay nhấn vào. Chrome âm thầm loại bỏ thông tin đăng nhập khỏi cờ proxy. Không có lỗi, không có dòng log, chỉ là một hộp thoại modal trong một trình duyệt mà chẳng ai để mắt tới.

Hướng dẫn này là bản đồ đầy đủ về các phương pháp xác thực proxy thực sự hoạt động trong Selenium và Playwright năm 2026 — nên dùng cái nào, nên tránh cái nào, và đoạn code chính xác cho từng cách. (Về chiến lược chọn proxy và xoay proxy trong các công cụ này, xem Sử dụng Proxy với Playwright & Puppeteer — bài viết này chỉ tập trung vào việc đưa thông tin đăng nhập đi qua.)

Vì sao user:pass@host thất bại trong trình duyệt

Các HTTP client dòng lệnh phân tích thông tin đăng nhập từ URL proxy và tự động trả lời phản hồi 407 Proxy Authentication Required của proxy bằng một header Proxy-Authorization. Chromium thì không: --proxy-server chỉ chấp nhận scheme://host:port, và khi 407 quay về, nó hiển thị một hộp thoại tương tác. Ở chế độ headless thậm chí còn chẳng có hộp thoại — yêu cầu chỉ đơn giản là thất bại.

Vậy nên câu hỏi không bao giờ là "làm sao đặt mật khẩu vào URL" — mà là "tầng nào sẽ trả lời 407 thay tôi". Có bốn câu trả lời hoạt động được, tốt nhất xếp trước.

Phương pháp 1: Xác thực gốc của Playwright (Dùng cách này nếu có thể)

Playwright đã giải quyết vấn đề một cách đàng hoàng: nó tự trả lời các thử thách xác thực proxy qua CDP. Thông tin đăng nhập đặt trong tùy chọn launch hoặc context:

# Python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(
        proxy={
            "server": "http://us.jibaoproxy.com:913",
            "username": "USERNAME-session-job1",
            "password": "PASSWORD",
        }
    )
// Node.js - và theo từng context, đây là nơi nó trở nên mạnh mẽ:
const browser = await chromium.launch({
  proxy: { server: 'per-context' }   // khai báo ý định khi launch
});
const ctx = await browser.newContext({
  proxy: {
    server: 'http://us.jibaoproxy.com:913',
    username: 'USERNAME-session-job2',
    password: 'PASSWORD',
  }
});

Proxy theo từng context nghĩa là một tiến trình trình duyệt có thể chạy N context trên N phiên sticky khác nhau — nền tảng của mọi thiết lập scraping nghiêm túc bằng Playwright. Nếu bạn đang dùng Playwright, vậy là xong; nhảy thẳng tới phần xác minh. Mọi thứ bên dưới tồn tại là vì Selenium không có cơ chế tương đương.

Phương pháp 2: selenium-wire (Selenium, Python)

selenium-wire bọc Selenium bằng một proxy MITM cục bộ xử lý xác thực với upstream:

from seleniumwire import webdriver   # pip install selenium-wire

options = {
    "proxy": {
        "http":  "http://USERNAME:[email protected]:913",
        "https": "https://USERNAME:[email protected]:913",
    }
}
driver = webdriver.Chrome(seleniumwire_options=options)
driver.get("https://example.com")

Những lưu ý bạn nên biết trước khi đưa lên production:

Phương pháp 3: Tiện ích Chrome chèn thông tin đăng nhập (Selenium, mọi ngôn ngữ)

Cách tiếp cận kinh điển: một tiện ích nhỏ được sinh ra để thiết lập proxy và trả lời xác thực qua chrome.webRequest.onAuthRequired. Không MITM, không tiến trình phụ, hoạt động trong Selenium thuần:

import zipfile, json

HOST, PORT = "us.jibaoproxy.com", 913
USER, PASS = "USERNAME-session-sel1", "PASSWORD"

manifest = {
    "version": "1.0.0", "manifest_version": 2, "name": "Proxy Auth",
    "permissions": ["proxy", "webRequest", "webRequestBlocking", ""],
    "background": {"scripts": ["background.js"]},
}
background = f"""
chrome.proxy.settings.set({{value: {{mode: "fixed_servers", rules: {{
  singleProxy: {{scheme: "http", host: "{HOST}", port: {PORT}}}
}}}}, scope: "regular"}}, () => {{}});
chrome.webRequest.onAuthRequired.addListener(
  () => ({{authCredentials: {{username: "{USER}", password: "{PASS}"}}}}),
  {{urls: [""]}}, ["blocking"]
);
"""

with zipfile.ZipFile("proxy_auth.zip", "w") as zp:
    zp.writestr("manifest.json", json.dumps(manifest))
    zp.writestr("background.js", background)

from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_extension("proxy_auth.zip")
driver = webdriver.Chrome(options=opts)

Hai điều dễ vấp: tiện ích không tải được ở chế độ headless cũ — hãy dùng --headless=new (Chrome 109+) hoặc chạy có giao diện; và các script nền Manifest V2 vẫn hoạt động với các tiện ích tự động hóa được nạp ngoài (sideload) nhưng hãy để ý mốc thời gian Chrome ngừng hỗ trợ MV2 — phiên bản MV3 tương đương cần một service worker và cùng listener onAuthRequired.

Phương pháp 4: Bộ chuyển tiếp cục bộ (Phương án dự phòng vạn năng)

Chạy một proxy cục bộ nhỏ giữ thông tin đăng nhập; trỏ trình duyệt tới localhost hoàn toàn không cần xác thực. Hoạt động với mọi trình duyệt, mọi driver, mọi ngôn ngữ, headless hay không:

# Dùng pproxy (pip install pproxy) - terminal 1:
pproxy -l http://127.0.0.1:8899 \
       -r "http://USERNAME:[email protected]:913"

# Script của bạn - terminal 2: Selenium thuần, không cần xác thực
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://127.0.0.1:8899")
driver = webdriver.Chrome(options=opts)

Đây cũng là câu trả lời gọn gàng nhất cho Firefox/geckodriver (nơi mẹo tiện ích không áp dụng được) và cho những môi trường đặc thù như kiểm thử ứng dụng Electron. Cái giá phải trả: thêm một bộ phận cần giám sát, và việc định tuyến theo từng context đòi hỏi một cổng forwarder cho mỗi phiên.

So sánh các phương pháp

Phương phápHoạt động trongHeadlessPhiên theo từng contextKết luận cho production
Playwright gốcPlaywrightLựa chọn mặc định
selenium-wireSelenium (Py)Theo từng driverỔn ở quy mô vừa phải
Tiện ích xác thựcSelenium (mọi ngôn ngữ)Chỉ --headless=newTheo từng driverVững chắc, để ý MV3
Bộ chuyển tiếp cục bộMọi thứMột cổng cho mỗi phiênDự phòng vạn năng

Xác minh rằng nó thực sự đã hoạt động

Âm thầm rơi về IP thật của bạn chính là kiểu thất bại khiến tài khoản bị cấm. Hãy khẳng định IP đầu ra ngay đầu mỗi lần chạy:

ip = driver.execute_script(
    "return fetch('https://api.ipify.org').then(r => r.text())"
) if hasattr(driver, 'execute_script') else page.evaluate(
    "fetch('https://api.ipify.org').then(r => r.text())"
)
assert ip != MY_REAL_IP, "Proxy not applied - aborting before we leak"
Công cụ miễn phí · không cần đăng ký

Kiểm tra bằng mắt trình duyệt tự động hóa của bạn

Lái trình duyệt Selenium/Playwright của bạn tới trang What Is My IP của chúng tôi: nó hiển thị IP đầu ra, vị trí địa lý, loại ASN và cờ proxy/VPN trong cùng một màn hình — xác nhận tức thì rằng tầng xác thực đã hoạt động và IP là dân cư (residential).

Mở công cụ kiểm tra IP →

Thấy ASN datacenter trên một proxy "residential"? Hãy chuyển sang một pool thực sự là residential — 500MB lưu lượng miễn phí →

Tham khảo nhanh khắc phục sự cố

Tóm tắt

Trình duyệt không đọc URL proxy dạng user:pass@ — phải có thứ gì đó trả lời 407 thay chúng. Trong Playwright, thứ đó được tích hợp sẵn; trong Selenium, đó là selenium-wire, một tiện ích xác thực được sinh ra, hoặc một bộ chuyển tiếp cục bộ. Hãy chọn theo ngăn xếp công nghệ của bạn từ bảng so sánh, rồi khẳng định IP đầu ra ở mỗi lần chạy để một lỗi xác thực âm thầm không bao giờ có thể làm lộ địa chỉ thật của bạn giữa chừng quá trình thu thập dữ liệu.

Thử cả bốn phương pháp ngay tối nay

500MB lưu lượng residential miễn phí — một gateway, sticky hoặc xoay, HTTP và SOCKS5.

Bắt đầu dùng thử miễn phí

Cho mọi sản phẩm IP · một kho node khổng lồ luôn sẵn sàng bất cứ lúc nào

Đăng ký ngay và nhận tới 100% hoàn tiền cho lần nạp của bạn

Người dùng mới nhận 500MB khi đăng ký, cùng tiền thưởng cho lần nạp đầu tiên. Ưu đãi có thời hạn.