---
title: "Sessões e cookies em Python: login com requests"
description: "requests.Session leva os cookies entre as requisições e mantém a sessão aberta. Mostramos o token CSRF do formulário, a verificação do login e o armazenamento."
url: https://proxynet.io/pt-br/blog/python-login-session-cookies
date: 2026-09-19
author: "Acar Diveroli"
category: "Tutoriais, Web scraping"
lang: pt-BR
---

# Sessões e cookies em Python: login com requests

Toda manhã você entra na mão no painel da sua própria empresa e baixa um relatório. O painel tem botão de exportação, não tem API oficial e os mesmos cinco cliques se repetem todo dia. Na hora de passar isso para um script Python, a primeira parede é a página de login: o que `requests.get()` baixa não é o relatório, é o formulário de login. O HTTP é um protocolo sem estado e o servidor só reconhece você por um cookie que você anexa a cada requisição.

Neste artigo explicamos o que são cookies e sessões, por que o token CSRF é lido de novo a cada execução e como se monta um fluxo de login com `requests.Session`. Depois tratamos de gravar a sessão em disco, renová-la quando expira, tirar as credenciais do código e ficar no mesmo IP de saída durante toda a sessão. No fim vem o método `storage_state` do Playwright, para sites que enviam o formulário com JavaScript. Os exemplos de código foram testados contra `quotes.toscrape.com`.

> **Nota: Resposta rápida**
>
> Um objeto `requests.Session` carrega o próprio pote de cookies: guarda o cookie de sessão que chega quando você faz `GET` no formulário de login, atualiza com o cookie novo da resposta ao seu `POST` e anexa sozinho a cada requisição seguinte. O fluxo tem quatro passos: pedir o formulário, ler o token CSRF que está nele, enviá-lo junto com usuário e senha e confirmar pelo conteúdo da página que o login realmente aconteceu. Tudo isso vale apenas para **a sua própria conta** ou para uma conta cujo titular deu permissão por escrito.

## A quais logins este artigo se aplica?

Tudo aqui se apoia em uma única premissa: a conta em que você entra é sua, ou o titular dela deu permissão por escrito. O painel administrativo da sua própria empresa, a sua própria conta de loja, um sistema de cliente que pediu por escrito "puxem esse relatório para nós todo dia". O que estiver fora disso não é assunto deste artigo.

Três perguntas antes do código:

1. **Existe uma API oficial ou uma exportação?** Se existe, não automatize a interface. Uma chave de API é estável e não quebra quando a interface muda; automatizar o login é o caminho para quando não há outro jeito de chegar ao dado. O lado jurídico tratamos em [Web scraping é legal? Um panorama](/pt-br/blog/is-data-web-scraping-legal).
2. **O que dizem os termos de uso do site?** Se alguma cláusula proíbe acesso automatizado, sua conta pode ser suspensa. Ser tecnicamente possível não significa ser permitido.
3. **A sua permissão está por escrito?** Em trabalho para cliente, aprovação verbal não basta. Com qual conta, de quais páginas e com que frequência você vai extrair dados precisa estar em contrato ou em e-mail.

Quatro coisas não são explicadas aqui de propósito:

- **Nada de testar senhas.** Código que joga uma lista no campo do formulário (credential stuffing) ou que gera combinações (força bruta) não aparece aqui. Não faça isso: o acesso não autorizado à conta de outra pessoa é crime em qualquer país, e a possibilidade técnica não muda nada.
- **Nada de contornar verificação em duas etapas ou CAPTCHA.** Esses controles existem para proteger a conta; tentar passar por eles com um script reduz a segurança justamente da conta com que você trabalha. Se a sua conta tem 2FA ativo, o caminho certo é a chave de API ou a senha de aplicativo do fornecedor; se não existe nenhum dos dois, esse trabalho não vai ser automatizado.
- **Nada de conta alheia.** "A conta de um amigo", "a conta de quem saiu da empresa" e credenciais compartilhadas na internet entram nessa lista.
- **Nada de roubar ou transportar cookie de sessão.** Uma sessão aberta com um cookie copiado do navegador de outra pessoa é falsidade de identidade. Os arquivos de cookie deste artigo são gerados **apenas com o seu próprio login** e ficam apenas na sua máquina.

