================================================================================ PROMPT 03 — INTEGRADO (MICROSOFT COPILOT) Nombre: Contract Downloader · Track Copilot Iterativo Herramientas: Microsoft Copilot Chat (web / Microsoft 365), Copilot en VS Code, Copilot en Edge Tiempo estimado breakout: 25 minutos Nivel: Usuario corporativo de Microsoft 365 con Copilot habilitado ================================================================================ OBJETIVO DEL BREAKOUT --------------------- Al finalizar los 25 minutos, el participante debe tener un script Python ensamblado a partir de 4 prompts cortos secuenciales, capaz de DESCARGAR archivos PDF de tabuladores y contratos colectivos desde el repositorio publico del Centro Federal de Conciliacion y Registro Laboral (CFCRL), a partir de una lista de numeros de CCT entregada por el facilitador. POR QUE ESTE FORMATO -------------------- - Copilot Chat suele truncar respuestas largas y prefiere ejemplos cortos. - Funciona mejor refinando codigo existente que generandolo desde cero. - En entorno corporativo el participante ya tiene Copilot habilitado y no necesita crear cuentas extra. REQUISITOS PREVIOS ------------------ - Microsoft Copilot Chat habilitado (web, M365 o Edge sidebar) - Python 3.10+ y pip instalados, o acceso a una terminal con permisos - Un archivo nuevo en VS Code o Notepad llamado `cfcrl_downloader.py` - 3 CCTs de prueba entregados por el facilitador - Red sin proxy corporativo que bloquee *.gob.mx ALCANCE DEL EJERCICIO --------------------- Esta version basica descarga los archivos cuyos enlaces estan visibles directamente en el HTML del repositorio. NO implementa el flujo AJAX "btn-generar-liga" (CSRF + token temporal) que el descargador profesional usa para bajar convenios y anexos protegidos. Aun asi, recupera la mayoria de documentos publicos accesibles, incluyendo tabuladores y contratos firmados directamente enlazados. ================================================================================ FLUJO ITERATIVO — 4 PROMPTS CORTOS, EN ORDEN ================================================================================ Despues de cada respuesta de Copilot, copia el codigo sugerido al archivo `cfcrl_downloader.py` en el orden que se indica. Si Copilot entrega menos de lo pedido, mandale: "Continua desde donde te quedaste, sin repetir lo anterior." Suele funcionar. -------------------------------------------------------------------------------- PASO 1 — CONFIGURACION INICIAL Y CONSTANTES (3-4 min) -------------------------------------------------------------------------------- PROMPT A COPIAR: Necesito construir un script Python que descargue archivos PDF desde el repositorio publico del Centro Federal de Conciliacion y Registro Laboral de Mexico. Las URLs reales y verificadas del sitio son: Busqueda: https://repositorio.centrolaboral.gob.mx Pagina contrato: https://repositorio.centrolaboral.gob.mx/contrato/{id} Descarga PDF: https://registro.centrolaboral.gob.mx/documento-publico/download/... Dame solo la primera seccion del archivo cfcrl_downloader.py: - Imports: requests, BeautifulSoup de bs4, time, logging, re, pathlib.Path - Constantes en mayusculas: BASE_REPO = "https://repositorio.centrolaboral.gob.mx" BASE_REG = "https://registro.centrolaboral.gob.mx" USER_AGENT = string realista de Chrome 120 en Windows TIMEOUT = 30 PAUSA = 2.5 # segundos entre peticiones OUTPUT_DIR = Path("descargas") CCTS = ["REEMPLAZAR_1", "REEMPLAZAR_2", "REEMPLAZAR_3"] - Configuracion de logging a nivel INFO con formato corto sin emojis - Una funcion construir_sesion() que devuelva una requests.Session con los headers User-Agent y "Accept-Language: es-MX,es;q=0.9" Solo esa parte. Codigo en un solo bloque, sin explicacion larga. -------------------------------------------------------------------------------- PASO 2 — BUSQUEDA DE CONTRATOS POR CCT (5-7 min) -------------------------------------------------------------------------------- PROMPT A COPIAR: Sobre el archivo anterior, agrega dos funciones: 1. buscar_ids_contrato(session, cct) que: - Haga GET a BASE_REPO con query params {"search": cct, "tab": "contratos", "form_general": "1"} y timeout=TIMEOUT. - Si la respuesta no es 2xx, log warning y devuelva lista vacia. - Parsee el HTML con BeautifulSoup (parser "html.parser") y encuentre todos los cuyo href haga match con la expresion regular r"^/contrato/(\d+)$" - Devuelva una lista de enteros unicos, ordenada de mayor a menor (mas reciente primero). 2. obtener_links_descarga(session, contrato_id) que: - Haga GET a BASE_REPO + "/contrato/" + str(contrato_id) con timeout=TIMEOUT. - Si falla, log warning y devuelva []. - Parsee el HTML y devuelva todas las URLs absolutas de los cuyo href contenga la cadena "/documento-publico/download/". - Si el href empieza con "/", anteponer BASE_REG. Si empieza con "http", dejarlo tal cual. - Eliminar duplicados conservando orden. Dame solo esas dos funciones, listas para pegar despues de las constantes. -------------------------------------------------------------------------------- PASO 3 — CLASIFICACION Y DESCARGA DE ARCHIVOS (6-8 min) -------------------------------------------------------------------------------- PROMPT A COPIAR: Ahora agrega tres funciones mas al mismo archivo: 1. nombre_archivo_desde_url(url) que: - Extraiga el nombre del archivo desde la URL. Las URLs del sitio usan "|" como separador interno, por ejemplo: .../download/2025|06|26|tramite-403862|contratos|miarchivo.pdf El nombre real es el segmento posterior al ULTIMO "|". Si la URL no contiene "|", usar la ultima parte despues del ultimo "/". - Sanitizar dejando solo letras, digitos, guion, punto y underscore; reemplazar el resto por "_". - Truncar a 120 caracteres. 2. clasificar_tipo(url) que devuelva: - "tabuladores" si la URL en minusculas contiene "tabulador", "sueldos_pdf" o "sueldos_excel" - "actas_votacion" si contiene "acta" o "resultado" - "convocatorias" si contiene "convocatoria" - "contratos" en cualquier otro caso (default) 3. descargar_archivo(session, url, ruta_destino) que: - Si ruta_destino ya existe en disco, log info "ya existe" y devolver "ya_existe". - Hacer GET con stream=True y timeout=TIMEOUT. - Si la respuesta no es 2xx, log warning y devolver "error". - Leer los primeros 4 bytes para validar firma de PDF ("%PDF") o verificar Content-Type que contenga "pdf", "officedocument" o "msword". Si no es archivo valido, log warning y devolver "no_archivo". - Crear las carpetas necesarias (mkdir parents=True exist_ok=True). - Escribir el contenido en chunks de 8192 bytes. - Devolver "ok" si se escribio correctamente. Dame solo esas tres funciones. -------------------------------------------------------------------------------- PASO 4 — ORQUESTACION Y MAIN (3-4 min) -------------------------------------------------------------------------------- PROMPT A COPIAR: Cierra el script con la orquestacion principal: 1. procesar_cct(session, cct) que: - Loggea "==== Procesando CCT {cct}". - Llama buscar_ids_contrato; si vacio, log warning "sin resultados" y devuelve dict {"ok":0,"ya_existe":0,"error":0,"no_archivo":0}. - Para cada id, espera PAUSA, llama obtener_links_descarga, y para cada link clasifica el tipo, arma la ruta OUTPUT_DIR / cct / tipo / nombre, espera PAUSA, llama descargar_archivo y acumula contadores. - Devuelve los contadores. 2. Bloque main bajo if __name__ == "__main__": - Crea la sesion. - Inicializa contador global. - Itera sobre CCTS, suma contadores parciales al global. - Al final imprime un resumen claro: "RESUMEN: descargados=X, ya_existian=Y, errores=Z, no_archivo=W" "Carpeta de salida: {OUTPUT_DIR.resolve()}" Dame solo esa parte final. Despues confirmare que el archivo completo corre. -------------------------------------------------------------------------------- EJECUCION -------------------------------------------------------------------------------- En terminal, en la carpeta donde guardaste cfcrl_downloader.py: pip install requests beautifulsoup4 python cfcrl_downloader.py Antes de ejecutar, edita la lista CCTS al inicio del archivo y reemplaza los 3 placeholders por los CCTs reales que entrego el facilitador. Despues de la corrida, abre la carpeta `descargas/` para ver los archivos PDF organizados por CCT y por tipo. ================================================================================ SI ALGO SALE MAL — TROUBLESHOOTING RAPIDO ================================================================================ 1. Copilot entrega un fragmento incompleto o cortado Mandale: "La respuesta se corto. Continua exactamente desde donde te quedaste, sin repetir lo anterior." 2. El script corre pero la carpeta descargas queda vacia Pega en Copilot: "Modifica buscar_ids_contrato y obtener_links_descarga para que la primera vez que se ejecuten guarden el HTML recibido en debug_busqueda.html y debug_contrato.html. Ademas que impriman cuantos enlaces /contrato/ y cuantos /documento-publico/download/ encontraron." Cuando tengas esos archivos, abrelos en VS Code para ver que devolvio el sitio. 3. Las descargas devuelven contenido pero no son PDFs validos Pidele a Copilot: "En descargar_archivo, antes de validar la firma PDF, imprime Content-Type, Content-Length y los primeros 200 caracteres de la respuesta solo en la primera descarga. Devuelveme el codigo modificado." 4. El portal devuelve 403 o no responde Pidele a Copilot: "Agrega manejo del 403 con un reintento usando un User-Agent alternativo de Firefox y sube PAUSA a 4 segundos." Si persiste, verifica que tu red corporativa no este bloqueando dominios *.gob.mx (puede pasar en redes con proxy estricto; prueba con hotspot 5G). 5. Encuentras el HTML pero no hay enlaces /documento-publico/download/ Es probable que ese contrato use exclusivamente botones AJAX ("btn-generar-liga"), que estan fuera del alcance de este ejercicio. Prueba con otro CCT de los entregados. ================================================================================ VARIANTES Y EXTENSIONES (mas alla del breakout) ================================================================================ A. Lectura de CCTs desde Excel Pidele a Copilot: "Modifica el script para que en vez de tener CCTS hardcoded, lea una columna llamada 'CCT' de un archivo Excel llamado empresas.xlsx hoja Sheet1, usando openpyxl. Mantente todo lo demas igual." B. Integracion con Power Automate Desktop Pidele: "Convierte la logica de procesar_cct en pseudocodigo para un flujo de Power Automate Desktop, recibiendo un CCT como entrada y devolviendo el numero de archivos descargados como salida." Util si tu organizacion ya usa PAD y prefiere no distribuir Python a usuarios finales. C. Programacion recurrente con Task Scheduler Pidele: "Dame el comando schtasks.exe para correr este script todos los lunes 7:00 AM en Windows, redirigiendo la salida a un archivo .log con la fecha del dia." Sirve para mantener un inventario local actualizado sin intervenir. ================================================================================ DISCLAIMER DE USO RESPONSABLE ================================================================================ El portal del CFCRL es un servicio publico. Mantente en PAUSA de minimo 2 segundos entre peticiones, limita el numero de CCTs por corrida a lo que realmente necesites, y evita correr el script en horarios pico de oficinas gubernamentales (9-14 hrs hora centro). La informacion es publica pero su uso posterior debe respetar la Ley Federal de Proteccion de Datos Personales en Posesion de los Particulares cuando incluya datos de personas fisicas identificables. Cita la fuente al publicar analisis derivados.