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

    Archivos protegidos que representan extracción segura de TAR con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extrae TAR con seguridad

    Aprende tarfile extraction_filter en Python para extraer archivos TAR con validación, seguridad y control de rutas.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python mostrando avisos controlados con catch_warnings
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: captura warnings en pruebas Python

    Aprende catch_warnings en Python para capturar, probar y controlar avisos con filtros específicos y alcance seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python en pantalla que representa inspección de módulos y paquetes
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifica paquetes Python

    Aprende inspect.ispackage en Python para identificar paquetes, explorar módulos y crear herramientas de introspección seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026