Uma distinção ainda é necessária por causa da semelhança dos nomes: autenticar-se em um servidor proxy (`user:pass` ou autorização por IP) e fazer login no site de destino são dois trabalhos diferentes. O primeiro está em [Autenticação de proxy: user:pass ou whitelist de IP](/pt-br/blog/proxy-authentication-methods); aqui falamos do segundo.

## O que são cookies e sessões?

O HTTP não guarda estado: o servidor não lembra de nada que ligue duas requisições. Os cookies preenchem essa lacuna. O servidor coloca um cabeçalho `Set-Cookie` na resposta, o cliente guarda o valor e o devolve com um cabeçalho `Cookie` nas requisições seguintes ao mesmo domínio. Todos os atributos do cabeçalho estão explicados um a um na [página Set-Cookie do MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie).

"Sessão" é o que dá sentido a esse cookie. Há dois desenhos comuns:

- **Sessão do lado do servidor.** No cookie viaja apenas um identificador aleatório (`sessionid`, `PHPSESSID`, `JSESSIONID`); quem é o usuário fica em um registro no servidor. Se o servidor apaga esse registro, a sessão acaba mesmo com o cookie ainda na sua mão.
- **Cookie assinado.** A informação do usuário está dentro do cookie e o servidor a assina com uma chave secreta. O cookie `session` padrão do Flask funciona assim.

