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,118 @@
|
||||
---
|
||||
name: monitor_freelance_projects
|
||||
kind: pipeline
|
||||
lang: py
|
||||
domain: pipelines
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def monitor_freelance_projects(category: str = 'it-programming', language: str = 'es', query: str = '', pages: int = 1, include_upwork: bool = False, upwork_query: str = 'custom software', duckdb_path: str = '', xlsx_path: str = '', port: int = 9222, timeout_s: float = 25.0) -> dict"
|
||||
description: "Monitor de captacion de clientes freelance: scrapea proyectos nuevos de Workana (+ Upwork opcional) via CDP, los persiste en DuckDB con dedup por url, marca los de software a medida y exporta a Excel (hojas Nuevos y Todos)."
|
||||
tags: [market-intel, recon, launcher, pipelines, freelance, workana, upwork, duckdb, excel]
|
||||
uses_functions:
|
||||
- scrape_workana_projects_py_browser
|
||||
- scrape_upwork_projects_py_browser
|
||||
- duckdb_execute_py_infra
|
||||
- duckdb_upsert_py_infra
|
||||
- duckdb_query_readonly_py_infra
|
||||
- write_xlsx_sheets_py_infra
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/pipelines/monitor_freelance_projects.py"
|
||||
params:
|
||||
- name: category
|
||||
desc: "Categoria de Workana (segmento ?category= de la URL de listado). Default 'it-programming'."
|
||||
- name: language
|
||||
desc: "Idioma de los proyectos de Workana (?language=). Default 'es'."
|
||||
- name: query
|
||||
desc: "Query libre aplicada a ambas fuentes. En Workana va como extra_query; en Upwork sobrescribe upwork_query si no esta vacia."
|
||||
- name: pages
|
||||
desc: "Numero de paginas de listado a recorrer por fuente. Default 1."
|
||||
- name: include_upwork
|
||||
desc: "Si True, scrapea Upwork ademas de Workana. Default False (selectores Upwork sin validar en vivo + requiere login); si Upwork falla, el pipeline sigue solo con Workana."
|
||||
- name: upwork_query
|
||||
desc: "Query para Upwork cuando include_upwork. Default 'custom software'. El param 'query' lo sobrescribe si se pasa."
|
||||
- name: duckdb_path
|
||||
desc: "Ruta del archivo DuckDB de persistencia. Si vacia, usa ~/.fn_freelance/freelance.duckdb (crea el directorio)."
|
||||
- name: xlsx_path
|
||||
desc: "Ruta del .xlsx de salida. Si vacia, usa ~/.fn_freelance/freelance_projects.xlsx (crea el directorio). Se sobrescribe en cada corrida."
|
||||
- name: port
|
||||
desc: "Puerto de remote debugging del Chrome que usan los scrapers (CDP). Default 9222 (chromium-personal logueado). Usa 9333 para el Chrome aislado del browser_mcp."
|
||||
- name: timeout_s
|
||||
desc: "Timeout en segundos por pagina para los scrapers (navegacion + espera de cards). Default 25.0."
|
||||
output: "dict. En exito: {status:'ok', new_count:int (proyectos nuevos de esta corrida), total_in_db:int, new_projects:[...], xlsx_path:'<abs>', duckdb_path:'<abs>', sources:{workana:{count,status}, upwork:{count,status}|'skipped'}}. En error (sin lanzar): {status:'error', error:str, sources:{...}}."
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Requiere un Chrome con remote debugging vivo en el puerto indicado.
|
||||
# Produccion (chromium-personal logueado, port 9222) con los paths por defecto:
|
||||
fn run monitor_freelance_projects
|
||||
|
||||
# Probar contra el Chrome aislado del browser_mcp (port 9333) con paths efimeros:
|
||||
fn run monitor_freelance_projects --port 9333 \
|
||||
--duckdb-path /tmp/freelance.duckdb --xlsx-path /tmp/freelance.xlsx
|
||||
```
|
||||
|
||||
```python
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.join("python", "functions"))
|
||||
from pipelines.monitor_freelance_projects import monitor_freelance_projects
|
||||
|
||||
out = monitor_freelance_projects(
|
||||
category="it-programming",
|
||||
language="es",
|
||||
pages=1,
|
||||
port=9222, # chromium-personal logueado
|
||||
)
|
||||
print(out["new_count"], "proyectos nuevos;", out["total_in_db"], "en la DB")
|
||||
print("Excel:", out["xlsx_path"])
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Monitor de captacion de clientes: detecta proyectos freelance NUEVOS de Workana
|
||||
(programacion / software a medida) y los deja en DuckDB + Excel para revisar de un
|
||||
vistazo. Resalta los que pintan a "software a medida" (`is_custom_software`) sin
|
||||
filtrar el resto. Idempotente por `url`: re-correrlo no duplica ni pisa el
|
||||
`first_seen_at`. Agendable con dag_engine (step `function:`) para una foto diaria de
|
||||
oportunidades nuevas.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Requiere un Chrome con CDP vivo en `port`**: los scrapers (Workana/Upwork son
|
||||
SPAs) renderizan via Chrome DevTools Protocol. Sin remote debugging escuchando en
|
||||
ese puerto el pipeline devuelve `status:'error'` con el detalle. Produccion = 9222
|
||||
(chromium-personal logueado); Chrome aislado = 9333 (browser_mcp).
|
||||
- **Upwork OFF por defecto**: sus selectores no estan validados en vivo (sin sesion
|
||||
Upwork). Con `include_upwork=True`, si Upwork devuelve `status:'error'` el pipeline
|
||||
loguea un WARN a stderr y sigue solo con Workana — nunca aborta por Upwork.
|
||||
- **El Excel se sobrescribe** por completo en cada corrida (`write_xlsx_sheets`). La
|
||||
fuente de verdad acumulativa es la DuckDB, no el .xlsx.
|
||||
- **`first_seen_at` lo posee la DB**: el upsert usa ownership selectivo (no esta en
|
||||
`update_cols`), asi que una re-corrida conserva la primera vez que se vio cada
|
||||
proyecto. `new_count` cuenta solo urls que no existian antes de esta corrida.
|
||||
- **Rate-limit / anti-bot**: scrapear muchas paginas seguidas puede disparar
|
||||
defensas de las plataformas. Mantener `pages` bajo y espaciar las corridas.
|
||||
- **Skills se guardan como `skills_json`** (TEXT con JSON) porque DuckDB no usa una
|
||||
columna lista aqui; en el Excel se re-expanden a una cadena separada por comas.
|
||||
|
||||
## Notas
|
||||
|
||||
Pipeline impuro: compone seis funciones del registry sin reescribir su logica
|
||||
(2 scrapers CDP del dominio browser + 3 primitivas del grupo `duckdb` + el exporter
|
||||
`write_xlsx_sheets`). El flag `is_custom_software` se calcula con la constante
|
||||
`CUSTOM_SW_KEYWORDS` (keywords fuertes de desarrollo a medida) sobre title + snippet
|
||||
+ skills, normalizados a minusculas y sin acentos.
|
||||
|
||||
Validado end-to-end contra Workana real (CDP 9333) el 17/06/2026:
|
||||
- Golden: `new_count=9`, `total_in_db=9`, 4 proyectos `is_custom_software=True`,
|
||||
.xlsx con hojas "Nuevos" (9 filas + cabecera) y "Todos", DuckDB con 9 filas.
|
||||
- Edge dedup: 2a corrida identica -> `new_count=0`, `total_in_db` sigue en 9 (no
|
||||
duplica) y `first_seen_at` preservado (ownership del upsert por `url`).
|
||||
Reference in New Issue
Block a user