urllib.request no Python: HTTP nativo

Publicado em: 19/08/2026
Tempo de leitura: 5 minutos
Teclas formando HTTP representando requisições com urllib.request no Python

O módulo urllib.request permite abrir URLs e fazer requisições HTTP usando apenas a biblioteca padrão. Ele oferece urlopen(), objetos Request e uma arquitetura extensível de handlers para redirects, autenticação, cookies, proxies e HTTPS.

Embora bibliotecas como Requests ou HTTPX sejam mais convenientes em aplicações grandes, urllib.request é útil em scripts portáveis, instaladores, ferramentas administrativas e ambientes onde dependências externas não são desejadas. Este guia mostra GET, POST, JSON, downloads limitados, TLS, redirects, proxies, erros e proteção contra URLs não confiáveis.

Primeira requisição GET

from urllib.request import urlopen

with urlopen("https://www.python.org/", timeout=10) as response:
    print(response.status)
    print(response.headers.get_content_type())
    data = response.read(4096)

A resposta funciona como context manager e expõe status, headers e url. O corpo é retornado como bytes. Você precisa determinar o encoding antes de transformá-lo em texto.

charset = response.headers.get_content_charset() or "utf-8"
text = data.decode(charset, errors="replace")

O guia de codecs no Python explica por que bytes e texto devem ser tratados separadamente.

Use sempre timeout

Sem timeout, uma conexão pode bloquear o programa por muito tempo. O parâmetro cobre operações bloqueantes de conexão e leitura de protocolos suportados.

with urlopen(url, timeout=10) as response:
    body = response.read(1_000_000)

O timeout não substitui um limite de tamanho. Um servidor pode responder continuamente dentro do prazo. Leia em blocos e interrompa quando o total ultrapassar o máximo permitido.

Criando um objeto Request

Request permite definir método e headers.

from urllib.request import Request, urlopen

request = Request(
    "https://api.example.com/items",
    headers={
        "Accept": "application/json",
        "User-Agent": "MinhaFerramenta/1.0",
    },
    method="GET",
)

with urlopen(request, timeout=10) as response:
    body = response.read(500_000)

Identifique o cliente honestamente. Não copie um User-Agent de navegador para contornar regras de acesso. Respeite termos, limites e o arquivo robots quando aplicável.

Query strings corretas

Use urllib.parse.urlencode() em vez de concatenar parâmetros manualmente.

from urllib.parse import urlencode

params = urlencode({"q": "python seguro", "page": 2})
url = f"https://example.com/search?{params}"

O artigo sobre urllib.parse no Python cobre quoting, parsing e validação de URLs.

POST com formulário

from urllib.parse import urlencode
from urllib.request import Request, urlopen

form = urlencode({"name": "Ana", "active": "1"}).encode("ascii")
request = Request(
    "https://example.com/form",
    data=form,
    headers={"Content-Type": "application/x-www-form-urlencoded"},
    method="POST",
)

with urlopen(request, timeout=10) as response:
    result = response.read(100_000)

Quando data é fornecido sem método explícito, o padrão passa a ser POST. Declarar o método deixa a intenção clara.

Enviando e recebendo JSON

import json
from urllib.request import Request, urlopen

