From a6ac87582053f7b8f549d9d33b82b423518e3a1b Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 29 Jul 2026 06:49:43 +0200 Subject: [PATCH] feat: DAG elempleo_co_daily (05:00 UTC) --- dags/elempleo_co_daily.py | 99 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 dags/elempleo_co_daily.py diff --git a/dags/elempleo_co_daily.py b/dags/elempleo_co_daily.py new file mode 100644 index 0000000..77a20d5 --- /dev/null +++ b/dags/elempleo_co_daily.py @@ -0,0 +1,99 @@ +""" +DAG: elempleo_co_daily +Ejecuta el scraper de ElEmpleo.com Colombia via SSH en el ARM VPS (152.53.242.31). +Schedule : 05:00 UTC diario +Autor : jpuma +""" + +from datetime import datetime, timedelta + +import paramiko +from airflow import DAG +from airflow.operators.python import PythonOperator + +# --------------------------------------------------------------------------- +# Constantes de conexión +# --------------------------------------------------------------------------- +VPS_HOST = '152.53.242.31' +VPS_PORT = 22 +VPS_USER = 'root' +VPS_PASS = 'XpvvdHnNr0Bq9qU' + +VENV_PYTHON = '/home/jpuma/proyectos/bot-sunat/.venv/bin/python' +SCRAPER_PATH = '/opt/scrapers/elempleo_co/scraper.py' + +# --------------------------------------------------------------------------- +# default_args +# --------------------------------------------------------------------------- +default_args = { + 'owner' : 'jpuma', + 'retries' : 2, + 'retry_delay' : timedelta(minutes=15), + 'email' : ['jpuma@redneurocom.com'], + 'email_on_failure' : True, + 'email_on_retry' : False, + 'execution_timeout': timedelta(hours=6), +} + + +# --------------------------------------------------------------------------- +# Callable +# --------------------------------------------------------------------------- +def run_scraper(**ctx): + """Conecta al VPS via SSH y ejecuta el scraper. Valida el patrón de éxito.""" + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect( + hostname=VPS_HOST, + port=VPS_PORT, + username=VPS_USER, + password=VPS_PASS, + timeout=30, + ) + ssh.get_transport().set_keepalive(30) + + cmd = '{} {} 2>&1'.format(VENV_PYTHON, SCRAPER_PATH) + + # exec_command con timeout de 6 horas (igual que execution_timeout del DAG) + _, stdout, _ = ssh.exec_command(cmd, timeout=21600) + log = stdout.read().decode('utf-8', errors='replace') + exit_code = stdout.channel.recv_exit_status() + ssh.close() + + # Volcamos el log completo para visibilidad en Airflow + print("===== LOG SCRAPER ELEMPLEO =====") + print(log) + print("===== EXIT CODE: {} =====".format(exit_code)) + + # Validación del patrón de éxito obligatorio + if 'Finalizado:' not in log: + raise RuntimeError( + "Scraper ElEmpleo no finalizó correctamente " + "(exit={}). Ultimas lineas: {}".format(exit_code, log[-800:]) + ) + + if exit_code != 0: + raise RuntimeError( + "Scraper terminó con exit code {}. Log: {}".format(exit_code, log[-800:]) + ) + + +# --------------------------------------------------------------------------- +# DAG +# --------------------------------------------------------------------------- +with DAG( + dag_id = 'elempleo_co_daily', + default_args = default_args, + description = 'Scraper diario de ofertas de empleo — ElEmpleo.com Colombia', + schedule_interval = '0 5 * * *', + start_date = datetime(2025, 1, 1), + catchup = False, + max_active_runs = 1, + tags = ['scraping', 'elempleo', 'co', 'empleos'], +) as dag: + + scrape_task = PythonOperator( + task_id = 'run_elempleo_scraper', + python_callable = run_scraper, + provide_context = True, + )