--- name: scrape_aliexpress_cdp kind: function lang: py domain: browser version: "1.0.0" purity: impure signature: "def scrape_aliexpress_cdp(query: str, sort: str = 'total_tranpro_desc', limit: int = 40, port: int = 9222, timeout_s: float = 25.0) -> dict" description: "Scrapea productos de AliExpress por Chrome DevTools Protocol (CDP) sobre el navegador diario logueado (chromium-personal, puerto 9222, IP residencial), evitando el captcha que bloquea el scraper HTTP. Capta coste en China (EUR) y numero de pedidos (demanda real) como senal de dropshipping: que importar de China. Ordena por defecto por numero de pedidos." tags: [market-intel, aliexpress, cdp, dropship, scraper, browser] uses_functions: [cdp_eval_py_browser] uses_types: [] returns: [] returns_optional: false error_type: "error_py_core" imports: [json, os, re, sys, time, datetime, browser.cdp_eval] params: - name: query desc: "Termino de busqueda (ej. 'organizador maletero coche'). Los espacios se convierten en guiones para la URL de busqueda." - name: sort desc: "Orden de resultados. 'total_tranpro_desc' = por numero de pedidos (demanda real, default util para dropshipping). Otros: 'default', 'price_asc', 'price_desc'." - name: limit desc: "Numero objetivo de productos a recolectar. El scroll itera (cap de 8 scrolls) hasta acercarse a este valor o hasta que el conteo de cards deja de crecer." - name: port desc: "Puerto de remote debugging de Chrome. Default 9222 (chromium-personal)." - name: timeout_s desc: "Timeout en segundos para cada evaluacion CDP. Default 25.0." output: "dict autosuficiente {status: 'ok'|'error'|'captcha', source:'aliexpress', query, url, count, products:[...]}. Cada product: item_id(str), url(str), title(str), price(float EUR|None), price_orig(float|None), rating(float|None), orders(str crudo p.ej. '10.000+ vendidos'|None), orders_num(int aprox), ship_from(str|None), scraped_at(iso). Nunca inventa datos: sin cards -> status='error' products=[]; captcha -> status='captcha' products=[]. Nunca lanza." tested: false tests: [] test_file_path: "" file_path: "python/functions/browser/scrape_aliexpress_cdp.py" --- ## Ejemplo ```bash # Requiere chromium-personal con remote debugging en 9222 y sesion logueada. cd "$HOME/fn_registry" python/.venv/bin/python3 python/functions/browser/scrape_aliexpress_cdp.py "organizador maletero coche" "total_tranpro_desc" 40 ``` ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from browser.scrape_aliexpress_cdp import scrape_aliexpress_cdp res = scrape_aliexpress_cdp("organizador maletero coche", sort="total_tranpro_desc", limit=40) print(res["status"], res["count"]) for p in res["products"][:3]: print(p["price"], "EUR -", p["orders_num"], "pedidos -", p["title"][:50]) # ok 7 # 14.49 EUR - 10000 pedidos - Caja organizadora de maletero de coche, gran capac... # 56.87 EUR - 5000 pedidos - YZ para Tesla Model Y Juniper 2021-2026, caja de al... ``` ## Cuando usarla Cuando necesites el **coste en China + la demanda (numero de pedidos)** de un producto para decidir que importar (market intelligence de dropshipping, proyecto `captacion_clientes`). Usala en lugar de `scrape_aliexpress_trending_py_datascience` cuando ese scraper HTTP devuelva captcha: esta via opera el navegador diario logueado con IP residencial y no dispara el muro anti-bot. La persistencia (DuckDB/Postgres/Excel) la hace un componente aparte: el dict de salida es autosuficiente y no casa con ninguna tabla. ## Gotchas - **Impura, depende del navegador diario**: requiere `chromium-personal` corriendo con `--remote-debugging-port=9222` y la sesion de AliExpress logueada (IP residencial). Sin CDP vivo, `cdp_eval` devuelve `ok=False` y la funcion retorna `status='error'`. - **Pisa la pestana activa de AliExpress**: navega via `location.href` sobre el primer target `page` cuya URL contenga "aliexpress" (o el primer page si no hay). Si tienes una pestana de AliExpress con trabajo en curso, la reemplaza. - **Volumen real bajo por pagina**: la galeria `/w/wholesale-...` suele exponer solo ~7-12 cards reales (el resto son banners promocionales "GRATIScon una compra" sin precio, que se descartan). `count` reflejara los productos reales disponibles en la pagina, no siempre llegara a `limit`. Para mas volumen hay que paginar (`&page=2`), fuera del alcance de esta funcion. - **Fragil ante cambios de HTML de AliExpress**: depende del selector `.search-item-card-wrapper-gallery` y del formato del texto de la card (`14,49€32,2€ -55%4.610.000+ vendidos`). Si AliExpress cambia el markup, la extraccion devolvera campos None o `status='error'` (no inventa datos). - **Lee `textContent`, no `innerText`**: las cards fuera del viewport devuelven `innerText` vacio; por eso se usa `textContent` normalizado. El texto viene pegado sin saltos de linea y los regex no dependen de `\n`. - **Captcha posible**: si AliExpress muestra el slider "nc" / punish page, la funcion lo detecta y devuelve `status='captcha'` sin intentar resolverlo. Reaccion correcta: handoff humano (activar la pestana y resolver a mano). - `orders_num` es aproximado: `'10.000+'` -> 10000, `'5.000+'` -> 5000, `'1.234'` -> 1234 (quita puntos de millar y el `+`). El `+` significa "al menos ese numero".