Se você está executando scripts de parsing ou multi-contas no Playwright e inicia uma instância separada do navegador para cada proxy — você está desperdiçando memória e CPU desnecessariamente. O Playwright permite atribuir proxies no nível de BrowserContext, e não no navegador inteiro, o que muda radicalmente a arquitetura dos scripts: um processo de navegador, dezenas de contextos isolados com diferentes IPs, cookies e impressões digitais. Neste artigo, veremos como isso funciona, código em Python e Node.js, e por que isso é especialmente importante ao trabalhar com proxies residenciais e móveis.
Problema: proxy em browser.launch() — caro e inconveniente
A abordagem clássica de muitos scripts no Playwright é a seguinte: para cada proxy, é chamado chromium.launch({ proxy: {...} }), criando um processo separado do navegador, que funciona, depois é fechado, e o próximo é iniciado com um novo proxy. Se você precisa processar 50 endereços IP em paralelo para parsing do Wildberries ou Ozon, isso significa 50 processos completos do Chromium ao mesmo tempo — cada um consome de 150 a 300 MB de RAM apenas na inicialização, sem contar as abas e a renderização.
Na prática, isso atinge o limite já com 15-20 navegadores paralelos em um VPS comum com 8 GB de memória. O servidor começa a usar swap, os scripts falham com timeouts, e parte dos proxies não consegue ser processada. O problema não está nos proxies e nem no Playwright — o problema está na arquitetura: cada inicialização do navegador carrega todo o motor de renderização, o processo da GPU, a rede e dezenas de threads auxiliares, embora na verdade você precise apenas de um novo IP e uma sessão limpa.
O Playwright resolve esse problema de forma elegante: o proxy pode ser definido não na inicialização do navegador, mas na criação do BrowserContext — uma "sandbox" leve e isolada dentro do mesmo processo do navegador. Um processo do Chromium pode manter dezenas de contextos, cada um com seu próprio proxy, seus cookies e localStorage, sem se cruzar.
Navegador vs BrowserContext: qual é a diferença
Browser — é o próprio processo do motor (Chromium, Firefox ou WebKit), que é iniciado uma vez e vive por muito tempo. BrowserContext — é um ambiente isolado dentro desse processo, semelhante a uma janela privada (incógnita), com seus próprios cookies, cache, permissões e, o que é importante para nós, suas próprias configurações de rede, incluindo proxies.
Se compararmos com navegadores anti-detect como Dolphin Anty ou AdsPower — lá, cada "perfil" também é, na verdade, um contêiner isolado separado com cookies e impressões digitais. O Playwright permite construir uma lógica semelhante programaticamente: um navegador — muitos contextos, cada um como um "perfil" separado com seu IP.
| Parâmetro | Proxy no Navegador | Proxy no BrowserContext |
|---|---|---|
| Memória por IP | 150-300 MB (novo processo) | 15-40 MB (novo contexto) |
| Velocidade de troca de IP | 2-5 seg (reinicialização do navegador) | 0.1-0.3 seg (novo contexto) |
| Isolamento de cookies | Completo (processos diferentes) | Completo (contextos não compartilham cookies) |
| Máx. sessões paralelas em 8 GB de RAM | ~20-25 | ~150-200 |
Proxy por contexto: código em Python
Abaixo está o padrão básico: um navegador é iniciado uma vez, e depois para cada proxy um contexto separado é criado. Esta é a API síncrona do Playwright, mas a lógica é idêntica para a versão assíncrona.
from playwright.sync_api import sync_playwright
proxies = [
{"server": "http://proxy1.example.com:8000", "username": "user1", "password": "pass1"},
{"server": "http://proxy2.example.com:8000", "username": "user2", "password": "pass2"},
{"server": "http://proxy3.example.com:8000", "username": "user3", "password": "pass3"},
]
with sync_playwright() as p:
# O navegador é iniciado UMA vez, sem proxy
browser = p.chromium.launch(headless=True)
for proxy in proxies:
# O proxy é atribuído ao contexto, e não ao navegador
context = browser.new_context(
proxy={
"server": proxy["server"],
"username": proxy["username"],
"password": proxy["password"],
},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
)
page = context.new_page()
page.goto("https://httpbin.org/ip", timeout=30000)
print(page.content())
page.close()
context.close() # fechamos apenas o contexto, o navegador continua ativo
browser.close()
O ponto chave é que browser.launch() é chamado uma vez fora do loop, enquanto browser.new_context() — dentro do loop para os proxies. Para proxies com autenticação por login e senha, o Playwright insere automaticamente os cabeçalhos de Basic Auth no nível da rede, não sendo necessário configurar nada adicional.
Proxy por contexto: código em Node.js
A mesma lógica em JavaScript usando async/await — frequentemente aplicada em conjunto com frameworks de parsing em Node.js:
const { chromium } = require('playwright');
const proxies = [
{ server: 'http://proxy1.example.com:8000', username: 'user1', password: 'pass1' },
{ server: 'http://proxy2.example.com:8000', username: 'user2', password: 'pass2' },
];
(async () => {
const browser = await chromium.launch({ headless: true });
for (const proxy of proxies) {
const context = await browser.newContext({
proxy: {
server: proxy.server,
username: proxy.username,
password: proxy.password,
},
});
const page = await context.newPage();
await page.goto('https://httpbin.org/ip', { timeout: 30000 });
console.log(await page.content());
await page.close();
await context.close();
}
await browser.close();
})();
Para processamento paralelo de dezenas de proxies, os contextos podem ser criados através de Promise.all, sem esperar o fechamento do anterior — essa é a principal vantagem da abordagem "um navegador, muitos contextos" em relação ao lançamento de processos separados.
Rotação limpa de proxies sem reiniciar o navegador
"Rotação limpa" significa que ao mudar de IP não ficam vestígios da sessão anterior — nem cookies, nem cache, nem service workers. Ao reiniciar todo o navegador, isso é garantido automaticamente, mas é caro em recursos. Ao trabalhar com contextos, o mesmo é alcançado fechando o contexto antigo e criando um novo — a operação leva frações de segundo.
async function rotateProxy(browser, proxyList, index) {
const proxy = proxyList[index % proxyList.length];
const context = await browser.newContext({ proxy });
const page = await context.newPage();
return { context, page };
}
// Usamos assim: a cada N-ésima iteração mudamos o IP
for (let i = 0; i < 100; i++) {
if (i % 10 === 0) {
const { context, page } = await rotateProxy(browser, proxies, i);
// trabalhamos com uma nova sessão em 10 requisições
}
}
Esse padrão é especialmente útil ao fazer parsing de marketplaces: por exemplo, ao coletar preços do Wildberries ou Ozon, onde a proteção contra bots monitora a quantidade de requisições de um único IP em um determinado período. A rotação no nível do contexto permite mudar de IP a cada 5-10 requisições, sem perder desempenho ao reiniciar o navegador.
Para cenários como esse, proxies residenciais são uma boa opção — eles fornecem IPs de usuários reais, que os marketplaces raramente marcam como suspeitos, ao contrário de faixas claramente de servidores.
Comparação de memória e velocidade
Em um VPS de teste com 8 GB de RAM e 4 vCPU, foi realizada uma verificação de duas abordagens na tarefa de contornar 100 páginas com diferentes IPs. No primeiro caso, para cada IP, um chromium.launch() separado era iniciado, no segundo — um único navegador com a criação de 100 contextos sequencialmente.
| Métrica | 100 lançamentos do browser.launch() | 100 contextos em 1 navegador |
|---|---|---|
| Memória máxima | ~6.8 GB | ~1.9 GB |
| Tempo total de execução | 182 seg | 97 seg |
| Número de sessões que falharam por timeout | 7 | 0 |
A economia de memória neste teste foi de cerca de 70%, e o tempo total de execução foi reduzido quase pela metade devido à ausência de sobrecarga para iniciar e finalizar o processo do Chromium a cada vez. Em grandes volumes — milhares de IPs por dia — a diferença se torna ainda mais perceptível, e isso afeta diretamente quantos proxies você pode usar em um único servidor sem atualizar o hardware.
Casos práticos: parsing e multi-contas
Parsing de marketplaces. Ao coletar preços do Wildberries, Ozon ou Avito, o mesmo navegador mantém dezenas de contextos abertos, cada um com um IP único e seu próprio armazenamento de cookies. Isso reduz o risco de bloqueio por padrão de requisições e permite coletar dados mais rapidamente, sem esperar pela reinicialização do processo entre IPs.
Multi-contas e farm de contas. Para testar campanhas publicitárias no Facebook Ads ou TikTok Ads através de scripts do Playwright, cada conta deve logicamente ser mantida em um contexto separado com seu próprio proxy e user-agent — isso imita o comportamento de navegadores anti-detect como Multilogin ou GoLogin, mas sem a licença para o próprio anti-detect, totalmente programaticamente.
Teste geográfico de anúncios. Para os profissionais de marketing que precisam verificar como um criativo é exibido em diferentes países, contextos com diferentes proxies permitem "visitar" os regiões necessárias em uma única sessão do navegador, sem abrir dezenas de janelas separadas manualmente.
Para cenários onde é importante simular tráfego móvel — por exemplo, verificar a versão móvel de um site ou anúncios no TikTok Ads — é recomendável considerar proxies móveis, que fornecem IPs de operadores de telefonia real e raramente levantam suspeitas em sistemas anti-fraude.
Se a tarefa é um parsing rápido e estável sem a necessidade de alta anonimidade (por exemplo, coletar dados públicos de sites sem proteção rigorosa contra bots), proxies de data center oferecem a melhor relação entre velocidade e custo em grandes volumes de requisições.
Erros comuns e como evitá-los
1. Proxies definidos em launch(), e não em new_context(). Este é o erro mais comum entre iniciantes — o proxy especificado ao iniciar o navegador é aplicado a todos os contextos ao mesmo tempo e não pode ser redefinido individualmente. Sempre passe o proxy em new_context(), se precisar de flexibilidade em relação ao IP.
2. Contextos antigos não são fechados. Se você criar centenas de contextos e não chamar context.close(), a memória do navegador crescerá linearmente até que o processo falhe por OOM. Sempre feche o contexto após concluir a tarefa para esse IP.
3. Um user-agent para todos os contextos. Se o proxy muda, mas a impressão digital do navegador permanece a mesma (user-agent, idioma, fuso horário), os sistemas anti-bot dos marketplaces e redes sociais ainda podem vincular as sessões entre si. Altere o user-agent e o viewport junto com o proxy para cada contexto.
4. Ignorando timeouts de proxies. Proxies residenciais e móveis às vezes respondem mais lentamente do que os de data center devido à infraestrutura de rede real. Defina um timeout razoável (20-40 segundos) ao chamar page.goto(), caso contrário, o script considerará o proxy ativo como "morto".
5. Muitos contextos em um único navegador ao mesmo tempo. Apesar da economia de memória, a criação descontrolada de 500+ contextos ativos ao mesmo tempo ainda atingirá os limites do processo. Um equilíbrio razoável é de 30-80 contextos ativos simultâneos por processo de navegador, dependendo da quantidade de RAM do servidor.
Conclusão
A atribuição de proxies no nível de BrowserContext, e não no navegador inteiro — é uma solução arquitetônica que economiza até 70% de memória, acelera a rotação de IPs em dezenas de vezes e proporciona isolamento limpo de cookies entre sessões sem reinicializações constantes do Chromium. Para parsing de marketplaces, teste de anúncios em diferentes regiões e multi-contas, essa é uma maneira direta de aumentar o número de sessões paralelas no mesmo servidor sem atualizar o hardware.
Se você está construindo um script no Playwright para coletar dados do Wildberries, Ozon ou para trabalhar com painéis de anúncios do Facebook Ads e TikTok Ads, a qualidade do pool de proxies é tão importante quanto a arquitetura do código. Para tarefas onde a confiabilidade do IP e a resistência a sistemas anti-bot são críticas, é aconselhável começar com proxies residenciais — eles combinam bem com a abordagem "um navegador, muitos contextos", descrita acima.