Tecnologias e proteções 10 min de leitura

Scraping de sites com autenticação: guia completo com exemplos de código

Scraping de sites com autenticação: cookies e sessões, tokens, CSRF e exemplos de como fazer login a partir do código em Python, Node.js, PHP e Go.

EW
Equipe Web-Scraping.biz
Coleta de dados para as demandas do negócio
Publicado: 19 abril 2025

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:

python
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:

python
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.

python
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.

No Node, para conservar os cookies entre as requisições, usa-se a combinação axios + tough-cookie + axios-cookiejar-support.

javascript
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

javascript
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();
})();

No PHP, os cookies são conservados entre as requisições em um arquivo, por meio das opções COOKIEJAR e COOKIEFILE.

php
<?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.

go
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.