Quanto tempo um cookie vive é definido por `Expires` e `Max-Age`. [A seção 4.1.2.2 da RFC 6265](https://www.rfc-editor.org/rfc/rfc6265#section-4.1.2.2) diz que, se um cookie não tem nem `Max-Age` nem `Expires`, o cliente o mantém até "a sessão atual terminar". No navegador isso significa "até você fechar o navegador"; em um script Python, "enquanto o processo viver". Então não estranhe se o seu script faz login de novo a cada execução: o cookie que ele guardou nunca foi persistente.

A quais requisições um cookie é anexado é decidido por estes atributos do cabeçalho `Set-Cookie`:

| Atributo | O que faz | O que significa no script |
|---|---|---|
| `Domain` | Para qual domínio o cookie vai | Um cookie de `painel.exemplo.com` não é anexado a uma requisição para `exemplo.com` |
| `Path` | Sob qual caminho ele vale | Um cookie em `/relatorio` não é enviado em uma requisição para `/` |
| `Secure` | Enviado apenas por HTTPS | Uma requisição que cai para HTTP não leva o cookie |
| `HttpOnly` | O JavaScript da página não consegue ler | Você não o vê com `document.cookie` no console do navegador; um cliente HTTP vê |
| `SameSite` | Se é anexado a requisições vindas de outro site | Com `Strict`, a primeira requisição vinda de um link externo parece sem sessão |

Essa tabela não é teoria, é uma lista de diagnóstico: a maior parte dos relatos do tipo "o login parece ter dado certo, mas a próxima requisição cai de novo na página de login" vem de divergência de `Domain` ou de `Path`.

## O que é o token CSRF e por que ele é lido toda vez?

Enquanto a sua sessão está aberta em uma página de banco, um formulário escondido em outro site pode enviar uma requisição em seu nome. Como o navegador anexa o cookie automaticamente, o servidor entende aquilo como requisição sua. Isso se chama falsificação de requisição entre sites (CSRF).

A defesa mais comum é o método que [a página de prevenção de CSRF da OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html) chama de "token sincronizador": o servidor embute em cada página de formulário um valor aleatório ligado àquela sessão, como campo oculto. Ao enviar o formulário, esse valor precisa bater com o correspondente no cookie de sessão. Um formulário em outro site não tem como conhecer o token da sua sessão, então a verificação falha.

Para quem escreve o script, isso tem três consequências:

- **O token não é fixo.** Se você copiar do navegador uma vez e deixar no código, no dia seguinte não funciona. A cada execução é preciso baixar a página de login e ler o campo de lá.
- **O token está preso à sessão.** Enviar o token em uma requisição e o formulário por outra conexão não adianta. Os dois têm que passar pelo mesmo objeto `Session`; essa é justamente a primeira razão para usar uma `Session`.
- **O nome do campo muda conforme o site.** `csrf_token`, `csrfmiddlewaretoken` (Django), `authenticity_token` (Rails) e `_token` (Laravel) fazem o mesmo trabalho. Olhe o código-fonte do formulário antes de escrever código.

Em aplicações que esperam o token em um cabeçalho (`X-CSRF-Token`) em vez de campo oculto, o valor continua saindo da página, mas vai para o dicionário `headers` e não para o corpo.

## Qual caminho escolher?

Para um trabalho atrás de um login há três caminhos, e o formulário do site decide qual deles.

| Caminho | Quando | Custo | Ponto fraco |
|---|---|---|---|
| `requests.Session` | O formulário é HTML puro e os campos aparecem na página | O mais baixo, sem navegador | Não enxerga campos gerados por JavaScript |
| `storage_state` do Playwright | O formulário é enviado por JavaScript ou o token é gerado por script | Alto, abre um navegador real | Memória e tempo, custo de instalação |
| Híbrido: entrar com o navegador e seguir por HTTP | O login é complexo, mas as páginas de dados são HTML puro | Navegador uma vez, depois barato | Os cookies passam entre dois ambientes, o IP precisa ser o mesmo |

Faça a escolha olhando o código-fonte da página, não no chute: se dentro de um `<form method="post">` você vê campos `input` com `name`, `requests` basta. Se os campos estão vazios ou o formulário é enviado com `fetch()`, vá para o segundo ou o terceiro caminho.

## Como fazer login com requests.Session?

São cinco passos e a ordem importa: os quatro primeiros são o login em si, o quinto é para as execuções seguintes.

1. **Baixe a página de login com `GET`.** Essa resposta traz duas coisas: um cookie de sessão ainda anônimo e o token CSRF do formulário. A `Session` guarda o cookie no próprio pote.
2. **Leia de fato os campos do formulário.** Preencha o formulário no navegador, envie e olhe o corpo da requisição na aba Rede das ferramentas de desenvolvedor. Nomes de campo não se adivinham, se copiam de lá; se houver um campo oculto `next` ou `redirect`, envie também.
3. **Mande o `POST` com a mesma `Session`.** O cookie é anexado sozinho. Se o atributo `action` do formulário aponta para outro caminho, envie a requisição para lá.
4. **Verifique o login pelo conteúdo.** O código de status engana: muitas aplicações devolvem `200` também com senha errada e reexibem o formulário com uma mensagem de erro. O critério é um elemento que só aparece com a sessão aberta: um link de saída, o nome de usuário, um menu de conta.
5. **Guarde os cookies.** A execução seguinte pula o passo do login.

Segundo [a seção Session Objects da documentação do Requests](https://requests.readthedocs.io/en/latest/user/advanced/), além de manter os cookies durante a vida da instância, uma `Session` também reaproveita a conexão TCP para requisições ao mesmo host. O padrão é igual nas três bibliotecas do [Comparativo entre HTTPX, Requests e AIOHTTP](/pt-br/blog/httpx-vs-requests-vs-aiohttp); só muda o nome da classe.

## Um exemplo que roda: quotes.toscrape.com

O código abaixo foi testado contra `quotes.toscrape.com/login`. É um site de treino aberto, montado para praticar web scraping, e **aceita qualquer nome de usuário e qualquer senha que você digitar**; ele não faz autenticação nenhuma, só imita o fluxo. Ver o mecanismo lá primeiro evita que você produza uma sequência de logins falhos na sua própria conta.

Primeiro o fluxo central, em quatro passos:

```python
import os

import requests
from bs4 import BeautifulSoup

BASE_URL = "https://quotes.toscrape.com"

session = requests.Session()

# 1) Peca o formulario de login: o cookie de sessao e o token CSRF vem nesta resposta
page = session.get(f"{BASE_URL}/login", timeout=20)
page.raise_for_status()
token = BeautifulSoup(page.text, "html.parser").select_one('input[name="csrf_token"]')["value"]

# 2) As credenciais nao ficam no codigo, sao lidas de variaveis de ambiente
payload = {
    "csrf_token": token,
    "username": os.environ["SITE_USERNAME"],
    "password": os.environ["SITE_PASSWORD"],
}

# 3) Envie o formulario com a mesma Session; o cookie a propria Session anexa
result = session.post(f"{BASE_URL}/login", data=payload, timeout=20)
result.raise_for_status()

# 4) Verifique pelo conteudo da pagina, nao pelo codigo de status
if BeautifulSoup(result.text, "html.parser").select_one('a[href="/logout"]') is None:
    raise SystemExit("Nao foi possivel verificar o login")

print("Login feito, cookies:", list(session.cookies.keys()))
```

Coloque as credenciais no ambiente antes de rodar; escrever a senha no código é escrevê-la também no histórico de versões:

```bash
export SITE_USERNAME="usuario"
export SITE_PASSWORD="senha"
python login.py
```

No Windows PowerShell a forma é `$env:SITE_USERNAME = "usuario"`. Como deixar variáveis de ambiente permanentes para ferramentas de linha de comando está em [Uso de proxy com o wget: comandos e exemplos](/pt-br/blog/wget-proxy).

## Como gravar a sessão em disco e quando renová-la?

Fazer login a cada execução enche o log de segurança da sua conta com registros inúteis; em algumas aplicações, logins repetidos em pouco tempo ainda disparam uma verificação extra. A solução é gravar o pote de cookies em um arquivo e recarregá-lo na execução seguinte.

O script completo abaixo faz isso: carrega os cookies salvos se existirem, faz login se não existirem, verifica a cada requisição se a sessão continua aberta e, se ela caiu, faz login de novo **uma única vez**. Ser uma única vez importa: um laço sem condição vira uma sequência infinita de tentativas de login quando o problema está na credencial.

```python
import json
import os
import time
from pathlib import Path

import requests
from bs4 import BeautifulSoup

BASE_URL = "https://quotes.toscrape.com"
COOKIE_FILE = Path("session_cookies.json")
DELAY_SECONDS = 2.0

class LoginError(Exception):
    """O login nao foi concluido; veja a causa em vez de tentar de novo."""

def build_session():
    session = requests.Session()
    session.headers["User-Agent"] = "script-relatorio/1.0 (contato: voce@exemplo.com)"
    proxy_url = os.environ.get("PROXY_URL")  # ex.: http://user:pass@pr.proxynet.io:8000
    if proxy_url:
        session.proxies = {"http": proxy_url, "https": proxy_url}
    return session

def save_cookies(session, path=COOKIE_FILE):
    cookies = [
        {"name": c.name, "value": c.value, "domain": c.domain, "path": c.path,
         "expires": c.expires, "secure": c.secure}
        for c in session.cookies
    ]
    path.write_text(json.dumps(cookies), encoding="utf-8")
    try:
        path.chmod(0o600)  # o arquivo e tao sensivel quanto uma senha; no Windows o efeito e limitado
    except OSError:
        pass

def load_cookies(session, path=COOKIE_FILE):
    if not path.exists():
        return False
    try:
        cookies = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        return False
    now = time.time()
    for c in cookies:
        if c["expires"] and c["expires"] < now:
            continue  # nunca carregue um cookie expirado
        session.cookies.set(c["name"], c["value"], domain=c["domain"], path=c["path"],
                            expires=c["expires"], secure=c["secure"])
    return True

def is_logged_in(html):
    return BeautifulSoup(html, "html.parser").select_one('a[href="/logout"]') is not None

def login(session):
    page = session.get(f"{BASE_URL}/login", timeout=20)
    page.raise_for_status()
    field = BeautifulSoup(page.text, "html.parser").select_one('input[name="csrf_token"]')
    if field is None:
        raise LoginError("Sem campo csrf_token no formulario; a pagina pode ter mudado")
    payload = {
        "csrf_token": field["value"],
        "username": os.environ["SITE_USERNAME"],
        "password": os.environ["SITE_PASSWORD"],
    }
    result = session.post(f"{BASE_URL}/login", data=payload, timeout=20)
    result.raise_for_status()
    if not is_logged_in(result.text):
        raise LoginError("Login nao verificado; confira as credenciais e os campos do formulario")
    save_cookies(session)

def get_page(session, path):
    """Busca a pagina; se a sessao caiu, faz login de novo uma unica vez."""
    for attempt in range(2):
        response = session.get(f"{BASE_URL}{path}", timeout=20)
        response.raise_for_status()
        if is_logged_in(response.text):
            return response
        if attempt == 0:
            print("Sessao invalida, fazendo login de novo")
            session.cookies.clear()
            login(session)
    raise LoginError("Sem sessao mesmo apos a segunda tentativa; pare e veja a causa")

def main():
    session = build_session()
    if load_cookies(session):
        print("Cookies salvos carregados")
    else:
        print("Sem sessao salva, fazendo login")
        login(session)

    for number in range(1, 4):
        response = get_page(session, f"/page/{number}/")
        quotes = BeautifulSoup(response.text, "html.parser").select("div.quote")
        print(f"Pagina {number}: {len(quotes)} registros")
        time.sleep(DELAY_SECONDS)

if __name__ == "__main__":
    main()
```

A primeira execução diz "Sem sessão salva" e faz login; a segunda diz "Cookies salvos carregados" e vai direto aos dados. Três pontos merecem atenção:

- **O arquivo de cookies é um segredo.** O valor que está nele substitui a senha durante aquela sessão. Coloque o arquivo no `.gitignore` e mantenha fora de backups e pastas compartilhadas; a documentação do Playwright faz o mesmo alerta para o arquivo de estado dele.
- **Não carregue cookie expirado.** `load_cookies` descarta esses olhando o campo `expires`; sem isso o script começa com uma sessão inválida.
- **Descubra pelo conteúdo que a sessão caiu.** Algumas aplicações redirecionam para a página de login com `302`, outras devolvem o formulário com `200`. Um critério único como `is_logged_in` pega os dois casos.

## Por que é preciso o mesmo IP durante a sessão?

Parte das aplicações prende a sessão aberta ao endereço IP de onde veio o login, ou à rede desse endereço. Se a próxima requisição chega de outro endereço, a sessão é encerrada, o usuário é levado à página de login ou uma verificação extra é pedida. Isso é uma decisão de segurança, e o objetivo é dificultar o uso de um cookie de sessão roubado em outro lugar.

Em um script que usa proxy, isso significa uma coisa: **um pool rotativo quebra a sessão em trabalhos que exigem login.** Um proxy rotativo troca o IP de saída a cada requisição ou em intervalos curtos; o endereço de onde você fez login e o endereço de onde baixa o relatório acabam diferentes, e a aplicação não reconhece você. Como a rotação funciona e para quais trabalhos ela é a escolha certa está em [O que é rotação de IP e como ela funciona?](/pt-br/blog/ip-rotation-explained).

A montagem correta é uma de duas opções:

- Com [Proxies de sessão fixa](https://proxynet.io/pt-br/sticky-proxy) você fica no mesmo IP de saída pelo tempo que definir, entre 1 e 60 minutos. Se a sessão é curta e você solta a conexão ao terminar o trabalho, isso basta.
- Com [Proxies ISP](https://proxynet.io/pt-br/static-isp-residential-proxy) o endereço continua o mesmo também de uma execução para outra. Em sistemas que limitam o acesso ao painel a IPs específicos esse é o único caminho, porque você informa o endereço uma vez ao outro lado e pede a inclusão na lista.

O caso de colocar um endereço na lista de permissões de uma API está em [IP estático para API: como resolver o erro de autorização](/pt-br/blog/static-ip-for-api-access), e a justificativa geral para um IP fixo está na seção "IP sticky onde a sessão é necessária" de [Como fazer web scraping sem ser bloqueado](/pt-br/blog/web-scraping-without-getting-blocked).

Vamos evitar uma leitura errada: um IP fixo não é ferramenta para contornar controle de segurança. O que ele faz é manter o seu próprio tráfego coerente em uma conta para a qual você já tem autorização. Se a conta não é sua, um IP fixo também não produz acesso legítimo.

## Limite de requisições: manter o script educado

Um script logado é mais visível que um anônimo: as suas requisições agora são registradas não contra um endereço IP, mas diretamente contra a sua conta. Respeitar o limite de requisições aqui não é preferência técnica, é parte de proteger a sua conta.

- **Comece com uma única thread.** Para um relatório por dia, não monte paralelismo.
- **Coloque uma espera entre as requisições.** `DELAY_SECONDS` no script acima é a forma mais simples disso.
- **Pare quando vir um `429`.** O cabeçalho `Retry-After` da resposta diz quanto esperar. O código completo está em [429 Too Many Requests: o que é o erro de rate limit](/pt-br/blog/http-429-too-many-requests) e em [Códigos de status HTTP no web scraping: 403, 407, 429, 503](/pt-br/blog/http-status-codes-web-scraping).
- **Apresente-se.** Escrever o nome do script e um endereço de contato no campo `User-Agent` permite que quem administra o site fale com você; para que serve o campo está em [O que é User-Agent? Como ver e como alterar](/pt-br/blog/what-is-user-agent).

## Se o login roda em JavaScript: storage_state do Playwright

Em algumas aplicações o formulário de login não manda um `POST` clássico: o JavaScript lê os campos, o navegador gera o token e a resposta vem por uma chamada de API. Em uma página assim, o formulário enviado com `requests` falha em silêncio. É nesse ponto que um navegador de verdade é necessário.

O Playwright consegue gravar o estado da sessão em um único arquivo JSON. Segundo [a documentação de autenticação do Playwright](https://playwright.dev/python/docs/auth), esse arquivo leva juntos os cookies e o `localStorage`, então também serve em aplicações que guardam a sessão no `localStorage` em vez de um cookie. Se a aplicação guarda o token no `IndexedDB`, isso precisa ser pedido à parte: `context.storage_state(path=..., indexed_db=True)`.

```python
import os
from pathlib import Path

from playwright.sync_api import sync_playwright

BASE_URL = "https://quotes.toscrape.com"
STATE_FILE = Path("storage_state.json")
PROXY = {"server": "http://pr.proxynet.io:8000", "username": "user", "password": "pass"}

with sync_playwright() as p:
    browser = p.chromium.launch(proxy=PROXY)
    if STATE_FILE.exists():
        context = browser.new_context(storage_state=STATE_FILE)  # abre com a sessao salva
    else:
        context = browser.new_context()
    page = context.new_page()
    page.goto(BASE_URL)

    if page.locator('a[href="/logout"]').count() == 0:
        page.goto(f"{BASE_URL}/login")
        page.fill("#username", os.environ["SITE_USERNAME"])
        page.fill("#password", os.environ["SITE_PASSWORD"])
        page.click('input[type="submit"]')
        page.wait_for_selector('a[href="/logout"]')  # prova do login
        context.storage_state(path=STATE_FILE)  # cookies e localStorage em um arquivo so
        print("Login feito, estado salvo")
    else:
        print("Sessao aberta com o estado salvo")

    browser.close()
```

A primeira execução faz login e grava o arquivo; a segunda nem vê o passo do login. A configuração de proxy do Playwright e a instalação do navegador estão detalhadas em [O que é Playwright e como usá-lo com proxy](/pt-br/blog/playwright-proxy); para o equivalente no Selenium, veja [Selenium com proxy: configuração em Python e Java](/pt-br/blog/selenium).

Daí sai o padrão híbrido: você entra uma vez com o navegador e gera `storage_state.json`, depois passa os cookies para uma `requests.Session` e busca os dados pelo caminho barato.

```python
import json
from pathlib import Path

import requests

state = json.loads(Path("storage_state.json").read_text(encoding="utf-8"))
session = requests.Session()
for c in state["cookies"]:
    session.cookies.set(c["name"], c["value"], domain=c["domain"], path=c["path"])
```

Esse padrão tem uma condição só: tanto o navegador quanto o cliente HTTP que vem depois precisam sair pelo mesmo IP. Se você der proxies diferentes aos dois, a sessão cai na primeira requisição. Qual automação de navegador combina com qual trabalho comparamos em [Playwright ou Selenium: qual escolher?](/pt-br/blog/playwright-vs-selenium).

## Casos de uso

- **Baixar o relatório diário do próprio painel.** O endereço atrás do botão de exportação costuma ser um link direto para um arquivo; enquanto o cookie de sessão durar, `requests` baixa. A montagem geral está na nossa página de [extração de dados](/pt-br/data-scraping).
- **Monitorar um fluxo que fica atrás do login.** Para conferir os passos de login e de carrinho da sua própria aplicação depois de cada deploy; a montagem está na nossa página de [testes de aplicativos](/pt-br/app-testing).
- **Transferir dados de um sistema de cliente.** Com permissão por escrito e um IP de saída fixo, para que o outro lado possa incluir esse endereço na lista dele.
- **Coletar dados de uma lista com várias páginas.** Depois do login é um trabalho de paginação comum; o padrão está em [O que é paginação e como percorrê-la em web scraping](/pt-br/blog/pagination-web-scraping).

## Erros comuns e diagnóstico

- **Redirecionamento infinito para a página de login.** Se a requisição volta a `/login` com `302`, o cookie ou não está sendo enviado ou é inválido no servidor. Olhe primeiro o conteúdo de `session.cookies` e depois compare o `Domain` do cookie com o endereço chamado.
- **Usar `requests.get` em vez de uma `Session`.** O `requests.get()` no nível do módulo abre uma conexão nova e um pote de cookies vazio a cada chamada. Em um fluxo de login isso não funciona.
- **Verificar o login pelo código de status.** Com senha errada também pode vir `200`. Procure uma prova no conteúdo.
- **Deixar o token CSRF escrito no código.** Funciona um dia e no outro dá erro de "token inválido".
- **Trocar de IP no meio da sessão.** Fazer login com um pool rotativo e depois extrair dados é a causa mais frequente de queda de sessão.
- **Entrar em laço depois de um login falho.** Se a senha está errada, tentar de novo não resolve; em alguns sistemas bloqueia a conta. O script deve parar na primeira falha.

## Guia de decisão

| Situação | O que fazer |
|---|---|
| O site tem API oficial | Nem entre na automação de login, use uma chave de API |
| Formulário HTML puro, campos na página | Faça login com `requests.Session` e salve os cookies em arquivo |
| O formulário tem campo oculto tipo `csrf_token` | Leia da página a cada execução, não deixe no código |
| O formulário é enviado por JavaScript | Faça login com o Playwright e guarde o arquivo `storage_state` |
| Login complexo, páginas de dados simples | Híbrido: entre com o navegador e passe os cookies para `requests` |
| A sessão cai no meio do trabalho | Fixe o IP de saída: [Proxies de sessão fixa](https://proxynet.io/pt-br/sticky-proxy) |
| O painel só aceita IPs específicos | Endereço fixo com [Proxies ISP](https://proxynet.io/pt-br/static-isp-residential-proxy) |
| A conta tem verificação em duas etapas | Peça uma chave de API ou senha de aplicativo, não tente passar pelo controle |
| A conta não é sua | Pare; obtenha permissão por escrito do titular |

## Perguntas frequentes

### Qual é a diferença entre sessão e cookie?

O cookie é um pedaço pequeno de dados que o servidor guarda no seu navegador ou cliente e que volta em cada requisição. A sessão é o estado para o qual esse cookie aponta: quem você é, desde quando está logado, quais permissões tem. No lado do Python, `requests.Session` é o objeto que une os dois; guarda os cookies e os carrega entre as requisições.

### Fiz login, mas a próxima requisição cai de novo na página de login, por quê?

Há três causas comuns. A primeira: você mandou a requisição de login fora da `Session` e o cookie se perdeu. A segunda: o `Domain` ou o `Path` do cookie não bate com o endereço chamado. A terceira: a aplicação prendeu a sessão a um IP e, por causa de um proxy rotativo, a segunda requisição saiu de outro endereço.

### Posso pegar o token CSRF uma vez e guardar?

Não. O token está preso à sessão e muda quando a sessão é renovada. A cada execução é preciso baixar a página de login e ler o campo de lá. O que se guarda não é o token, é o cookie de sessão que chega depois do login.

### Extrair dados de um site protegido por senha é crime?

O que decide não é o site pedir senha, e sim se você tem autorização de acesso àquela conta. Puxar os seus próprios dados da sua própria conta é uso comum; entrar com credencial de outra pessoa é acesso não autorizado e é crime em qualquer país. Se os termos de uso proíbem acesso automatizado, você descumpre o contrato mesmo na sua própria conta. O detalhe está em [Web scraping é legal? Um panorama](/pt-br/blog/is-data-web-scraping-legal).

### Por quanto tempo um cookie de sessão vale?

Depende da aplicação. Um cookie sem `Max-Age` nem `Expires` acaba quando o cliente fecha. Os que têm prazo podem viver de algumas horas a algumas semanas, mas quando o servidor apaga o registro do lado dele, a sessão acaba mesmo com o cookie ainda na sua mão. Por isso um script verifica "a sessão continua aberta?" em vez de "já expirou?".

### Posso trocar de proxy depois de fazer login?

Melhor não. Se o IP que abriu a sessão e o IP de onde saem as requisições seguintes são diferentes, a aplicação pode encerrar a sessão ou pedir verificação extra. Se você usa navegador e cliente HTTP no mesmo trabalho, dê o mesmo proxy para os dois.

## Em resumo

O núcleo técnico de um trabalho atrás de um login é pequeno: `requests.Session` carrega os cookies, o token CSRF lido da página de login entra no formulário, o resultado é verificado pelo conteúdo da página e não pelo código de status, e a sessão é gravada em arquivo e passada para a execução seguinte. Em sites que enviam o formulário por JavaScript, o arquivo `storage_state` do Playwright faz o mesmo serviço. Para a sessão não cair, o IP de saída não pode mudar enquanto ela durar; para isso existem [Proxies de sessão fixa](https://proxynet.io/pt-br/sticky-proxy) ou um endereço fixo. A pré-condição não muda: a conta precisa ser sua ou é preciso ter a permissão por escrito do titular, e se existir uma API oficial, ela vem primeiro. Os tipos de proxy adequados estão nos nossos [serviços de proxy](/pt-br/proxy).
