urllib.request en Python: HTTP nativo

Publicado el: 19/08/2026
Tempo de leitura: 4 minutos
Teclas formando HTTP que representan solicitudes con urllib.request en Python

El módulo urllib.request permite abrir URLs y realizar solicitudes HTTP utilizando únicamente la biblioteca estándar. Proporciona urlopen(), objetos Request configurables y una arquitectura de handlers para redirects, autenticación, cookies, proxies y HTTPS.

Clientes superiores como Requests o HTTPX son más cómodos en aplicaciones grandes, pero urllib.request es útil en scripts portables, instaladores, herramientas administrativas y entornos sin dependencias externas. Esta guía cubre GET, POST, JSON, descargas limitadas, TLS, redirects, proxies, errores, retries y protección frente a URLs no confiables.

Primera solicitud 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)

La respuesta funciona como context manager y expone status, headers y url. El cuerpo son bytes porque el cliente no puede determinar automáticamente el encoding correcto.

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

La guía de codecs en Python explica la frontera entre texto y bytes.

Usa siempre timeout

Sin timeout, DNS, conexión, TLS o lectura pueden bloquear el programa durante mucho tiempo.

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

El timeout no limita el tamaño. Un servidor puede continuar enviando datos mientras la conexión sigue activa. Cuenta los bytes y detén la lectura al superar el máximo configurado.

Objeto Request

from urllib.request import Request, urlopen

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

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

Identifica la automatización honestamente. No suplantes un navegador para evitar políticas. Respeta límites, términos y robots cuando corresponda.

Query strings correctas

from urllib.parse import urlencode

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

La guía de urllib.parse en Python cubre quoting, análisis y validación de URLs.

POST de formulario

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)

Si se proporciona data sin método, POST se convierte en el valor por defecto. Declararlo facilita la revisión.

Enviar JSON

import json
from urllib.request import Request, urlopen

payload = json.dumps({"title": "Ejemplo"}).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"))

Valida Content-Type, estado y tamaño antes de procesar. La guía para integrar APIs con Python complementa autenticación y errores.

HTTPError y 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"Fallo de red: {error.reason}")
except TimeoutError:
    print("Tiempo agotado")

HTTPError representa una respuesta de error y todavía permite leer un cuerpo limitado. URLError cubre DNS, conexión, TLS y protocolos.

Descargas con límite

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("El archivo declarado es demasiado 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("La descarga superó el límite")
            output.write(chunk)

Escribe en un temporal, valida formato o digest y mueve de forma atómica. La guía de hashlib en Python muestra verificación SHA-256.

Contenido comprimido

urllib.request no descomprime automáticamente todos los encodings. Si solicitas gzip, inspecciona la cabecera y limita entrada y salida.

import gzip

encoding = response.headers.get("Content-Encoding", "").lower()
raw = response.read(2_000_000)
data = gzip.decompress(raw) if encoding == "gzip" else raw

Para contenido grande o externo usa descompresión incremental con máximo. Consulta la guía de gzip en Python.

TLS y CA privada

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)

HTTPS valida certificado y hostname de forma segura. No desactives la comprobación. La guía de ssl en Python explica los contextos TLS.

Redirects

El opener por defecto sigue redirects. Revisa response.url para conocer el destino final. Los headers sensibles no deben enviarse a dominios inesperados. Usa add_unredirected_header() o un handler restrictivo.

Los códigos 301 y 302 pueden convertir POST en GET. Los códigos 307 y 308 conservan el método. Revisa esta política en operaciones que modifican estado.

Desactivar 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())

Si permites redirects, limita la cantidad y valida esquema, hostname, puerto e IP resuelta en cada salto.

Proxies del entorno

El opener puede leer http_proxy, https_proxy y configuración del sistema. Un servicio sensible no debería depender de un entorno no controlado.

from urllib.request import ProxyHandler, build_opener

opener = build_opener(ProxyHandler({}))

Un diccionario vacío desactiva proxies detectados. Configura proxies aprobados explícitamente y mantén credenciales fuera del código.

Autenticación Basic y Digest

HTTPBasicAuthHandler y HTTPDigestAuthHandler integran credenciales. Basic solo codifica usuario y contraseña y requiere HTTPS. Limita cada credencial al URI correcto. Python 3.14 añadió SHA-256 a Digest.

Protección contra SSRF

No pases una URL del usuario directamente a urlopen(). La biblioteca acepta esquemas como file:, data: y FTP, lo que puede leer archivos locales o acceder a servicios internos.

Permite solo HTTPS, normaliza hostname, resuelve DNS, bloquea direcciones privadas, loopback, link-local y metadata de nube, y repite la validación después de cada redirect. Considera DNS rebinding.

Retries seguros

El módulo no incluye una política completa. Repite solo errores transitorios y métodos idempotentes, con backoff y máximo. Un POST puede haberse procesado aunque la respuesta se perdiera. Usa idempotency key cuando exista.

Errores frecuentes

Los fallos comunes son omitir timeout, leer sin límite, asumir encoding, desactivar TLS, seguir redirects sin validar destino, filtrar Authorization, aceptar cualquier esquema, heredar proxies no confiables y repetir POST ciegamente.

Buenas prácticas

Crea Request explícito, define timeout, limita respuesta y descompresión, valida estado y tipo, usa HTTPS seguro, controla redirects y proxies y cierra con with. Para aplicaciones complejas, prefiere un cliente mantenido con pooling y políticas más completas.

Conclusión

urllib.request es un cliente HTTP funcional sin dependencias. Es adecuado para scripts controlados si timeouts, límites, TLS, redirects y URLs externas se gestionan explícitamente. Su arquitectura de handlers es potente, pero exige comprender cada etapa.

Consulta la documentación oficial de urllib.request y el RFC 9110 sobre semántica HTTP.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Cables Ethernet conectados que representan servidores de red con socketserver en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    socketserver en Python: crea servidores

    Aprende socketserver en Python para crear servidores TCP y UDP con handlers, concurrencia, límites, timeouts y apagado seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Sala de servidores iluminada que representa conexiones TLS seguras con ssl en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ssl en Python: conexiones TLS seguras

    Aprende ssl en Python para crear clientes y servidores TLS, validar certificados y hostname, configurar CA, versiones mínimas y mTLS.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Pantalla de verificación de cuenta que representa autenticación de mensajes con HMAC en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    HMAC en Python: autentica mensajes

    Aprende HMAC en Python para firmar y validar webhooks, archivos y mensajes con SHA-256, claves seguras y comparación resistente a

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Lector de huella digital que representa verificación de hashes con hashlib en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    hashlib en Python: hashes seguros

    Aprende hashlib en Python para calcular SHA-256, verificar archivos, usar BLAKE2, derivar claves y evitar errores comunes de seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Código binario proyectado que representa conversiones con binascii en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    binascii en Python: binario y ASCII

    Aprende binascii en Python para convertir hexadecimal, Base64 y quoted-printable, calcular CRC y validar datos binarios de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    codecs en Python: domina encodings

    Aprende codecs en Python para usar encodings, handlers, BOM, streams incrementales y migrar codecs.open a open.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026