---
name: scrape_amazon_search_saturation_cdp
kind: function
lang: py
domain: browser
version: "1.0.0"
purity: impure
signature: "def scrape_amazon_search_saturation_cdp(query: str, marketplace: str = 'amazon.es', port: int = 9222, timeout_s: float = 25.0) -> dict"
description: "Mide la saturacion de mercado de un termino en la busqueda de Amazon (/s?k=) renderizando la pagina via CDP en el navegador diario. Devuelve nº de resultados que declara Amazon, anuncios patrocinados en el top y cards de la primera pagina. Senal para decidir dropshipping. Compone cdp_open_url_and_wait + cdp_eval; nunca inventa datos (captcha -> status='captcha', sin oferta -> status='error')."
tags: [market-intel, amazon, saturation, cdp, dropship]
uses_functions: [cdp_open_url_and_wait_py_pipelines, cdp_eval_py_browser]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/browser/scrape_amazon_search_saturation_cdp.py"
params:
  - name: query
    desc: "Termino de busqueda (ej. 'cepillo gato'). Los espacios se convierten a '+' en la URL /s?k=."
  - name: marketplace
    desc: "Dominio Amazon objetivo. Default 'amazon.es'. Tambien 'amazon.com', etc. Determina la URL."
  - name: port
    desc: "Puerto de remote debugging del Chrome a usar. Default 9222 (chromium-personal residential de produccion). 9333 = Chrome aislado del browser_mcp."
  - name: timeout_s
    desc: "Timeout en segundos para la navegacion. Default 25.0. Tras el load event espera ~2s extra para que el grid monte async."
output: "dict autosuficiente {status, source:'amazon_saturation', query, marketplace, total_results (int|None = nº de resultados que declara Amazon, aproximado/redondeado), sponsored_top (int = anuncios entre los primeros 16 cards), n_cards (int = cards de la 1a pagina), scraped_at (ISO8601 UTC)}. status='ok' en exito; 'captcha' si Amazon sirve interstitial anti-bot; 'error' si no hay resultados ni cards (con clave 'error' explicando el motivo). total_results puede ser None aun con status='ok' si Amazon oculta el contador."
---

## Ejemplo

```bash
# Requiere chromium-personal con CDP vivo en 9222 (navegador diario residential).
cd $HOME/fn_registry
./fn run scrape_amazon_search_saturation_cdp "cepillo gato"
# -> {"status":"ok","source":"amazon_saturation","query":"cepillo gato",
#     "marketplace":"amazon.es","total_results":50000,"sponsored_top":3,
#     "n_cards":60,"scraped_at":"2026-06-20T14:38:04+00:00"}
```

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from browser.scrape_amazon_search_saturation_cdp import scrape_amazon_search_saturation_cdp

r = scrape_amazon_search_saturation_cdp("soporte movil coche")
if r["status"] == "ok":
    print(r["total_results"], "resultados |", r["sponsored_top"], "sponsored top16")
elif r["status"] == "captcha":
    print("handoff humano: Amazon pidio captcha")
```

## Cuando usarla

Cuando evaluas si un producto/nicho merece la pena para dropshipping y necesitas
medir cuanta OFERTA ya existe en Amazon. Muchos resultados + muchos sponsored =
mercado saturado y disputado (mala apuesta); pocos resultados = hueco. Usala como
filtro de saturacion en el pipeline de market-intel de `captacion_clientes`,
junto a las senales de demanda (`scrape_amazon_movers_cdp`) y precios de la
competencia. Tambien para comparar la misma query entre marketplaces.

## Gotchas

- **IMPURA, depende de chromium-personal**: necesita un Chrome con remote
  debugging vivo en `port` (9222 = navegador diario residential). Si el puerto no
  responde -> `status='error'` con mensaje "no hay Chrome usable en el puerto N".
  El residential pasa el anti-bot mucho mejor que `requests`/Chrome aislado.
- **El nº de resultados es APROXIMADO**: Amazon redondea el contador (ej. "more
  than 50,000 results"). `total_results` es esa cifra redondeada, no un conteo
  exacto. Util como orden de magnitud, no como dato fino.
- **`total_results` puede ser None aun con `status='ok'`**: Amazon a veces oculta
  el `s-result-info-bar` para ciertas busquedas. Si hay cards (`n_cards > 0`) el
  status sigue siendo `'ok'`; usa `n_cards` y `sponsored_top` como respaldo.
- **Header en ingles aun en amazon.es**: el bar puede salir "over 50,000 results"
  en vez de "mas de 50.000 resultados". El regex y el parser de int cubren ambos
  idiomas y ambos separadores de millar (`,` y `.`).
- **Captcha = handoff humano**: si `status='captcha'`, NO reintentar en bucle ni
  auto-resolver. El token va atado a la sesion del navegador; activa la pestana
  (`fn run focus_cdp_tab_window`) y avisa al humano.
- **Fragil ante cambios de layout**: depende de los selectores de Amazon
  (`s-result-info-bar`, `s-search-result`, texto "sponsored/patrocinado"). Si
  Amazon cambia la plantilla, el JS deja de extraer y devuelve `status='error'`.
- **`sponsored_top` solo mira los primeros 16 cards**: es una muestra de la
  densidad de anuncios arriba, no el total de sponsored de la pagina.
