Aviso legal: Este artigo pressupõe automação autorizada e coleta exclusivamente de dados públicos. Acesse apenas sites onde você tem permissão ou cujos robots.txt e Termos de Serviço permitam. Nos EUA, o CFAA restringe acesso não autorizado a sistemas computacionais; na UE, o GDPR impõe limites ao processamento de dados pessoais. Raspagem não autorizada pode violar ambos.
Se você ainda usa undetected-chromedriver e vê bloqueios crescentes do Cloudflare, Imperva e DataDome, é hora de migrar. Scraping com nodriver significa usar o sucessor assíncrono do undetected-chromedriver, que remove completamente o Selenium/WebDriver e fala diretamente com o Chrome DevTools Protocol (CDP) via websockets. Combinado com um nodriver proxy residencial bem configurado, você obtém um navegador headless que parece um usuário real — sem navigator.webdriver, sem vazamentos de CDP, com IPs de ISPs legítimos.
Por que scraping com nodriver supera o undetected-chromedriver
O nodriver foi criado pelo mesmo autor do undetected-chromedriver, mas com uma premissa diferente: zero Selenium. O Selenium/WebDriver injeta navigator.webdriver=true, adiciona cabeçalhos webdriver e expõe endpoints CDP detectáveis. Soluções anti-bot modernas como Cloudflare Turnstile e Imperva Incapsula procuram exatamente esses sinais.
O nodriver elimina essa superfície de ataque ao:
- Conectar-se via CDP puro sobre websockets, sem o intermediário WebDriver.
- Remover
navigator.webdrivere outros artefatos de automação no nível do processo Chrome. - Usar async/await nativo do Python, sem
time.sleep()ou esperas implícitas. - Expor hooks de eventos (network, console, dialog) que substituem
WebDriverWait.
Para quem faz nodriver web scraping em larga escala, isso significa menos CAPTCHAs, menos retries e maior taxa de sucesso. Em testes práticos, páginas protegidas por Cloudflare que bloqueiam 100% das requisições Selenium costumam responder 200 OK com nodriver + proxy residencial.
Arquitetura: CDP over websockets
O nodriver lança um processo Chrome real (não headless por padrão, o que é melhor para stealth) e anexa a ele via chrome://devtools. Cada aba é um Tab exposto como objeto async. Não há driver.get() síncrono — tudo é await tab.get(url).
Essa arquitetura tem três implicações práticas:
- Menor latência: sem overhead do protocolo W3C WebDriver, as chamadas CDP são ~30–50% mais rápidas.
- Mais controle: você pode interceptar requests (
Network.requestWillBeSent), modificar headers e responder a dialogs em tempo real. - Mais stealth: sem
webdrivernonavigator, semcdc_variáveis nowindow.
Configurando um nodriver proxy residencial
O nodriver não tem rotação de proxy embutida. Isso é por design — o autor prefere que você controle a estratégia de IP. Para passar um proxy, você usa browser_args com a flag --proxy-server= do Chromium:
import nodriver as uc
async def main():
browser = await uc.start(
browser_args=[
'--proxy-server=http://gate.proxyhat.com:8080',
'--disable-blink-features=AutomationControlled',
],
headless=False,
)
page = await browser.get('https://example.com')
await page.save_screenshot('example.png')
browser.stop()
if __name__ == '__main__':
uc.loop().run_until_complete(main())
Esse exemplo funciona, mas há um problema: o Chromium não suporta autenticação de proxy via flag. Se o seu endpoint exigir usuário e senha, você verá um diálogo de autenticação. A solução idiomatic é usar um extension proxy injetado via CDP ou um pequeno plugin carregado com --load-extension.
O método mais limpo, porém, é usar um proxy com IP allowlist ou um gateway que não exija auth no nível HTTP. O ProxyHat suporta ambos: você pode allowlistar o IP do seu servidor no dashboard ou usar autenticação no formato user-country-US:pass@gate.proxyhat.com:8080 via um interceptor CDP.
Construindo a URL do proxy com geo-targeting
O ProxyHat usa flags no username para geo-targeting e sessões sticky. Veja os formatos:
| Objetivo | Formato do username | Comportamento |
|---|---|---|
| IP dos EUA rotativo | user-country-US | Novo IP por request |
| Berlim, Alemanha | user-country-DE-city-berlin | Geo-cidade fixa |
| Sessão sticky 30 min | user-session-abc123 | Mesmo IP enquanto a sessão durar |
| SOCKS5 mobile | user-country-US-session-mob1 na porta 1080 | IPs 4G/5G |
Como o Chromium não envia credenciais de proxy nativamente, a abordagem recomendada no nodriver é injetar auth via CDP usando Fetch.authRequired ou carregar uma extensão que define chrome.webRequest.onAuthRequired. Abaixo, um helper reutilizável que injeta a extensão de proxy em runtime:
import nodriver as uc
import base64, json, zipfile, io, os
def build_proxy_extension(host, port, user, password, scheme='http'):
manifest = {
'version': '1.0.0',
'manifest_version': 2,
'name': 'ProxyHat Auth',
'permissions': ['proxy', 'tabs', 'unlimitedStorage', 'storage'],
'background': {'scripts': ['background.js']},
}
background = f'''
var config = {{
mode: 'fixed_servers',
rules: {{
singleProxy: {{
scheme: '{scheme}',
host: '{host}',
port: parseInt({port})
}},
bypassList: ['localhost']
}}
}};
chrome.proxy.settings.set({{value: config, scope: 'regular'}}, function(){{}});
function callbackFn(details) {{
return {{
authCredentials: {{
username: '{user}',
password: '{password}'
}}
}};
}}
chrome.webRequest.onAuthRequired.addListener(
callbackFn,
{{urls: ['<all_urls>']}},
['blocking']
);
'''
zip_buf = io.BytesIO()
with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.writestr('manifest.json', json.dumps(manifest))
zf.writestr('background.js', background)
return zip_buf.getvalue()
async def start_with_proxy(country='US', session='abc123'):
ext_bytes = build_proxy_extension(
host='gate.proxyhat.com',
port=8080,
user=f'user-country-{country}-session-{session}',
password='sua_senha_aqui',
)
ext_path = '/tmp/proxyhat_ext.zip'
with open(ext_path, 'wb') as f:
f.write(ext_bytes)
browser = await uc.start(
browser_args=[
f'--load-extension={ext_path}',
'--disable-extensions-except={ext_path}',
],
headless=False,
)
return browser
Essa extensão define o proxy e responde ao desafio de autenticação automaticamente. É a forma mais estável de usar nodriver proxy com credenciais, e funciona tanto em modo headful quanto headless (com --headless=new).
Exemplo completo: nodriver undetected em página protegida
Vamos juntar tudo: iniciar o nodriver com proxy residencial dos EUA, sessão sticky, navegar em uma página que usa Cloudflare e extrair JSON de uma chamada XHR interceptada.
import nodriver as uc
import asyncio, json
PROXYHAT_USER = 'user-country-US-session-abc123'
PROXYHAT_PASS = 'sua_senha_aqui'
async def scrape_protected():
browser = await start_with_proxy(country='US', session='abc123')
tab = await browser.get('https://exemplo-protegido.com')
# Hook para capturar respostas XHR
api_data = []
async def on_response(event):
if '/api/produtos' in event.response.url:
body = await event.response.body
api_data.append(json.loads(body))
tab.add_handler(uc.cdp.network.ResponseReceived, on_response)
# Aguarda o botão carregar usando select (substitui WebDriverWait)
btn = await tab.select('button#load-more', timeout=20)
await btn.click()
# Espera a chamada XHR completar
await asyncio.sleep(3)
print(f'Capturados {len(api_data)} lotes de produtos')
if api_data:
with open('produtos.json', 'w') as f:
json.dump(api_data, f, ensure_ascii=False, indent=2)
browser.stop()
if __name__ == '__main__':
uc.loop().run_until_complete(scrape_protected())
Pontos-chave desse exemplo:
tab.select()substituiWebDriverWait— é async e disparaTimeoutErrornativo.- O handler
ResponseReceivedcaptura JSON sem precisar depage.evaluate(fetch(...)). - A sessão sticky
abc123mantém o mesmo IP residencial durante toda a navegação — importante para sites que validam consistência de IP entre requests. headless=False(ou--headless=new) reduz detecção; headless clássico é mais fingerprintável.
Escalando: nodriver async com múltiplas abas e frotas headless
Para escalar nodriver web scraping, você precisa de três coisas: concorrência de abas, isolamento de contexto por proxy e shutdown gracioso. O nodriver suporta múltiplas Tab em um mesmo Browser, mas para isolamento máximo de fingerprint, o ideal é um Browser por proxy.
Padrão: pool de browsers com proxies diferentes
import nodriver as uc
import asyncio
async def worker(country, session, urls, results):
browser = await start_with_proxy(country=country, session=session)
tab = await browser.get('about:blank')
for url in urls:
try:
page = await tab.get(url, new_tab=True)
title = await page.evaluate('document.title')
results.append({'url': url, 'title': title, 'country': country})
await page.close()
except Exception as e:
print(f'Erro em {url}: {e}')
browser.stop()
async def main():
tasks = []
results = []
countries = ['US', 'DE', 'BR', 'JP']
url_batches = [
['https://site1.com', 'https://site2.com']
for _ in countries
]
for country, batch in zip(countries, url_batches):
session = f'sess-{country}-001'
tasks.append(worker(country, session, batch, results))
await asyncio.gather(*tasks)
print(f'Total coletado: {len(results)}')
if __name__ == '__main__':
uc.loop().run_until_complete(main())
Frota headless em Docker
Para produção, containerize cada worker com seu próprio Chrome. Use uma imagem base como zenika/alpine-chrome ou selenium/standalone-chrome (sem Selenium, apenas o Chrome) e exponha o CDP na porta 9222. Um orquestrador (Kubernetes, Docker Swarm ou até docker-compose) escala horizontalmente.
Exemplo de docker-compose.yml com 4 workers:
version: '3.8'
services:
scraper-us:
build: ./scraper
environment:
- PROXYHAT_USER=user-country-US-session-fleet-us
- PROXYHAT_PASS=${PROXYHAT_PASS}
- TARGET=https://alvo.com
deploy:
replicas: 4
scraper-de:
build: ./scraper
environment:
- PROXYHAT_USER=user-country-DE-session-fleet-de
- PROXYHAT_PASS=${PROXYHAT_PASS}
- TARGET=https://alvo.de
deploy:
replicas: 2
Com 6 containers, 2 IPs sticky por país e 4 abas concorrentes cada, você pode processar ~24 páginas em paralelo com IPs residenciais distintos — algo que custaria centenas de dólares em proxies datacenter e ainda assim seria bloqueado.
Shutdown gracioso
Nunca use browser.stop() dentro de except sem finally. Se o processo for morto (SIGTERM), o Chrome órfão consome RAM. Use:
async def main():
browser = await start_with_proxy(country='US')
try:
await do_work(browser)
finally:
browser.stop()
# Garante que o processo Chrome morra
if browser.process:
browser.process.kill()
Erros comuns e edge cases
1. Usar headless=True sem --headless=new
O headless clássico do Chrome tem fingerprint diferente do headful. Sites como Cloudflare detectam navigator.webdriver e diferenças em User-Agent. Prefira headless=False em servidor com Xvfb, ou use --headless=new (Chrome 112+).
2. Esquecer de fechar abas
Cada Tab aberta consome ~80–150 MB de RAM. Em loops longos, vazamentos de abas derrubam o processo. Sempre chame await page.close() ou use async with se disponível.
3. Misturar proxies datacenter com nodriver
O nodriver resolve a detecção de navegador, não de IP. Cloudflare bloqueia IPs datacenter conhecidos (AWS, DigitalOcean, OVH) antes mesmo de avaliar o fingerprint. Proxies residenciais são essenciais para stealth real. Veja opções em /pt/locations.
4. Ignorar robots.txt
Respeite robots.txt mesmo em scraping autorizado. É sinal de boa-fé e reduz risco legal. Use robotexclusionrulesparser para validar.
5. Não tratar CAPTCHAs
Mesmo com nodriver + proxy residencial, alguns sites exibem CAPTCHA em 0,1% das visitas. Tenha fallback: tentar outro IP, esperar 60s, ou usar um serviço de resolução. Nunca dependa de 100% de sucesso.
Quando NÃO usar um browser completo
O nodriver é poderoso, mas caro. Cada instância consome 300–500 MB de RAM e 1 núcleo de CPU. Se o site não requer JavaScript (APIs REST, HTML estático), use httpx + selectolax com proxy residencial — é 50–100x mais rápido e barato.
Para sites que validam apenas TLS fingerprint (JA3/JA4), curl_cffi com impersonate='chrome' + proxy residencial resolve 80% dos casos sem browser. Reserve nodriver para páginas com challenge JS complexo, canvas fingerprinting ou Cloudflare Turnstile.
Veja mais estratégias em /pt/use-cases/web-scraping e /pt/use-cases/serp-tracking.
Configuração ProxyHat: passo a passo
- Crie sua conta em dashboard.proxyhat.com e obtenha suas credenciais.
- Allowliste o IP do seu servidor ou use autenticação por usuário/senha.
- Escolha o plano residencial adequado — veja /pt/pricing.
- Construa a URL do proxy:
http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080 - Use a extensão de proxy (snippet acima) ou injete auth via CDP.
- Teste com
curl -x http://user-country-US:pass@gate.proxyhat.com:8080 https://ipinfo.ioantes de integrar no nodriver.
Documentação completa do gateway: docs.proxyhat.com.
Key Takeaways
- nodriver remove Selenium/WebDriver e fala CDP direto — menos detecção, mais velocidade.
- Proxy residencial é obrigatório para stealth real; datacenter é bloqueado antes do fingerprint.
- Use sessões sticky (
user-session-xxx) para manter consistência de IP em fluxos multi-request.- Autenticação de proxy no Chromium exige extensão ou CDP auth handler — a flag
--proxy-servernão envia credenciais.- Escale com um Browser por proxy, não com muitas abas em um único contexto.
- Não use browser se não precisar de JS —
curl_cffi+ proxy residencial é 50x mais barato.- Respeite
robots.txt, ToS e GDPR/CFAA — scraping ético reduz risco legal e reputacional.
FAQ
O que é scraping com nodriver?
É a prática de raspagem web usando a biblioteca nodriver, sucessora assíncrona do undetected-chromedriver. O nodriver controla o Chrome via Chrome DevTools Protocol (CDP) sobre websockets, sem Selenium/WebDriver, removendo artefatos detectáveis como navigator.webdriver. Isso reduz bloqueios de anti-bots como Cloudflare e Imperva, especialmente quando combinado com proxies residenciais.
Por que o nodriver importa para usuários de proxy?
Porque mesmo o melhor proxy residencial é bloqueado se o navegador parecer automatizado. O nodriver elimina vazamentos de CDP e webdriver que anti-bots usam para detectar Selenium. Combinado com IPs residenciais do ProxyHat, você obtém um navegador que se parece com um usuário real em todos os níveis: fingerprint de browser, TLS, e IP de ISP. Sem nodriver, proxies caros são desperdiçados em bloqueios de fingerprint.
Qual tipo de proxy funciona melhor com nodriver?
Proxies residenciais são os mais eficazes, pois usam IPs de ISPs reais que o Cloudflare e o Imperva não bloqueiam por range. Datacenter proxies são detectados por ASN (AWS, OVH, DigitalOcean) antes mesmo do fingerprint ser avaliado. Para casos extremos (login, contas sensíveis), proxies mobile 4G/5G na porta SOCKS5 1080 oferecem a maior reputação de IP. Configure via --proxy-server=gate.proxyhat.com:8080 com extensão de auth.
Como evitar bloqueios ao implementar scraping com nodriver?
Use quatro camadas: (1) nodriver com headless=False ou --headless=new; (2) proxy residencial com sessão sticky para consistência de IP; (3) delays aleatórios entre 2–8s e rotação de User-Agent; (4) respeito a robots.txt e rate limits. Adicione fallback de CAPTCHA e monitoramento de taxa de sucesso. Se a taxa cair abaixo de 90%, pause e troque de sessão proxy.
O nodriver suporta rotação de proxy automática?
Não. O nodriver não tem rotação embutida por design — você controla a estratégia de IP. Para rotar, use o flag user-country-US (sem session) no username do ProxyHat para obter um novo IP por request, ou implemente um middleware que troque a extensão de proxy a cada N requisições. Sessões sticky (user-session-xxx) mantêm o mesmo IP por até 30 minutos, ideal para fluxos multi-página.






