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

    Programador analizando código para identificar tipos MIME de archivos con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecta tipos MIME

    Aprende mimetypes.guess_file_type en Python para detectar tipos MIME en rutas, URLs, uploads y respuestas HTTP con fallbacks seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    13/09/2026
    Componentes de servidor que representan intérpretes Python aislados ejecutándose en paralelo
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real en Python

    Aprende InterpreterPoolExecutor en Python para tareas CPU-bound, intérpretes aislados, paralelismo real y concurrencia segura.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocesador que representa las CPU disponibles para un proceso Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: cuenta CPUs disponibles

    Aprende os.process_cpu_count en Python para dimensionar workers según las CPU disponibles para el proceso.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Portátil con código digital que representa datos BLOB en SQLite
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: lee BLOBs sin cargar todo en memoria

    Aprende sqlite3.Blob en Python para leer y escribir BLOBs por partes, reducir memoria y manejar datos binarios en SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026
    Análisis estadístico para random.binomialvariate en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    random.binomialvariate: simula resultados binomiales

    Aprende random.binomialvariate en Python para simular éxitos, validar probabilidades y analizar escenarios binomiales con ejemplos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/09/2026
    Código Python y análisis de firmas de funciones
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    inspect.signature.bind: valida argumentos de funciones

    Aprende inspect.signature.bind en Python para validar argumentos, aplicar valores predeterminados y crear APIs dinámicas seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    11/09/2026