================================================================================
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.