Pipeline de extracción

El pipeline de camara-senadores-mex ejecuta dos recorridos principales: votaciones nominales y perfiles de senadores. Ambos usan Scrapy, pero tienen fuentes y criterios de aceptación distintos.

IDs de votación


/66/votacion/{id}

      ├── parsing temporal

      └── POST viewTableVot.php


        filas nominales


        SQLite: votaciones + votos_nominales


 IDs de senador observados


 /66/senador/{id}


 SQLite: senadores disponibles

1. Selección de IDs

El spider de votaciones acepta dos modos de entrada:

ModoComportamiento
max_idItera range(1, max_id + 1).
idsUsa una lista explícita separada por comas.

Esto permite tanto crawls masivos como recrawls selectivos de casos concretos.

Ejemplos operativos documentados por el propio spider:

scrapy crawl votaciones
scrapy crawl votaciones -a max_id=50
scrapy crawl votaciones -a ids=347,891,2103,2789,3671,4256,4890

El recorrido autorizado del dataset final usó el rango [1, 5000]; la documentación del pipeline no inventa filtros adicionales sobre legislatura, partido o tipo de votación.

2. Request HTML de votación

Para cada ID, Scrapy solicita:

https://www.senado.gob.mx/66/votacion/{id}

La request incluye impersonación de navegador en meta para mitigar el WAF:

meta={"vote_id": vote_id, "impersonate": "chrome131"}

Sobre esa respuesta se intenta extraer metadata temporal:

  • legislatura;
  • año de ejercicio;
  • periodo;
  • fecha.

El parser revisa textos directos de <strong> y también fragmentos separados por <br>. Si la legislatura no aparece explícitamente, puede inferirse desde la fecha usando rangos conocidos de LX a LXVI.

3. Request AJAX vigente

Después del HTML inicial, el spider siempre consulta la vista AJAX nominal:

POST https://www.senado.gob.mx/66/app/votaciones/functions/viewTableVot.php

La request usa cuerpo form-urlencoded:

action=ajax&cell=1&order=DESC&votacion={id}&q=

y headers:

Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Referer: <url de la página de votación>

La decisión de si una votación tiene datos se toma en parse_votes(), con la respuesta AJAX como evidencia principal. Si no hay votos y tampoco se pudo resolver legislatura, el caso se descarta silenciosamente; si hay votos o metadata legislativa, se emite la votación.

4. Parsing de filas nominales

El parser recorre filas <tr> y exige al menos cuatro celdas. Para cada fila útil:

  1. toma el nombre del enlace en la segunda celda;
  2. limpia prefijos como Sen. / Senador / Senadora;
  3. reordena nombres con coma (Apellido, NombreNombre Apellido);
  4. extrae partido desde la tercera celda;
  5. une todos los nodos de texto de la cuarta celda para conservar votos multi-nodo;
  6. extrae senador_id desde el href de nombre o partido;
  7. emite VotoNominalItem solo si hay senador_id y nombre.

La normalización de espacios internos es parte del pipeline para evitar que detalles de HTML fragmentado contaminen la base. No se inventan valores cuando el portal no los entrega.

5. Persistencia conceptual

La persistencia usa un pipeline SQLite:

ItemOperación conceptual
VotacionItemINSERT OR REPLACE en votaciones.
VotoNominalItemINSERT OR IGNORE en votos_nominales, apoyado en restricción de unicidad.
SenadorItemINSERT OR REPLACE en senadores.

La base se escribe en lotes con commits periódicos. Esto permite recrawls selectivos sin duplicar votos nominales cuando la restricción de unicidad aplica.

6. Pipeline de perfiles

El spider de senadores parte de la base ya poblada:

SELECT DISTINCT vn.senador_id
FROM votos_nominales vn
LEFT JOIN senadores s ON vn.senador_id = s.id
WHERE s.id IS NULL
ORDER BY vn.senador_id

Con esa lista visita:

https://www.senado.gob.mx/66/senador/{id}

Si la página no contiene la sección de información esperada, el perfil se omite. Si existe, se guardan nombre, sexo inferido desde prefijo, tipo de elección, estado y URL.

7. Validación y límites

El pipeline no termina con una base “perfecta”; termina con una base auditable.

Los límites conocidos forman parte del resultado:

  • hay IDs de votación vacíos dentro del rango recorrido;
  • no todos los IDs presentes en votos tienen perfil disponible;
  • pueden existir partidos o votos vacíos;
  • el WAF puede introducir variabilidad de acceso;
  • el extractor no debe rellenar faltantes sin evidencia del portal.

La validación posterior lee, cuenta y señala. No debe borrar la historia de extracción ni esconder anomalías que son relevantes para entender la fuente.