payload = json.dumps({"title": "Exemplo"}).encode("utf-8")
request = Request(
    "https://api.example.com/items",
    data=payload,
    headers={
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
    method="POST",
)

with urlopen(request, timeout=10) as response:
    raw = response.read(500_000)
    result = json.loads(raw.decode("utf-8"))

Valide o Content-Type antes de assumir JSON e limite a resposta. O guia de APIs REST no Python apresenta validação de status, retries e autenticação.

Tratamento de HTTPError e URLError

from urllib.error import HTTPError, URLError

try:
    with urlopen(request, timeout=10) as response:
        body = response.read(100_000)
except HTTPError as error:
    error_body = error.read(20_000)
    print(error.code, error.reason)
except URLError as error:
    print(f"Falha de rede: {error.reason}")
except TimeoutError:
    print("Tempo esgotado")

HTTPError representa uma resposta HTTP com erro e também funciona como objeto de resposta, permitindo ler um corpo limitado. URLError cobre falhas de resolução, conexão, TLS e protocolos.

Downloads com limite

Evite response.read() sem limite para arquivos externos. Verifique o cabeçalho e conte os bytes realmente lidos.

from pathlib import Path

MAX_BYTES = 50 * 1024 * 1024

with urlopen(url, timeout=20) as response:
    declared = response.headers.get("Content-Length")
    if declared and int(declared) > MAX_BYTES:
        raise ValueError("Arquivo declarado é muito grande")

    total = 0
    with Path("download.tmp").open("wb") as output:
        while chunk := response.read(64 * 1024):
            total += len(chunk)
            if total > MAX_BYTES:
                raise ValueError("Download ultrapassou o limite")
            output.write(chunk)

Grave em arquivo temporário, valide hash ou formato e mova atomicamente para o destino. Veja o guia de hashlib no Python para verificar SHA-256.

Conteúdo comprimido

urllib.request não descomprime automaticamente toda resposta. Ao solicitar gzip, valide o header e limite também o tamanho descomprimido.

import gzip

encoding = response.headers.get("Content-Encoding", "").lower()
raw = response.read(2_000_000)

if encoding == "gzip":
    data = gzip.decompress(raw)
else:
    data = raw

Para dados não confiáveis e grandes, prefira descompressão incremental com limite. O artigo de gzip no Python detalha proteção contra expansão excessiva.

TLS e certificados

HTTPS usa validação segura por padrão. Para uma CA privada, forneça um SSLContext confiável.

import ssl

context = ssl.create_default_context(cafile="empresa-ca.pem")
with urlopen(request, timeout=10, context=context) as response:
    body = response.read(100_000)

Não desative hostname nem validação de certificado. Corrija a cadeia de confiança. O guia de ssl no Python explica os riscos de CERT_NONE.

Redirects e headers sensíveis

O handler padrão segue redirects HTTP. Analise o URL final em response.url. Credenciais e headers sensíveis não devem ser encaminhados para domínios inesperados. Use add_unredirected_header() para um header que não pode acompanhar redirects ou implemente um HTTPRedirectHandler restritivo.

Redirects 301 e 302 podem transformar POST em GET, imitando navegadores. Os códigos 307 e 308 preservam o método. Quando a operação tem efeito, revise explicitamente essa política.

Desabilitando redirects automáticos

from urllib.request import build_opener, HTTPRedirectHandler

class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

opener = build_opener(NoRedirect())

Ao permitir redirects, imponha quantidade máxima e valide esquema, hostname e porta de cada destino. Isso é essencial quando parte do URL vem do usuário.

Proxies do ambiente

O opener padrão pode ler http_proxy, https_proxy e configurações do sistema. Em servidores e tarefas sensíveis, não deixe essa dependência implícita.

from urllib.request import ProxyHandler, build_opener

opener = build_opener(ProxyHandler({}))  # sem proxy autodetectado

Para proxy explícito, forneça um dicionário e mantenha credenciais fora do código. Variáveis de ambiente não confiáveis podem desviar tráfego.

Autenticação Basic e Digest

HTTPBasicAuthHandler e HTTPDigestAuthHandler integram autenticação ao opener. Basic apenas codifica usuário e senha; ele exige HTTPS. Restrinja credenciais ao URI correto para evitar envio a outros hosts. Python 3.14 adicionou suporte a SHA-256 no handler Digest.

Proteção contra SSRF

Não passe diretamente uma URL fornecida pelo usuário para urlopen(). O módulo também abre esquemas como file:, data: e FTP. Uma aplicação pode acabar lendo arquivos locais ou acessando serviços internos.

Faça parsing, permita apenas https, normalize hostname, resolva DNS, bloqueie IPs privados, loopback, link-local e metadata de nuvem, e valide novamente após cada redirect. Considere DNS rebinding e diferenças entre o hostname validado e o endereço usado na conexão.

Retries seguros

urllib.request não implementa uma política completa de retry. Repita somente erros transitórios e métodos idempotentes, com backoff e limite. Um POST pode ter sido processado mesmo se a resposta não chegou. Use idempotency key quando a API oferecer suporte.

Erros comuns

Os principais problemas são omitir timeout, ler resposta sem limite, confiar no encoding, desativar TLS, seguir redirects sem validar destino, vazar Authorization, aceitar qualquer esquema, depender de proxy do ambiente e repetir POST cegamente.

Boas práticas

Crie Request explícito, configure timeout, limite corpo e descompressão, valide status e tipo, use HTTPS com contexto seguro, trate redirects, controle proxies e feche respostas com with. Para aplicações complexas, prefira um cliente HTTP mantido com pooling, retries e limites mais completos.

Conclusão

urllib.request oferece um cliente HTTP funcional sem dependências externas. Ele é adequado para scripts e integrações controladas, desde que timeouts, limites, TLS, redirects e URLs externas sejam tratados explicitamente. A arquitetura de handlers permite personalização, mas também exige compreender o comportamento de cada etapa.

Consulte a documentação oficial de urllib.request e o RFC 9110 sobre semântica HTTP.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python em tela representando inspeção de módulos e pacotes
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifique pacotes Python

    Aprenda inspect.ispackage no Python para identificar pacotes, explorar módulos e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026