Фреймворки браузерной автоматизации принимают прокси как опцию запуска, но прокси с аутентификацией требуют особой обработки, потому что Chromium не принимает user:pass@ в URL прокси: Playwright принимает учетные данные как отдельные поля (и может установить разный прокси для каждого контекста браузера), Puppeteer требует page.authenticate(), а Selenium требует расширения или локального форвардера. Это руководство дает рабочий код для каждого случая, а затем рассматривает то, что важнее синтаксиса: headless-браузер за резидентным IP все еще идентифицируем, если остальная его история не соответствует.
Ключевой факт: Chromium игнорирует учетные данные прокси в URL
Инструменты командной строки и библиотеки Python спокойно принимают http://user:pass@host:port. Флаг Chromium --proxy-server принимает только host:port (с опциональной схемой); учетные данные в нем молча отбрасываются. Когда прокси отвечает 407, браузер показывает диалог аутентификации — который не отслеживает ни один скрипт автоматизации. Поэтому каждый фреймворк имеет свой способ предоставления имени пользователя и пароля, и именно здесь возникает большинство проблем типа «прокси работает в curl, но не в моем браузерном скрипте». Firefox более снисходителен в некоторых случаях, но описанные ниже механизмы работают во всех движках.
Playwright
Playwright имеет первоклассную поддержку прокси с отдельными полями учетных данных и — что делает его лучшим выбором для работы с несколькими идентичностями — прокси можно установить для каждого контекста браузера, так что один процесс браузера может размещать несколько изолированных сессий, каждая из которых выходит с разного IP.
Python
from playwright.sync_api import sync_playwright
PROXY = {"server": "http://proxy.example.com:8080",
"username": "user", "password": "pass"}
with sync_playwright() as p:
# вариант A: один прокси для всего браузера
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
page.goto("https://api.ipify.org?format=json")
print(page.text_content("body"))
browser.close()
# вариант B: разный прокси для каждого контекста (один браузер, много идентичностей)
browser = p.chromium.launch(proxy={"server": "per-context"}) # заглушка; реальные прокси берутся из контекстов
ctx_de = browser.new_context(proxy={"server": "http://gate.provider.com:7777",
"username": "customer-country-de-session-a1", "password": "pass"},
locale="de-DE", timezone_id="Europe/Berlin")
ctx_us = browser.new_context(proxy={"server": "http://gate.provider.com:7777",
"username": "customer-country-us-session-b2", "password": "pass"},
locale="en-US", timezone_id="America/New_York")
for ctx in (ctx_de, ctx_us):
pg = ctx.new_page(); pg.goto("https://api.ipify.org?format=json"); print(pg.text_content("body"))
browser.close()
Две детали. Прокси для каждого контекста в Chromium требует, чтобы браузер был запущен с опцией прокси (любое значение — документированная заглушка {"server": "per-context"}); без этого контексты не могут переопределить прокси. И обратите внимание на locale и timezone_id, установленные вместе с прокси: немецкий IP с американским часовым поясом — это противоречие, которое учитывают антибот-системы, как объясняется в как работают антибот-системы. Прокси SOCKS5 поддерживаются ("server": "socks5://host:1080"), но Chromium не отправляет учетные данные SOCKS-прокси, поэтому используйте аутентификацию по белому списку IP с SOCKS.
Node.js
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ proxy: { server: 'per-context' } });
const context = await browser.newContext({
proxy: { server: 'http://gate.provider.com:7777',
username: 'customer-country-de-session-a1', password: 'pass' },
locale: 'de-DE', timezoneId: 'Europe/Berlin',
});
const page = await context.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.textContent('body'));
await browser.close();
})();
Поле bypass в Playwright ("bypass": "localhost,*.internal") исключает хосты из прокси, а launch_persistent_context() принимает ту же опцию proxy, если нужен постоянный профиль.
Puppeteer
Puppeteer передает прокси как флаг Chromium и аутентифицируется на каждой странице:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://proxy.example.com:8080'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'user', password: 'pass' }); // отвечает на 407 для этой страницы
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();
page.authenticate() должен вызываться до навигации и на каждой странице (вкладке), которая будет использовать прокси. Прокси действует на весь браузер — в Puppeteer нет настройки прокси для каждого контекста, как в Playwright. Для нескольких идентичностей есть три варианта: запустить несколько процессов браузера (по одному прокси на каждый; тяжело, но просто), использовать --proxy-server, указывающий на локальный переключающий прокси, который маршрутизирует по какому-то маркеру, или перейти на Playwright. Также полезно: --proxy-bypass-list=<-loopback>, если нужно проксировать и localhost, и puppeteer-extra с его плагином прокси, который обрабатывает аутентификацию и может ротировать прокси на каждой странице.
Selenium
Selenium устанавливает прокси через опции браузера. Прокси без аутентификации (по белому списку IP) задаются одной строкой; с аутентификацией — это неудобный случай.
Chrome, аутентификация по белому списку
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://proxy.example.com:8080")
driver = webdriver.Chrome(options=opts)
driver.get("https://api.ipify.org?format=json")
print(driver.find_element("tag name", "body").text)
driver.quit()
Chrome, имя пользователя/пароль
В протоколе WebDriver Selenium нет хука для ответа на 407 от прокси. Стандартные обходные пути:
- Selenium Wire (Python): расширение Selenium, которое направляет трафик через свой локальный прокси и поддерживает аутентифицированные вышестоящие прокси:
Обратите внимание, что Selenium Wire больше не поддерживается активно; он все еще работает с текущим Chrome для многих, но рассматривайте его как временное решение.from seleniumwire import webdriver # pip install selenium-wire opts = {"proxy": {"http": "http://user:pass@proxy.example.com:8080", "https": "http://user:pass@proxy.example.com:8080"}} driver = webdriver.Chrome(seleniumwire_options=opts) - Сгенерированное расширение Chrome, которое устанавливает прокси через
chrome.proxyи отвечает наonAuthRequiredучетными данными — хорошо известный паттерн; расширение состоит из манифеста и нескольких строк фонового скрипта, загружается с помощьюopts.add_extension(...). Не работает в headless-режиме со старыми версиями Chrome (расширения там не поддерживались); работает в текущем headless=new. - Локальный форвардер-прокси на
127.0.0.1, который хранит учетные данные и пересылает запросы вышестоящему прокси; укажите Chrome на него без аутентификации. Не зависит от фреймворка и надежен. - Аутентификация по белому списку от провайдера, если у вашей машины статический IP — полностью обходит проблему.
Firefox
opts = webdriver.FirefoxOptions()
opts.set_preference("network.proxy.type", 1)
opts.set_preference("network.proxy.http", "proxy.example.com"); opts.set_preference("network.proxy.http_port", 8080)
opts.set_preference("network.proxy.ssl", "proxy.example.com"); opts.set_preference("network.proxy.ssl_port", 8080)
opts.set_preference("network.proxy.socks_remote_dns", True) # удаленный DNS при использовании SOCKS
driver = webdriver.Firefox(options=opts)
Firefox все равно покажет диалог аутентификации для прокси с именем пользователя/паролем; применимы подходы с локальным форвардером или белым списком.
Что прокси не решает в headless-браузере
Настоящий браузер позволяет обойти TLS и HTTP/2 фингерпринтинг — рукопожатие действительно Chrome (см. TLS-фингерпринтинг). Но сам по себе он не обходит JavaScript-слой:
navigator.webdriverравенtrueпри автоматизации; сайты проверяют это в первую очередь.- Headless-режим имеет различия в рендеринге и API (отсутствующие плагины, специфические строки WebGL-рендерера, размеры окна), которые проверяют фингерпринтинг-скрипты. Новый headless Chrome (
headless=new) гораздо ближе к обычному, но не идентичен. - WebRTC может раскрыть реальный IP машины, если его не отключить или не подменить; прокси его не трогает. Проверьте это — Как тестировать прокси.
- Часовой пояс, локаль и геолокация берутся с машины, если их не задать (Playwright:
timezone_id,locale,geolocation); несоответствие стране прокси — классический признак. - Поведение: мгновенные переходы, отсутствие движения мыши, одинаковое время выполнения каждого запуска.
Стелс-плагины (puppeteer-extra-plugin-stealth, playwright-stealth) маскируют наиболее очевидные артефакты; антидетект-браузеры идут дальше, предоставляя согласованный фингерпринт для каждого профиля и открывая отладочный порт, к которому подключается ваш скрипт Playwright/Puppeteer — это производственное решение для работы с аккаунтами. См. что такое антидетект-браузер и, для выбора типа IP для каждого профиля, наш рейтинг прокси для антидетект-браузеров.
Выбор фреймворка для работы с прокси
| Playwright | Puppeteer | Selenium | |
|---|---|---|---|
| Прокси с аутентификацией | Нативно (поля username/password) | page.authenticate() для каждой страницы | Требуется обходной путь (Wire, расширение, локальный форвардер) |
| Разный прокси для каждой сессии | Для каждого контекста, один браузер | Один браузер на прокси | Один драйвер на прокси |
| SOCKS5 | Да (без учетных данных → белый список) | Через флаг (без учетных данных) | Через опции/предпочтения |
| Локаль / часовой пояс для каждой сессии | Нативные опции контекста | Эмуляция API / флаги | Предпочтения / CDP |
| Лучше всего подходит для | Мульти-идентичного скрапинга и тестирования | Автоматизации Chrome на Node.js | Легаси-наборов, кросс-браузерных гридов |
Часто задаваемые вопросы
Как использовать прокси с аутентификацией в Playwright?
Передайте proxy={"server": "http://host:port", "username": "…", "password": "…"} в launch() или, для каждого контекста, в new_context() (сначала запустите с proxy={"server": "per-context"}).
Как использовать прокси с аутентификацией в Puppeteer?
Запустите с args: ['--proxy-server=host:port'] и вызовите await page.authenticate({username, password}) перед page.goto() на каждой странице.
Почему Chrome игнорирует user:pass@ в --proxy-server?
Флаг Chromium принимает только scheme://host:port; учетные данные не разбираются. Он отвечает на 407 от прокси интерактивным диалогом, поэтому автоматизация должна предоставлять учетные данные через механизм фреймворка.
Можно ли использовать разный прокси для каждой вкладки?
Не для каждой вкладки. Playwright делает это для каждого контекста браузера (каждый контекст — изолированная сессия); Puppeteer и Selenium требуют отдельного экземпляра браузера на прокси или локального переключающего прокси.
Делает ли прокси headless Chrome невидимым?
Нет. Он решает проблему IP-слоя и, будучи настоящим браузером, TLS-слоя. Флаги автоматизации, артефакты headless, WebRTC, несоответствие часового пояса и поведение — это отдельные сигналы, требующие отдельной обработки.
Какой прокси использовать для браузерной автоматизации: резидентный или датацентровый?
Тот, который соответствует IP-политике целевого сайта — браузер не меняет эту логику. Датацентровые, где допустимо; резидентные или мобильные на защищенных сайтах; статические (ISP) для постоянных профилей с логином. См. сравнение типов прокси.