Aplicación de escritorio para convertir el export de datos de Instagram — cientos de ficheros JSON que nadie abre a mano — en un dashboard visual: actividad en el tiempo, mensajes, conexiones y limpieza de quién no te sigue de vuelta.
Interfaz Fluent Design (PySide6 + QFluentWidgets) con tema claro/oscuro automático, gráficos nativos y todo el procesamiento pesado corriendo en segundo plano para que la UI nunca se congele.
Empezó como un script de consola de una sola función (
NonFollowersChecker: compararfollowers_1.htmlvsfollowing.json). Esa función se conserva intacta — pestaña Limpieza dentro de Conexiones — pero hoy la aplicación analiza y visualiza el export completo, no solo followers/following.
- Por qué este proyecto
- Características
- Stack técnico
- Arquitectura
- Ingeniería de rendimiento
- Empezando
- Tests
- Estructura del repositorio
- Roadmap
- Privacidad
- Licencia
Instagram te deja pedir "toda tu información", pero te la entrega como un zip de cientos de ficheros JSON con esquemas inconsistentes entre sí y, en cuentas con años de actividad, gigabytes de datos — nadie los abre a mano. Este proyecto existe para convertir ese export en algo que se puede mirar y entender en un par de minutos, sin subir tus datos a ningún servicio de terceros: todo corre en local.
Además de ser una herramienta que uso yo mismo, es el sitio donde he ido metiendo intencionadamente problemas de ingeniería reales — un fichero de más de 200 MB que hay que parsear sin congelar la UI, una tabla que tiene que renderizar cientos de miles de filas, un formato de texto que Instagram entrega mal codificado — en vez de datos de ejemplo bonitos y pequeños.
- Resumen (dashboard): conteos totales por categoría, un gráfico de actividad en el tiempo (likes + comentarios + stories + mensajes por mes) y un ranking de las cuentas con las que más interactúas.
- Actividad: sub-pestañas de Me gusta, Comentarios y Stories, cada una con su gráfico mensual y su tabla de cuentas más frecuentes.
- Mensajes: lista de conversaciones (inbox y broadcast) con conteo de mensajes y última actividad; al seleccionar una, un mini-gráfico de actividad y un visor de contenido paginado (remitente, fecha, texto).
- Conexiones:
- Limpieza — la función original: compara un export de seguidores con
uno de seguidos y muestra quién sigues que no te sigue de vuelta, con
exportación a
.txt/.csv. - Crecimiento — evolución en el tiempo de followers/following y conteos de amigos íntimos, bloqueados y solicitudes.
- Limpieza — la función original: compara un export de seguidores con
uno de seguidos y muestra quién sigues que no te sigue de vuelta, con
exportación a
- Explorador de archivos: árbol con todos los ficheros del export (recorrido recursivo, soporta la estructura real del zip de Instagram), con una tabla paginada que interpreta el contenido de cualquier JSON, sea cual sea su esquema interno — el respaldo genérico para categorías sin dashboard propio (anuncios, seguridad, información personal, apps...).
- Dos formatos soportados para el análisis de no-seguidores: el HTML legado de Instagram y el JSON que exporta actualmente la plataforma — se detectan automáticamente por extensión.
- Corrección de mojibake: Instagram exporta el JSON con los caracteres
no-ASCII mal codificados (
"Germán"en vez de"Germán"); se corrige en toda la aplicación antes de mostrarse. - Cache en disco: los agregados de analítica (especialmente sobre
ficheros grandes, como un
liked_posts.jsonde cientos de MB) se cachean en.cache/analytics/y solo se recalculan si el fichero fuente cambia. - Tablas Model/View: el explorador y las tablas de analítica usan
QTableViewen vez de un widget por celda, para que ficheros con decenas de miles de filas sigan yendo fluidos. - Enlaces clicables y avatares por iniciales: cualquier URL detectada se muestra como hipervínculo real; cada usuario se muestra con un avatar circular de color estable generado a partir de su nombre (Instagram no incluye foto de perfil en sus exports).
- Interfaz Fluent Design, con tema claro/oscuro automático según el de Windows, y gráficos (QtCharts) que se adaptan al mismo tema.
| Capa | Tecnología | Por qué |
|---|---|---|
| UI | PySide6 + QFluentWidgets | Fluent Design nativo de Windows 11, sin reinventar componentes |
| Gráficos | PySide6.QtCharts |
Incluida en PySide6-Addons — cero dependencias nuevas, tema claro/oscuro coherente con el resto de la UI |
| Concurrencia | QThread (AnalysisWorker, AggregateWorker) |
El parsing de ficheros de cientos de MB nunca bloquea el hilo de UI |
| Parsing | json (stdlib) + BeautifulSoup4 |
Un ProfileParser (Strategy) por formato de export |
| Persistencia | Cache JSON en disco, invalidada por mtime/tamaño |
Evita recalcular sobre ficheros que no han cambiado |
| Tests | pytest, fixtures sintéticas |
Domino testeado sin Qt y sin depender de datos personales |
El proyecto separa el dominio (parsing y análisis, sin dependencias de UI) de la presentación (PySide6 + QFluentWidgets), siguiendo los principios SOLID:
core/ # Dominio puro — testeable sin Qt
├── models.py # Profile, FileEntry, TableResult
├── exceptions.py # Errores de dominio (EaseYourIGStatsError)
├── text.py # fix_mojibake
├── parsers/ # Strategy: un parser por formato de export
│ ├── base.py # interfaz ProfileParser
│ ├── html_parser.py # export HTML legado
│ ├── json_parser.py # export JSON actual
│ └── factory.py # elige parser según la extensión del fichero
├── services/
│ ├── non_followers_analyzer.py # following - followers (Limpieza)
│ └── files_repository.py # listado y normalización paginada de files/
└── analytics/ # Agregados para el dashboard y las paginas
├── cache.py # AnalyticsCache: cache en disco por mtime/tamaño
├── categories.py # localizacion de ficheros por sufijo de ruta
├── models.py # TimeSeriesPoint, RankedAccount
├── timeseries.py # bucket_by_month, cumulative
├── ranking.py # top_accounts
└── aggregators/
├── connections.py # followers/following + metadatos relacionados
├── interactions.py # likes, comentarios, stories
├── messages.py # conversaciones + visor de contenido
└── dashboard.py # combina todo lo anterior para Resumen
ui/ # Presentación — PySide6 + QFluentWidgets
├── app.py # arranque de QApplication y tema Fluent
├── main_window.py # FluentWindow con navegacion (5 secciones)
├── pages/
│ ├── dashboard_page.py # Resumen
│ ├── activity_page.py # Actividad (likes/comentarios/stories)
│ ├── messages_page.py # Mensajes
│ ├── connections_page.py # Conexiones (envuelve AnalysisPage)
│ ├── analysis_page.py # Limpieza de no-seguidores (sin cambios)
│ └── files_explorer_page.py # Explorador generico
├── widgets/ # componentes visuales reutilizables
│ ├── avatar.py # avatar circular por iniciales
│ ├── link_label.py # hiperenlace para la tabla de Limpieza
│ ├── link_delegate.py # hiperenlace pintado en QTableView (sin widget por celda)
│ ├── table_model.py # QAbstractTableModel generico
│ ├── charts.py # helpers sobre QtCharts
│ ├── summary_card.py # tarjeta de numero + caption
│ └── layout_utils.py # clear_layout
└── workers.py # QThread: AnalysisWorker y AggregateWorker
tests/ # Tests del dominio (sin Qt), contra fixtures sinteticas
└── fixtures/ # HTML/JSON sintéticos de prueba (no datos reales)
Por qué esta separación:
- SRP — cada módulo tiene una única responsabilidad: parsear, analizar, explorar ficheros o presentar. Cambiar la UI no afecta a la lógica de negocio, y viceversa.
- OCP — soportar un nuevo formato de export o una nueva categoría de analítica solo requiere una nueva clase/agregador, sin tocar el resto.
- LSP —
HtmlProfileParseryJsonProfileParserimplementan la misma interfaz y son intercambiables para quien los use. - ISP —
ProfileParsersolo exponesupports()/parse(), sin métodos irrelevantes para sus consumidores. - DIP —
NonFollowersAnalyzer, los agregadores y las páginas de la UI dependen de abstracciones (ParserFactory,AnalyticsCache), no deBeautifulSoupni dejsondirectamente.
Un export real de Instagram con varios años de actividad puede rondar los
2 GB en más de 500 ficheros, con un solo liked_posts.json de más de
200 MB (100 000+ likes). Con eso como caso real de prueba, no como
hipótesis:
- Antes: una tabla
QTableWidgetcon unQWidget(QLabel) por cada celda que pareciera un enlace. Con miles de filas, cada apertura de fichero se notaba; con el fichero de 100k+ filas, dejaba de ser usable. - Después:
QTableView+QAbstractTableModelgenérico, con unQStyledItemDelegateque pinta los hipervínculos directamente sobre la vista — cero widgets por fila — más paginación (500 filas por página) en el explorador de ficheros. - Cache en disco por huella de fichero (
AnalyticsCache): cada agregado del dashboard se computa una vez y se guarda en.cache/analytics/, con clave de invalidación por tamaño +mtimedel fichero fuente (nunca por releerlo, para no anular el propio ahorro). Resultado sobre el fichero de 200+ MB: ~2 s en frío, ~0.4 s en caliente. - Todo el parsing pesado corre en un
QThread(AggregateWorkergenérico, reutilizado por las cinco páginas), nunca en el hilo de UI — la ventana permanece responsive incluso mientras se procesa el fichero más grande del export.
- Python 3.10 o superior (probado con 3.14).
- Windows 10/11 (la interfaz está pensada para el lenguaje visual Fluent de Windows 11; funciona en otros sistemas pero sin la integración nativa).
# Crear y activar un entorno virtual (si no existe ya .venv)
python -m venv .venv
.venv\Scripts\Activate.ps1
# Instalar dependencias
pip install -r requirements.txt- En Instagram: Ajustes → Tu actividad → Descargar tu información.
- Solicita el export en formato JSON (recomendado) o HTML.
- Descomprime el
.ziprecibido. Dentro encontrarás carpetas comoconnections/followers_and_following/,your_instagram_activity/likes/,your_instagram_activity/messages/, etc. - Copia esa carpeta (o su contenido) dentro de
files/en este proyecto. No hace falta aplanar la estructura: la aplicación la recorre de forma recursiva y encuentra cada fichero conocido esté donde esté.
python main.pySe abrirá la ventana principal con cinco secciones: Resumen, Actividad, Mensajes, Conexiones (con la pestaña Limpieza de no-seguidores) y Explorador de archivos.
pip install pytest # dependencia de desarrollo, no de runtime
python -m pytest tests/Los tests cubren la capa de dominio (parsers, analizador de no-seguidores,
FilesRepository, fix_mojibake, resolución de categorías, cache y cada
agregador) usando fixtures sintéticas en tests/fixtures/ — nunca contra la
carpeta files/ real, que es el export personal de quien ejecute la app.
EaseYourIGStats/
├── main.py # entrypoint de la aplicación
├── requirements.txt
├── core/ # dominio (parsers, servicios, analytics)
├── ui/ # interfaz gráfica
├── tests/ # tests del dominio
├── files/ # tus exports de Instagram (.json o .html) — ignorado por git
└── .cache/ # cache en disco de analítica (se regenera sola) — ignorado por git
El proyecto se construyó a propósito como un MVP en fases. Próximas categorías del export con dashboard propio, hoy accesibles vía el Explorador de archivos genérico:
- Anuncios e intereses (temas sugeridos, anunciantes, categorías usadas para segmentarte)
- Seguridad (histórico de logins, dispositivos, IPs)
- Información personal y preferencias
- Exportar el Resumen completo a PDF/imagen
Tus datos de Instagram nunca salen de tu máquina: no hay llamadas de red,
ni telemetría, ni servicios en la nube. La carpeta files/ (tu export
real) y .cache/ (los agregados calculados sobre él) están en
.gitignore — nunca se suben al repositorio.
Añade aquí la licencia que prefieras (por ejemplo, MIT) si vas a publicar el repositorio.