Sites modernos aprenderam a reconhecer navegadores automatizados (Selenium, Puppeteer, Playwright) e bloqueiam suas solicitações. Navegadores headless deixam dezenas de rastros digitais, pelos quais sistemas anti-bot detectam a automação em milissegundos. Neste guia, vamos explorar todos os métodos de mascaramento de navegadores headless com exemplos de código em Python e JavaScript, para que seus scrapers funcionem de forma estável sem bloqueios.
O artigo é destinado a desenvolvedores que trabalham com web scraping, automação de testes ou coleta de dados de sites protegidos. Vamos discutir os detalhes técnicos da detecção e soluções práticas para contornar a proteção.
Como os sites detectam navegadores headless: métodos principais
Sistemas anti-bot utilizam uma verificação em múltiplas camadas do navegador, analisando centenas de parâmetros. Navegadores headless diferem dos normais em muitos aspectos que não podem ser ocultados apenas trocando o User-Agent. Compreender os métodos de detecção é o primeiro passo para um mascaramento eficaz.
Marcadores JavaScript de automação
O método mais comum é a verificação de propriedades JavaScript que aparecem apenas em navegadores automatizados:
navigator.webdriver— retornatrueem Selenium e Puppeteerwindow.chrome— ausente no Chrome headlessnavigator.plugins.length— igual a 0 no modo headlessnavigator.languages— frequentemente um array vazio ou contém apenas "en-US"navigator.permissions— a API funciona de maneira diferente no modo headless
Análise do Chrome DevTools Protocol
Puppeteer e Playwright controlam o navegador através do Chrome DevTools Protocol (CDP). A presença de uma conexão CDP pode ser detectada através de verificações JavaScript específicas que analisam objetos window.cdc_ ou verificam anomalias no comportamento de eventos de mouse e teclado.
Canvas e WebGL Fingerprinting
Navegadores headless geram impressões de Canvas e WebGL idênticas, pois utilizam renderização por software em vez de hardware. Sistemas anti-bot criam um elemento Canvas invisível, desenham texto ou formas nele e calculam o hash da imagem. Se milhares de usuários têm um hash idêntico — isso é um sinal de bots.
Análise comportamental
Sistemas modernos (DataDome, PerimeterX, Cloudflare Bot Management) analisam movimentos do mouse, velocidade de rolagem, padrões de cliques. Navegadores headless executam ações instantaneamente e sem atrasos naturais, o que revela a automação. Eventos também são analisados: em um navegador normal, sempre ocorre um evento mousemove antes do clique, enquanto bots frequentemente clicam sem movimento prévio do mouse.
Importante: Sistemas anti-bot modernos utilizam aprendizado de máquina para analisar centenas de parâmetros simultaneamente. Mascarar apenas um parâmetro (por exemplo, User-Agent) não protegerá contra bloqueios — é necessária uma abordagem abrangente.
Remoção de navigator.webdriver e outros marcadores JavaScript
A propriedade navigator.webdriver é o método mais simples de detecção de Selenium e outras ferramentas WebDriver. Em um navegador normal, essa propriedade retorna undefined, enquanto em um automatizado — true. É possível removê-la executando código JavaScript antes de carregar a página.
Selenium (Python): remoção do webdriver através do CDP
Para Selenium, é necessário usar o Chrome DevTools Protocol para executar JavaScript antes de carregar qualquer página:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--disable-blink-features=AutomationControlled')
driver = webdriver.Chrome(options=options)
# Remoção de navigator.webdriver através do CDP
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
'''
})
driver.get('https://example.com')
A opção --disable-blink-features=AutomationControlled desativa a flag que o Chrome adiciona no modo de automação. Esta é uma proteção básica que deve ser combinada com outros métodos.
Puppeteer (Node.js): mascaramento através de Page.evaluateOnNewDocument
No Puppeteer, utiliza-se o método page.evaluateOnNewDocument() para executar código antes de carregar a página:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--disable-blink-features=AutomationControlled']
});
const page = await browser.newPage();
// Remoção do webdriver e outros marcadores
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
// Adição do objeto chrome
window.chrome = {
runtime: {}
};
// Emulação de plugins
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});
});
await page.goto('https://example.com');
})();
Playwright: opções de mascaramento integradas
O Playwright possui um mascaramento mais avançado por padrão, mas ainda assim requer configuração adicional:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
headless: true,
args: ['--disable-blink-features=AutomationControlled']
});
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
const page = await context.newPage();
await page.addInitScript(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
});
await page.goto('https://example.com');
})();
Mascaramento do Chrome DevTools Protocol
Puppeteer e Playwright deixam rastros de conexão CDP que podem ser detectados através da análise de objetos window. Sistemas anti-bot buscam variáveis com o prefixo cdc_, $cdc_ ou __webdriver_, que são criadas pelo Chrome ao se conectar através do DevTools Protocol.
Remoção de variáveis CDP
O seguinte script remove todas as variáveis relacionadas à automação do objeto window:
await page.evaluateOnNewDocument(() => {
// Remoção de todas as variáveis com o prefixo cdc_
const cdcProps = Object.keys(window).filter(prop =>
prop.includes('cdc_') ||
prop.includes('$cdc_') ||
prop.includes('__webdriver_')
);
cdcProps.forEach(prop => {
delete window[prop];
});
// Redefinição de document.__proto__ para ocultar CDP
const originalQuery = document.querySelector;
document.querySelector = function(selector) {
if (selector.includes('cdc_')) return null;
return originalQuery.call(this, selector);
};
});
Uso de versões patchadas do Chromium
Existem builds modificadas do Chromium que não deixam rastros de CDP. Por exemplo, a biblioteca puppeteer-extra com o plugin puppeteer-extra-plugin-stealth aplica automaticamente dezenas de patches para mascarar CDP.
Configuração de User-Agent e cabeçalhos HTTP
Navegadores headless utilizam strings de User-Agent desatualizadas ou não realistas. Por exemplo, o Puppeteer adiciona por padrão a palavra "HeadlessChrome" no User-Agent. Além disso, é necessário configurar cabeçalhos adicionais que estão presentes nas solicitações de navegadores normais.
User-Agent atualizados para mascaramento
Utilize User-Agent recentes de navegadores reais. Aqui estão exemplos para 2024:
# Chrome 120 no Windows 10
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
# Chrome 120 no macOS
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
# Firefox 121 no Windows
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Configuração de cabeçalhos no Selenium
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
# Cabeçalhos adicionais através do CDP
driver.execute_cdp_cmd('Network.setUserAgentOverride', {
"userAgent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
"platform": 'Win32',
"acceptLanguage": 'en-US,en;q=0.9'
})
Configuração de cabeçalhos no Puppeteer
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
await page.setExtraHTTPHeaders({
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document'
});
Os cabeçalhos Sec-Fetch-* são críticos — eles apareceram no Chrome 76+ e sua ausência revela versões antigas de navegadores ou bots.
Emulação de Canvas e WebGL Fingerprint
Canvas e WebGL fingerprinting são métodos poderosos de detecção, pois navegadores headless geram impressões idênticas. Sistemas anti-bot criam um Canvas invisível, desenham texto nele e calculam o hash dos pixels. Se milhares de solicitações têm o mesmo hash — são bots.
Adicionando ruído ao Canvas
O seguinte script adiciona ruído aleatório à impressão do Canvas, tornando cada solicitação única:
await page.evaluateOnNewDocument(() => {
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
const originalToBlob = HTMLCanvasElement.prototype.toBlob;
const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
// Função para adicionar ruído
const addNoise = (canvas, context) => {
const imageData = originalGetImageData.call(context, 0, 0, canvas.width, canvas.height);
for (let i = 0; i < imageData.data.length; i += 4) {
imageData.data[i] += Math.floor(Math.random() * 10) - 5;
imageData.data[i + 1] += Math.floor(Math.random() * 10) - 5;
imageData.data[i + 2] += Math.floor(Math.random() * 10) - 5;
}
context.putImageData(imageData, 0, 0);
};
HTMLCanvasElement.prototype.toDataURL = function() {
addNoise(this, this.getContext('2d'));
return originalToDataURL.apply(this, arguments);
};
});
Emulação de parâmetros WebGL
WebGL revela informações sobre a placa de vídeo e drivers. No modo headless, esses parâmetros revelam renderização por software:
await page.evaluateOnNewDocument(() => {
const getParameter = WebGLRenderingContext.prototype.getParameter;
WebGLRenderingContext.prototype.getParameter = function(parameter) {
// Emulação de uma placa de vídeo real
if (parameter === 37445) {
return 'Intel Inc.';
}
if (parameter === 37446) {
return 'Intel Iris OpenGL Engine';
}
return getParameter.call(this, parameter);
};
});
O parâmetro 37445 é UNMASKED_VENDOR_WEBGL, e 37446 é UNMASKED_RENDERER_WEBGL. No modo headless, eles retornam "Google SwiftShader", o que revela a automação.
Selenium Stealth: soluções prontas para Python
A biblioteca selenium-stealth aplica automaticamente dezenas de patches para mascarar o Selenium. Esta é a solução mais simples para desenvolvedores Python, que não requer configuração manual de cada parâmetro.
Instalação e configuração básica
pip install selenium-stealth
from selenium import webdriver
from selenium_stealth import stealth
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
driver = webdriver.Chrome(options=options)
# Aplicação dos patches stealth
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
driver.get("https://bot.sannysoft.com")
driver.save_screenshot("test.png")
A biblioteca remove automaticamente navigator.webdriver, adiciona window.chrome, emula plugins, mascara WebGL e aplica mais de 20 patches. Isso cobre 80% dos casos de detecção.
Configuração avançada com proxies
Para um mascaramento completo, combine selenium-stealth com proxies residenciais — eles fornecem IPs reais de usuários domésticos, o que é crítico para contornar sistemas anti-bot avançados:
from selenium.webdriver.common.proxy import Proxy, ProxyType
proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = "ip:port"
proxy.ssl_proxy = "ip:port"
capabilities = webdriver.DesiredCapabilities.CHROME
proxy.add_to_capabilities(capabilities)
driver = webdriver.Chrome(desired_capabilities=capabilities, options=options)
stealth(driver, languages=["en-US", "en"], vendor="Google Inc.", platform="Win32")
Puppeteer Extra Stealth Plugin para Node.js
Para Puppeteer, existe o plugin puppeteer-extra-plugin-stealth, que é a solução mais avançada para mascarar navegadores headless no ecossistema JavaScript. Ele contém 23 módulos independentes, cada um mascarando um aspecto específico da automação.
Instalação e uso básico
npm install puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.goto('https://bot.sannysoft.com');
await page.screenshot({ path: 'test.png' });
await browser.close();
})();
O que o Stealth Plugin mascara
O plugin aplica automaticamente os seguintes patches:
- Remoção de
navigator.webdriver - Adição do objeto
window.chrome - Emulação da API
navigator.permissions - Mascaramento de
navigator.pluginsenavigator.mimeTypes - Emulação de Canvas e WebGL fingerprint
- Mascaramento de User-Agent e idiomas
- Correção de anomalias no iframe contentWindow
- Emulação da API Battery, Media Devices, WebRTC
Configuração com proxies e parâmetros adicionais
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker');
puppeteer.use(StealthPlugin());
puppeteer.use(AdblockerPlugin({ blockTrackers: true }));
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: [
'--proxy-server=http://your-proxy:port',
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process'
]
});
const page = await browser.newPage();
// Autenticação do proxy
await page.authenticate({
username: 'your-username',
password: 'your-password'
});
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
})();
Playwright: configuração de anti-detectores
O Playwright possui uma arquitetura mais sofisticada em comparação ao Puppeteer e deixa menos rastros de automação. No entanto, para contornar sistemas anti-bot avançados, é necessária configuração adicional. Para o Playwright, existe a versão do plugin stealth — playwright-extra.
Instalação do playwright-extra
npm install playwright-extra playwright-extra-plugin-stealth playwright
const { chromium } = require('playwright-extra');
const stealth = require('playwright-extra-plugin-stealth');
chromium.use(stealth());
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
viewport: { width: 1920, height: 1080 },
locale: 'en-US',
timezoneId: 'America/New_York'
});
const page = await context.newPage();
await page.goto('https://bot.sannysoft.com');
await page.screenshot({ path: 'test.png' });
await browser.close();
})();
Configuração do contexto do navegador para máxima mascaramento
O Playwright permite a criação de contextos de navegador isolados com configurações individuais. Isso é útil para multi-contas ou scraping paralelo:
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
viewport: { width: 1440, height: 900 },
locale: 'en-US',
timezoneId: 'America/Los_Angeles',
permissions: ['geolocation'],
geolocation: { latitude: 37.7749, longitude: -122.4194 },
colorScheme: 'light',
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false,
// Configuração de proxy para o contexto
proxy: {
server: 'http://your-proxy:port',
username: 'user',
password: 'pass'
}
});
Os parâmetros geolocation e timezoneId devem corresponder ao IP do proxy, caso contrário, os sistemas anti-bot detectarão a discrepância (por exemplo, IP da Califórnia, mas timezone de Nova York).
Rotação de proxies para reduzir o risco de bloqueio
Mesmo um navegador headless perfeitamente mascarado pode ser bloqueado se usar um único IP para centenas de solicitações. Sistemas anti-bot modernos analisam a frequência de solicitações de um único IP e bloqueiam atividades suspeitas. A rotação de proxies é um elemento obrigatório de proteção durante o scraping.
Tipos de proxies para scraping: comparação
| Tipo de proxy | Velocidade | Trust Score | Melhor para |
|---|---|---|---|
| Datacenter | Muito alta (50-200 ms) | Baixa | Sites simples, scraping em massa |
| Residenciais | Média (300-1000 ms) | Alta | Sites protegidos, redes sociais |
| Móveis | Baixa (500-2000 ms) | Muito alta | Aplicativos móveis, Instagram, TikTok |
Para scraping de sites protegidos (marketplaces, redes sociais, plataformas de anúncios), são recomendados proxies residenciais, pois eles possuem IPs reais de usuários domésticos e não entram em listas negras de sistemas anti-bot.
Implementação da rotação de proxies no Puppeteer
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const proxyList = [
'http://user1:pass1@proxy1:port',
'http://user2:pass2@proxy2:port',
'http://user3:pass3@proxy3:port'
];
async function scrapeWithRotation(urls) {
for (let i = 0; i < urls.length; i++) {
const proxy = proxyList[i % proxyList.length];
const browser = await puppeteer.launch({
headless: true,
args: [`--proxy-server=${proxy}`]
});
const page = await browser.newPage();
try {
await page.goto(urls[i], { waitUntil: 'networkidle2' });
const data = await page.evaluate(() => document.body.innerText);
console.log(data);
} catch (error) {
console.error(`Erro em ${urls[i]}:`, error);
} finally {
await browser.close();
}
// Atraso entre solicitações
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
scrapeWithRotation([
'https://example1.com',
'https://example2.com',
'https://example3.com'
]);
Rotação através de proxies baseados em sessão
Alguns provedores de proxies (incluindo ProxyCove) oferecem rotação baseada em sessão — cada solicitação recebe automaticamente um novo IP sem a necessidade de reiniciar o navegador. Isso é implementado através de um formato especial de URL de proxy:
// Formato: username-session-RANDOM:password@gateway:port
const generateSessionProxy = () => {
const sessionId = Math.random().toString(36).substring(7);
return `http://username-session-${sessionId}:password@gateway.proxycove.com:12321`;
};
const browser = await puppeteer.launch({
args: [`--proxy-server=${generateSessionProxy()}`]
});
Como verificar a qualidade do mascaramento: ferramentas de teste
Após configurar o mascaramento, é necessário verificar quão bem seu navegador headless imita um usuário comum. Existem vários sites especializados que analisam dezenas de parâmetros do navegador e mostram quais rastros de automação permaneceram.
Principais ferramentas de teste
- bot.sannysoft.com — verifica mais de 15 parâmetros de detecção, incluindo webdriver, objeto Chrome, plugins, Canvas
- arh.antoinevastel.com/bots/areyouheadless — especializado na detecção de Chrome headless
- pixelscan.net — análise avançada de fingerprint com visualização de todos os parâmetros
- abrahamjuliot.github.io/creepjs — a análise mais detalhada (mais de 200 parâmetros), mostra o nível de confiança do navegador
- iphey.com — verificação do IP para pertencimento a proxies e VPNs
Automação de testes
Crie um script para verificar automaticamente o mascaramento após cada alteração nas configurações:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
async function testStealth() {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// Teste 1: Sannysoft
await page.goto('https://bot.sannysoft.com');
await page.screenshot({ path: 'test-sannysoft.png', fullPage: true });
// Teste 2: Are you headless
await page.goto('https://arh.antoinevastel.com/bots/areyouheadless');
const headlessDetected = await page.evaluate(() => {
return document.body.innerText.includes('You are not Chrome headless');
});
console.log('Headless detectado:', !headlessDetected);
// Teste 3: Propriedade Webdriver
const webdriverPresent = await page.evaluate(() => navigator.webdriver);
console.log('navigator.webdriver:', webdriverPresent);
// Teste 4: Objeto Chrome
const chromePresent = await page.evaluate(() => !!window.chrome);
console.log('window.chrome presente:', chromePresent);
await browser.close();
}
testStealth();
Lista de verificação para mascaramento bem-sucedido
Seu navegador headless está corretamente mascarado se:
navigator.webdriverretornaundefinedwindow.chromeexiste e contém o objetoruntimenavigator.plugins.lengthé maior que 0- O fornecedor e o renderizador do WebGL mostram uma placa de vídeo real, e não SwiftShader
- A impressão do Canvas é única para cada sessão
- O User-Agent corresponde à versão atual do Chrome/Firefox
- O IP do proxy não está em listas negras (verificação através do iphey.com)
- Timezone e locale correspondem à geolocalização do IP
Conclusão
O mascaramento de navegadores headless é uma tarefa complexa que requer atenção a dezenas de parâmetros. Sistemas anti-bot modernos utilizam aprendizado de máquina e analisam centenas de características do navegador simultaneamente, portanto, a simples troca do User-Agent já não funciona. Para um scraping bem-sucedido, é necessário combinar vários métodos de proteção.
Os principais elementos de um mascaramento eficaz incluem a remoção de marcadores JavaScript de automação (navigator.webdriver, variáveis CDP), emulação de Canvas e WebGL fingerprint, configuração de cabeçalhos HTTP realistas e uso de proxies de qualidade. Soluções prontas (selenium-stealth para Python, puppeteer-extra-plugin-stealth para Node.js) cobrem 80% dos casos, mas para contornar proteções avançadas é necessária configuração adicional.
Um ponto crítico é a escolha do proxy. Mesmo um navegador perfeitamente mascarado será bloqueado se usar IPs de listas negras ou fizer muitas solicitações de um único IP. Para scraping de sites protegidos, recomendamos o uso de proxies residenciais com rotação automática — eles oferecem uma alta pontuação de confiança e risco mínimo de bloqueios, pois utilizam IPs reais de usuários domésticos em vez de endereços de servidores.
Teste regularmente a qualidade do mascaramento através de serviços especializados (bot.sannysoft.com, pixelscan.net) e adapte as configurações para mudanças nos sistemas anti-bot. O scraping é uma corrida constante entre desenvolvedores de bots e criadores de proteção, portanto, uma configuração que funciona hoje pode precisar de atualização em alguns meses.