A maioria das tarefas de web scraping se resolve com uma simples requisição GET a uma página pública. Mas, assim que os dados necessários ficam «atrás do login» — na área do cliente, em uma seção privada ou sob uma assinatura paga —, a requisição comum devolve o formulário de login ou um erro 401/403. Para chegar ao conteúdo, o scraper precisa primeiro se autenticar, exatamente como faz o navegador do usuário.
Onde se aplica com mais frequência
O cenário mais comum do scraping com autenticação é o monitoramento de preços de lojas online. A situação típica é esta: sua empresa trabalha com um fornecedor cujos preços e estoques atualizados só estão disponíveis na área do cliente do site dele. Não há acesso completo aos dados via API, tampouco exportações no formato necessário, e os preços mudam com tanta frequência que transferi-los manualmente não é racional.
Vale destacar um ponto: esse acesso está acordado com o fornecedor e não viola as condições de uso do site. Ou seja, automatiza-se a obtenção dos dados que já se tem permissão para ver com a própria conta — apenas por programa, e não clique a clique. Somente nesses termos o scraping com autenticação é uma ferramenta de trabalho legítima, e não uma forma de burlar restrições.
Antes de escrever código, certifique-se sempre de que:
- o dono do recurso permite o acesso aos dados (contrato, consentimento por escrito, condições do programa de parceiros);
- a coleta automatizada não é proibida pelos termos de serviço (ToS) nem pelo arquivo
robots.txt; - a carga sobre o site se mantém razoável e não interfere no funcionamento dele;
- não são coletados nem tratados dados pessoais de terceiros sem uma base que o justifique.
Como funciona a autenticação: quatro mecanismos principais
Para escolher a abordagem de código, é preciso entender de que maneira o site autentica o usuário. Na prática, encontram-se quatro variantes principais.
1. Formulário de login e cookies de sessão. O caso mais frequente. O usuário e a senha são enviados em uma requisição POST ao endpoint de autenticação; o servidor responde definindo um cookie de sessão (por exemplo, sessionid ou PHPSESSID) e, a partir daí, esse cookie acompanha todas as requisições. A sessão dura enquanto o cookie continuar válido.
2. Token CSRF. Muitos formulários são protegidos por um token que se esconde no HTML da página de login (em um campo oculto ou em uma tag meta) ou que é entregue em um cookie à parte. Antes de enviar o formulário, é preciso carregar a página de login, extrair o token e mandá-lo junto com as credenciais. Sem ele, o servidor rejeitará a requisição.
3. Tokens (Bearer / JWT). Os sites modernos e as SPAs costumam autenticar o usuário por meio de uma API que devolve um token em JSON. Depois, o token é enviado no cabeçalho Authorization: Bearer <token>. Aqui, os cookies podem nem ser usados.
4. HTTP Basic Auth. A variante mais simples: o usuário e a senha são codificados em base64 e viajam no cabeçalho Authorization. Aparece em sistemas internos e em algumas APIs.
Uma dificuldade à parte são os sites que geram o conteúdo com JavaScript. Ali, um cliente HTTP comum não basta: é preciso um navegador «headless» (Playwright, Puppeteer, Selenium) que execute os scripts e entregue o DOM já construído.
Python: requests com sessão
requests.Session() conserva automaticamente os cookies entre as requisições — a base ideal para o scraping com autenticação. Exemplo com obtenção prévia do token CSRF:
import requests
from bs4 import BeautifulSoup
LOGIN_URL = "https://supplier.example.com/login"
PRICES_URL = "https://supplier.example.com/account/prices"
session = requests.Session()
session.headers.update({
"User-Agent": "Mozilla/5.0 (compatible; PriceMonitor/1.0)"
})
# 1. Carregamos a página de login e extraímos o token CSRF
login_page = session.get(LOGIN_URL, timeout=30)
soup = BeautifulSoup(login_page.text, "html.parser")
csrf_token = soup.select_one('input[name="csrf_token"]')["value"]
# 2. Enviamos o formulário de login
payload = {
"username": "your_login",
"password": "your_password",
"csrf_token": csrf_token,
}
resp = session.post(LOGIN_URL, data=payload, timeout=30)
resp.raise_for_status()
if "Minha conta" not in resp.text:
raise RuntimeError("Falha na autenticação: verifique as credenciais")
# 3. Sessão estabelecida: solicitamos a página privada de preços
prices_page = session.get(PRICES_URL, timeout=30)
soup = BeautifulSoup(prices_page.text, "html.parser")
for row in soup.select("table.prices tr"):
cells = row.select("td")
if len(cells) >= 2:
name = cells[0].get_text(strip=True)
price = cells[1].get_text(strip=True)
print(f"{name}: {price}")Boa prática é guardar o usuário e a senha fora do código — em variáveis de ambiente (os.environ) ou em um arquivo .env —, para não enviá-los por acidente ao repositório.
Python: autenticação por token (API)
Se o site autentica por meio de uma API JSON e devolve um token, o código é mais simples:
import requests
auth = requests.post(
"https://supplier.example.com/api/auth/login",
json={"login": "your_login", "password": "your_password"},
timeout=30,
)
auth.raise_for_status()
token = auth.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
data = requests.get(
"https://supplier.example.com/api/prices",
headers=headers,
timeout=30,
).json()
for item in data["items"]:
print(item["sku"], item["price"])Python: Playwright para sites feitos com JavaScript
Quando a área do cliente é uma SPA e os preços são carregados por scripts, a solução é um navegador headless. O Playwright sabe fazer login como um usuário real e até salvar o estado da sessão em um arquivo, para não precisar entrar de novo a cada execução.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context()
page = context.new_page()
# Login
page.goto("https://supplier.example.com/login")
page.fill("input[name='username']", "your_login")
page.fill("input[name='password']", "your_password")
page.click("button[type='submit']")
page.wait_for_url("**/account/**")
# Salvamos a sessão para reutilizá-la mais tarde
context.storage_state(path="auth_state.json")
# Vamos aos preços e esperamos os dados carregarem
page.goto("https://supplier.example.com/account/prices")
page.wait_for_selector("table.prices")
rows = page.query_selector_all("table.prices tr")
for row in rows:
cells = row.query_selector_all("td")
if len(cells) >= 2:
print(cells[0].inner_text(), "—", cells[1].inner_text())
browser.close()O auth_state.json salvo é conectado depois via browser.new_context(storage_state="auth_state.json") — e a etapa de login pode ser pulada enquanto a sessão não expirar.
Node.js: axios com armazenamento de cookies
No Node, para conservar os cookies entre as requisições, usa-se a combinação axios + tough-cookie + axios-cookiejar-support.
const axios = require("axios");
const { wrapper } = require("axios-cookiejar-support");
const { CookieJar } = require("tough-cookie");
const cheerio = require("cheerio");
const jar = new CookieJar();
const client = wrapper(axios.create({ jar, withCredentials: true }));
async function run() {
// 1. Obtemos o token CSRF da página de login
const loginPage = await client.get("https://supplier.example.com/login");
const $ = cheerio.load(loginPage.data);
const csrf = $('input[name="csrf_token"]').val();
// 2. Fazemos login
await client.post(
"https://supplier.example.com/login",
new URLSearchParams({
username: "your_login",
password: "your_password",
csrf_token: csrf,
}),
);
// 3. Solicitamos os preços
const pricesPage = await client.get(
"https://supplier.example.com/account/prices",
);
const $$ = cheerio.load(pricesPage.data);
$$("table.prices tr").each((_, el) => {
const cells = $$(el).find("td");
if (cells.length >= 2) {
const name = $$(cells[0]).text().trim();
const price = $$(cells[1]).text().trim();
console.log(`${name}: ${price}`);
}
});
}
run().catch(console.error);Node.js: Puppeteer para páginas dinâmicas
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto("https://supplier.example.com/login");
await page.type("input[name='username']", "your_login");
await page.type("input[name='password']", "your_password");
await Promise.all([
page.click("button[type='submit']"),
page.waitForNavigation(),
]);
await page.goto("https://supplier.example.com/account/prices");
await page.waitForSelector("table.prices");
const prices = await page.evaluate(() =>
Array.from(document.querySelectorAll("table.prices tr"))
.map((row) => {
const td = row.querySelectorAll("td");
return td.length >= 2
? { name: td[0].innerText.trim(), price: td[1].innerText.trim() }
: null;
})
.filter(Boolean),
);
console.log(prices);
await browser.close();
})();PHP: cURL com cookies de sessão
No PHP, os cookies são conservados entre as requisições em um arquivo, por meio das opções COOKIEJAR e COOKIEFILE.
<?php
$cookieFile = __DIR__ . "/cookies.txt";
function curlInit(string $cookieFile) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_COOKIEJAR => $cookieFile,
CURLOPT_COOKIEFILE => $cookieFile,
CURLOPT_USERAGENT => "Mozilla/5.0 (compatible; PriceMonitor/1.0)",
]);
return $ch;
}
// 1. Carregamos a página de login e extraímos o token CSRF
$ch = curlInit($cookieFile);
curl_setopt($ch, CURLOPT_URL, "https://supplier.example.com/login");
$html = curl_exec($ch);
preg_match('/name="csrf_token"\s+value="([^"]+)"/', $html, $m);
$csrf = $m[1] ?? "";
// 2. Enviamos o formulário de login
curl_setopt($ch, CURLOPT_URL, "https://supplier.example.com/login");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
"username" => "your_login",
"password" => "your_password",
"csrf_token" => $csrf,
]));
curl_exec($ch);
// 3. Solicitamos os preços
curl_setopt($ch, CURLOPT_URL, "https://supplier.example.com/account/prices");
curl_setopt($ch, CURLOPT_POST, false);
$pricesHtml = curl_exec($ch);
curl_close($ch);
// Parsing do HTML
$dom = new DOMDocument();
@$dom->loadHTML($pricesHtml);
$xpath = new DOMXPath($dom);
foreach ($xpath->query("//table[@class='prices']//tr") as $row) {
$cells = $row->getElementsByTagName("td");
if ($cells->length >= 2) {
echo trim($cells->item(0)->textContent) . ": "
. trim($cells->item(1)->textContent) . PHP_EOL;
}
}Go: net/http com cookiejar
A biblioteca padrão do Go inclui net/http/cookiejar, que gerencia os cookies de forma automática.
package main
import (
"fmt"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"github.com/PuerkitoBio/goquery"
)
func main() {
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
// 1. Obtemos o token CSRF
resp, _ := client.Get("https://supplier.example.com/login")
doc, _ := goquery.NewDocumentFromReader(resp.Body)
resp.Body.Close()
csrf, _ := doc.Find(`input[name="csrf_token"]`).Attr("value")
// 2. Fazemos login
form := url.Values{
"username": {"your_login"},
"password": {"your_password"},
"csrf_token": {csrf},
}
client.Post(
"https://supplier.example.com/login",
"application/x-www-form-urlencoded",
strings.NewReader(form.Encode()),
)
// 3. Extraímos os preços
pricesResp, _ := client.Get("https://supplier.example.com/account/prices")
pricesDoc, _ := goquery.NewDocumentFromReader(pricesResp.Body)
pricesResp.Body.Close()
pricesDoc.Find("table.prices tr").Each(func(_ int, s *goquery.Selection) {
cells := s.Find("td")
if cells.Length() >= 2 {
name := strings.TrimSpace(cells.Eq(0).Text())
price := strings.TrimSpace(cells.Eq(1).Text())
fmt.Printf("%s: %s\n", name, price)
}
})
}Recomendações práticas
Reutilize a sessão. Não faça login a cada requisição: é carga desnecessária e risco de bloqueio. Guarde o cookie ou o token e renove-os apenas quando a sessão expirar.
Trate a expiração da sessão. Cookies e tokens têm prazo de validade. Preveja uma verificação: se a requisição devolver um redirecionamento para o formulário de login ou um código 401, autentique-se de novo e repita a requisição.
Mantenha um ritmo razoável. Faça pausas entre as requisições (por exemplo, de 1 a 3 segundos) e não dispare dezenas de threads em paralelo. É uma cortesia com o servidor do fornecedor e reduz a probabilidade de cair na proteção anti-bots.
Guarde os segredos com segurança. Usuário, senha e tokens ficam em variáveis de ambiente ou em um cofre protegido, não no código e muito menos em um repositório público.
Seja resistente às mudanças de layout. Os sites mudam e os seletores quebram. Registre os erros de parsing e configure alertas para detectar rápido quando a estrutura da página mudou.
Use um User-Agent honesto e, se possível, inclua dados de contato. Se o fornecedor aprovou o acesso, um bot identificável simplifica o diagnóstico do lado dele quando algo dá errado.
Conclusão
Tecnicamente, o scraping com autenticação consiste em reproduzir os passos que o navegador dá ao fazer login: obter e enviar o formulário (com o token CSRF, se existir), guardar o cookie de sessão ou o token e anexá-lo às requisições seguintes. Para páginas estáticas, basta um cliente HTTP com suporte a sessões (requests, axios, cURL, net/http); para as dinâmicas, é preciso um navegador headless (Playwright, Puppeteer).
O essencial, porém, não está no código, e sim na base que legitima a coleta de dados. O scraping da área do cliente de um fornecedor para o monitoramento de preços é uma ferramenta de trabalho legítima exatamente quando o acesso está acordado com o dono do recurso e não viola as condições de uso. A tecnologia funciona igualmente bem nas duas direções, então a responsabilidade de aplicá-la corretamente continua sendo sua.