Cómo bloquear Bots usando user-agents en Python y FastAPI

Tiempo de lectura: < 1 minuto

Hoy vamos a aprender cómo podemos implementar un bloqueador de bots usando user-agents.

Gato - pexels

1. Instalar la librería

pip install user-agents

2. Crear el archivo del decorador

# dependencies/bot_detection.py

import functools
from fastapi import Request, HTTPException
from user_agents import parse

def block_bots(func):
    @functools.wraps(func)
    async def wrapper(request: Request, *args, **kwargs):
        ua_string = request.headers.get("user-agent", "")
        ua = parse(ua_string)

        if ua.is_bot:
            raise HTTPException(status_code=403, detail="Acceso denegado.")

        return await func(request, *args, **kwargs)
    return wrapper

3. Importarlo y usarlo en tu router

from dependencies.bot_detection import block_bots

@limiter.limit("5/minute")
@amazon.get("/mi_endpoint", response_model=str, status_code=status.HTTP_200_OK)
@block_bots
async def endponit(
    request: Request,
    busqueda: str = Query(..., description="Término de búsqueda"),
    limit: int = Query(1, description="Número de resultados a obtener")
) -> str:
    logger.info(f"Buscando {busqueda}")
    ...

4. Probar que funciona

Puedes testear rápido desde terminal:

# Petición normal — debe pasar ✅
curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36" \
  http://localhost:8000/mi_endpoint?busqueda=1234567890

# Petición de bot — debe dar 403 ❌
curl -H "User-Agent: Googlebot/2.1" \
  http://localhost:8000/mi_endpoint?busqueda=1234567890

5. Opcional — loguear los bloqueos

Si quieres ver en tu log quién está siendo bloqueado:

def block_bots(func):
    @functools.wraps(func)
    async def wrapper(request: Request, *args, **kwargs):
        ua_string = request.headers.get("user-agent", "")
        ua = parse(ua_string)

        if ua.is_bot:
            logger.warning(f"Bot bloqueado - IP: {request.client.host} UA: {ua_string}")
            raise HTTPException(status_code=403, detail="Acceso denegado.")

        return await func(request, *args, **kwargs)
    return wrapper

Para utilizarla: recuerda añadir siempre:

 request: Request,

En los parametros de entrada de la función del route.

Deja un comentario