Conectar um proxy residencial ao Playwright é questão de poucas linhas assim que você vê como é feito. A dificuldade nunca está na opção de launch, e sim nos detalhes que ninguém escreve: como as credenciais codificam o targeting, a pegadinha do Chromium que quebra proxies por contexto, como a rotação se mapeia em contextos de navegador, e o fato de que toda imagem que o navegador carrega é banda que você paga.
Este é o complemento de como usar proxies residenciais com Python, para o lado do navegador. Exemplos prontos para copiar e colar que realmente funcionam, além das partes que transformam um snippet funcional em automação que sobrevive em produção.
Tudo abaixo usa o gateway residencial da Shifter: um único endpoint, p.shifter.io:443, com todo o targeting codificado no nome de usuário. Se você usa outro provedor, a estrutura é a mesma; basta trocar o host e o formato de credenciais. Os exemplos são em Node/JavaScript; um snippet de Playwright em Python está perto do final.
A primeira coisa que você precisa entender
Em um gateway residencial, o nome de usuário do proxy carrega sua autenticação e seu targeting. Você não muda de endpoint para trocar de país ou sessão, você muda a string do nome de usuário. Um nome de usuário se parece com isto:
customer-USERNAME-country-us-sid-abc123-ttl-600Leia da esquerda para a direita: id da conta, depois flags. country-us direciona para os EUA. sid-abc123 fixa uma sessão sticky. ttl-600 mantém esse IP por 600 segundos. Remova sid/ttl e toda nova conexão gira para um novo IP. A senha é constante. Esse é todo o modelo mental, o resto é encaixar isso no slot de proxy do Playwright.
Mantenha as credenciais em variáveis de ambiente, nunca fixas no código:
const USER = process.env.SHIFTER_USER; // your account usernameconst PASS = process.env.SHIFTER_PASS;const GATEWAY = "http://p.shifter.io:443";Uma observação específica do Playwright, logo de início: não coloque o nome de usuário e a senha embutidos na URL do proxy. O Chromium ignora credenciais de proxy embutidas, então http://user:pass@host falha silenciosamente na autenticação. O Playwright tem campos dedicados username e password no objeto proxy, use-os.
A versão de 10 linhas
Para um único proxy em todo o navegador, defina proxy em launch():
const { chromium } = require("playwright");
const USER = process.env.SHIFTER_USER;const PASS = process.env.SHIFTER_PASS;
(async () => { const browser = await chromium.launch({ proxy: { server: "http://p.shifter.io:443", username: `${USER}-country-us`, // targeting lives here password: PASS, }, }); const page = await browser.newPage(); await page.goto("https://api.ipify.org?format=json"); console.log(await page.textContent("body")); // {"ip":"<a US residential IP>"} await browser.close();})();O campo username é onde reside todo o modelo do gateway. ${USER}-country-us direciona para os EUA; adicione sid/ttl para um IP sticky, adicione city/asn para restringir ainda mais. A senha nunca muda.
A pegadinha do Chromium: proxies por contexto
A versão de proxy único é boa para uma identidade. Mas o verdadeiro motivo para usar o Playwright é rodar muitas identidades, e para isso você quer um proxy diferente por contexto de navegador (cada contexto tem seus próprios cookies/armazenamento isolados, ou seja, seu próprio “usuário”).
Aqui está a armadilha: no Chromium, proxies por contexto só funcionam se você iniciar o navegador com um proxy já definido. Se você iniciar sem proxy, todo newContext({ proxy }) é ignorado silenciosamente. A solução é um servidor placeholder no launch:
const browser = await chromium.launch({ proxy: { server: "per-context" }, // placeholder, enables per-context proxying});
// Now each context can carry its own real proxy + targetingconst ctxUS = await browser.newContext({ proxy: { server: "http://p.shifter.io:443", username: `${USER}-country-us-sid-a1-ttl-600`, password: PASS, },});const ctxDE = await browser.newContext({ proxy: { server: "http://p.shifter.io:443", username: `${USER}-country-de-sid-b2-ttl-600`, password: PASS, },});ctxUS navega em um IP dos EUA, ctxDE em um IP alemão, ao mesmo tempo, em um único navegador. Este é o padrão sobre o qual construir. (Firefox e WebKit não precisam estritamente do placeholder, mas defini-lo é inofensivo e mantém seu código compatível entre navegadores.)
Rotativo vs sticky, à moda do Playwright
Em um scraper HTTP puro, você costuma rotacionar a cada requisição. Em um navegador, isso geralmente está errado: um usuário real não muda de IP no meio da sessão, então um IP que muda entre carregamentos de página enquanto os cookies permanecem constantes é um sinal de detecção. O mapeamento idiomático é:
- Um contexto = uma identidade sticky. Dê a cada contexto um
sidúnico e umttlque cubra a sessão, para que todos os carregamentos de página compartilhem um IP, como um usuário real. - Rotacione criando novos contextos. Um contexto novo com um
sidnovo (ou semsid) recebe um novo IP. Feche o antigo quando terminar.
async function contextForSession(browser, sessionId, country = "us") { return browser.newContext({ proxy: { server: "http://p.shifter.io:443", username: `${USER}-country-${country}-sid-${sessionId}-ttl-600`, password: PASS, }, });}
// Process 50 targets, each with its own IP-stable identityfor (let i = 0; i < 50; i++) { const ctx = await contextForSession(browser, `job-${i}`, "us"); const page = await ctx.newPage(); await page.goto("https://example.com/item/" + i); // ... scrape ... await ctx.close(); // done with this identity}Escolha o sid você mesmo, qualquer string única por sessão lógica. A mesma string retorna o mesmo IP até o TTL expirar. Execute contextos de forma concorrente (um pequeno pool) em vez de todos os 50 ao mesmo tempo; mais navegadores não é mais velocidade, e uma rajada de IPs martelando um único alvo ainda dispara a detecção comportamental. Se você está sendo bloqueado consistentemente em vez de intermitentemente, o problema é qualidade de IP ou comportamento, não rotação, veja como evitar ser bloqueado ao fazer scraping.
Geo-targeting, e alinhando o navegador ao IP
Como o targeting reside no nome de usuário, a geolocalização é apenas uma flag: country-de, adicione city-berlin ou asn-3320 para restringir. Mas com um navegador real há uma segunda metade que as pessoas perdem: sua fingerprint de navegador precisa concordar com seu IP. Um IP residencial alemão combinado com um locale en-US e um fuso horário de Nova York é uma contradição que sistemas anti-bot sinalizam de imediato. (Veja por que scrapers são bloqueados.)
O Playwright permite alinhar tudo isso no contexto:
const ctx = await browser.newContext({ proxy: { server: "http://p.shifter.io:443", username: `${USER}-country-de-city-berlin-sid-de1-ttl-600`, password: PASS, }, locale: "de-DE", timezoneId: "Europe/Berlin", geolocation: { latitude: 52.52, longitude: 13.405 }, permissions: ["geolocation"],});Nomes de cidades usam o formato do gateway (minúsculas); combine flags livremente, a ordem não importa para o gateway. Alinhar locale/timezoneId/geolocation ao país do proxy é uma das ações de maior impacto para parecer um usuário local genuíno.
Reduza sua banda (você paga por GB)
Este ponto é específico de navegadores e importa, porque proxies residenciais são cobrados por gigabyte. Um navegador headless, deixado sozinho, baixa toda imagem, fonte, vídeo e script de rastreamento da página, a maioria dos quais você não precisa para scraping. Abortá-los pode cortar a banda (e o custo) em bem mais da metade, e acelera suas execuções.
Use context.route() para bloquear tipos de recursos pesados antes que cheguem ao proxy:
await ctx.route("**/*", (route) => { const type = route.request().resourceType(); if (["image", "media", "font"].includes(type)) { return route.abort(); // never downloaded, never billed } return route.continue();});Mantenha document, script, xhr e fetch (os dados reais e o JS que os renderiza); descarte o resto. Se o site carrega conteúdo de forma lazy atrás de imagens, teste se seu scraping ainda funciona, mas para a maioria dos trabalhos isso é dinheiro de graça. Combina bem com o modelo por GB abordado em proxies residenciais precificados por banda.
Verificando se realmente funciona
Antes de confiar em uma configuração, confirme duas coisas: o IP está no país certo (geo) e ele muda entre contextos (rotação). Uma verificação rápida:
const page = await ctx.newPage();await page.goto("http://ip-api.com/json");const data = JSON.parse(await page.textContent("body"));console.log(data.query, data.countryCode); // expect a DE IPCrie dois contextos com sids diferentes e você deve ver dois IPs diferentes, ambos no país correto. Se o país estiver errado, verifique a ortografia da sua flag. Se o IP nunca mudar entre contextos, você provavelmente esqueceu o placeholder no momento do launch e o Chromium está ignorando o proxy por contexto.
Os erros que realmente acontecem
O Playwright expõe problemas de proxy como erros de navegação, não códigos de status HTTP. Os três que você vai encontrar:
net::ERR_TUNNEL_CONNECTION_FAILEDautenticação do proxy falhou, ou uma flag não reconhecida (um erro de digitação emcountryouasn, ou credenciais embutidas que o Chromium ignorou). Corrija os campos username/password; tentar de novo não vai ajudar.net::ERR_PROXY_CONNECTION_FAILEDnão foi possível alcançar o gateway, ou nenhum IP correspondeu a um filtro excessivamente restrito (como country + city + asn). Verifique a conectividade, depois relaxe uma flag.- Uma página que carrega mas mostra um bloqueio/CAPTCHA o alvo, não o proxy, está recusando você. Rotacione para um novo contexto/IP, diminua o ritmo e alinhe a fingerprint como acima.
Envolva a navegação em um pequeno retry que cria um contexto novo em caso de falha:
async function gotoWithRetry(browser, url, country = "us", tries = 3) { for (let attempt = 0; attempt < tries; attempt++) { const ctx = await contextForSession(browser, `r-${Date.now()}-${attempt}`, country); const page = await ctx.newPage(); try { await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 }); return { ctx, page }; // caller closes ctx when done } catch (e) { await ctx.close(); if (attempt === tries - 1) throw e; await new Promise((r) => setTimeout(r, 2 ** attempt * 1000)); // 1s, 2s } }}Um contexto novo significa um IP novo, então um retry realmente obtém uma rota diferente, não a mesma que falhou.
Uma nota sobre SOCKS5
O gateway fala SOCKS5, mas há uma limitação do Playwright que vale conhecer: o Playwright/Chromium não suporta proxies SOCKS autenticados. Como o gateway autentica pelo nome de usuário (é ali que seu targeting reside), o SOCKS5 não é prático aqui, use o proxy HTTP mostrado acima, que é o padrão certo para scraping com navegador de qualquer forma. As vantagens e desvantagens do SOCKS estão em proxies SOCKS5 para automação.
O mesmo em Python
A API Python do Playwright espelha a versão JavaScript; o objeto proxy é idêntico:
from playwright.sync_api import sync_playwrightimport os
USER, PASS = os.environ["SHIFTER_USER"], os.environ["SHIFTER_PASS"]
with sync_playwright() as p: browser = p.chromium.launch(proxy={"server": "per-context"}) ctx = browser.new_context(proxy={ "server": "http://p.shifter.io:443", "username": f"{USER}-country-us-sid-a1-ttl-600", "password": PASS, }) page = ctx.new_page() page.goto("https://api.ipify.org") print(page.text_content("body")) browser.close()Mesma regra do placeholder no launch, mesmo targeting codificado no nome de usuário. Para trabalho HTTP fora do navegador, o guia de Python cobre requests, httpx e Scrapy.
Perguntas frequentes
Por que http://user:pass@host não funciona no Playwright?
O Chromium ignora credenciais de proxy embutidas. Coloque o nome de usuário e a senha nos campos dedicados username/password do objeto proxy. Este é o motivo mais comum de “o proxy não está funcionando” no Playwright.
Por que meus proxies por contexto são ignorados?
Porque você iniciou o Chromium sem proxy. O proxy por contexto só é ativado se o navegador foi iniciado com um, passe um placeholder proxy: { server: "per-context" } para launch(), depois defina proxies reais em cada contexto.
Devo rotacionar o IP a cada requisição em um navegador?
Não. Uma sessão de navegador deve manter um IP, como um usuário real. Use um sid sticky por contexto e rotacione criando novos contextos, não trocando de IP no meio da sessão.
Como executo diferentes países ao mesmo tempo?
Um navegador, múltiplos contextos, cada um com um country-<cc> diferente em seu nome de usuário de proxy. Eles rodam de forma concorrente e independente.
Isso funciona com Puppeteer ou Selenium também?
O gateway funciona. O Puppeteer recebe --proxy-server no launch e autentica via page.authenticate(); o Selenium usa uma capability de proxy ou uma extensão para autenticação. O targeting codificado no nome de usuário é idêntico; só a conexão muda. O modelo por contexto do Playwright é o mais limpo para trabalho com múltiplas identidades.
Minhas execuções estão consumindo banda rapidamente. O que fazer?
Bloqueie imagens, mídia e fontes com context.route() como mostrado acima. Navegadores baixam tudo por padrão, e em um plano por GB isso é dinheiro real. Abortar recursos pesados costuma cortar o uso em mais da metade.
Encerrando
O padrão: inicie o Chromium com um placeholder per-context, dê a cada contexto um proxy cujo username codifica country/sid/ttl, alinhe locale/timezone/geolocation ao país, e aborte imagens e fontes para economizar banda. Rotacione criando novos contextos, não trocando de IP. Erros aparecem como falhas de navegação net::, e um retry que constrói um contexto novo garante um IP novo.
Comece pelos snippets acima, aponte-os para o gateway residencial, e você terá uma automação de navegador pronta para produção em uma tarde. Planos e taxas por GB estão na página de preços, e a referência completa de flags está na documentação do gateway.