feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
---
|
||||
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".
|
||||
Reference in New Issue
Block a user