Automatizacion de postulaciones laborales. Recibe links de ofertas, las analiza contra tu perfil, genera cartas personalizadas con IA y las postula automaticamente en sistemas ATS.
URL +-----------+ +----------+ +---------+ +-----------+ +---------+
ofertas --------->| Scraper |--->| Matcher |--->| Cover |--->| Apply ATS |--->| Enviado |
+-----------+ +----------+ +---------+ +-----------+ +---------+
| | | |
extrae titulo, compara vs genera carta llena formulario
empresa, stack perfil y CV personalizada y sube CV
(DeepSeek) (Playwright)
ApplyJob/
├── main.py # orquestador del pipeline
├── profile/
│ ├── cv.md # perfil del candidato en español
│ ├── cv_en.md # perfil del candidato en ingles
│ ├── CV_Mickaell_Moran.pdf # CV en PDF (español)
│ ├── CV_Mickaell_Moran_EN.pdf # CV en PDF (ingles)
│ └── cv_template.md # template de perfil
├── src/
│ ├── scraper.py # extrae informacion de ofertas desde URLs
│ ├── profile.py # carga y parsea el CV/perfil
│ ├── matcher.py # calcula compatibilidad oferta vs perfil
│ ├── cover.py # genera carta via DeepSeek Flash (es/en via param lang)
│ ├── apply_ats.py # auto-postulacion en ATS via Playwright (soporta lang=en)
│ ├── letter_to_pdf.py # convierte cartas .txt a PDF via python-docx + LibreOffice
│ ├── inbox.py # lector IMAP para boletines
│ └── sender.py # envia correo via Gmail SMTP
├── samples/ # boletines de ofertas guardados
├── output/
│ └── cartas/ # cartas generadas (gitignored: contienen PII)
├── run_batch.py # batch: scrapea y genera cartas
├── run_manual.py # batch: usa descripciones manuales
├── run_today.py # genera cartas para la shortlist de la fecha actual
├── test_apply.py # test del modulo apply_ats
└── .env # variables de entorno (no versionado)
| Componente | Tecnologia |
|---|---|
| Lenguaje | Python 3.12+ |
| IA generativa | DeepSeek Flash via Anthropic SDK |
| Web scraping | httpx + BeautifulSoup |
| ATS Automation | Playwright (headless Chromium) |
| Correo | Gmail SMTP + Google App Password |
| Entorno | Linux Mint |
git clone https://github.com/Mickaell22/ApplyJob.git
cd ApplyJob
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
playwright install chromiumCrear archivo .env en la raiz del proyecto:
DEEPSEEK_API_KEY=sk-...
GMAIL_USER=tu-correo@gmail.com
GMAIL_APP_PASSWORD=xxxx xxxx xxxx xxxx
CV_PATH=./profile/CV_Mickaell_Moran.pdf
CV_PATH_EN=./profile/CV_Mickaell_Moran_EN.pdf
# Datos del candidato (apply_ats.py los usa para llenar formularios ATS)
CANDIDATE_NAME=...
CANDIDATE_PHONE=...
CANDIDATE_LOCATION=...
CANDIDATE_CITY=...
CANDIDATE_COUNTRY=...
CANDIDATE_LINKEDIN=...
CANDIDATE_GITHUB=...
CANDIDATE_WEBSITE=
- DeepSeek API key: platform.deepseek.com -> API Keys
- Gmail App Password: https://myaccount.google.com/apppasswords (requiere 2FA activado)
# Scrapea + match + genera cartas desde URLs reales
python run_batch.py
# Usa descripciones manuales (cuando el scraping falla)
python run_manual.py
# Genera cartas para la shortlist de ofertas de la fecha actual
python run_today.pyLas cartas en ingles se generan con cover.generate(job, cv, lang="en") (ej. Canonical).
python src/letter_to_pdf.py output/cartas/07_Canonical_SWE_Python_Cloud.txt
# genera output/cartas/07_Canonical_SWE_Python_Cloud.pdffrom src.apply_ats import run
jobs_with_letters = [
{"job": {"title": "...", "company": "...", "url": "..."}, "carta": "cover letter text..."}
]
# Dry run (llena formulario pero NO envia)
result = run(jobs_with_letters, dry_run=True)
# Envio real
result = run(jobs_with_letters, dry_run=False)| Plataforma | Estado | Empresas |
|---|---|---|
| Workable | ✅ Funcional | Platzi, Canonical, Loft |
| Greenhouse | ✅ Implementado | Canonical |
| Teamtailor | ❌ No funcional (cookie wall) | Global66, Loft |
| Ashby | 📋 Pendiente | Addi |
| Workday | 📋 No viable (login/anti-bot) | Amadeus, Oracle, BBVA |
Nota: Workday y Oracle Cloud exigen crear cuenta con login + verificacion email, por lo que no son automatizables de forma confiable; se postulan manualmente.
# Una oferta
python main.py https://juniorjobs.short.gy/LbO1f3
# Varias ofertas
python main.py https://juniorjobs.short.gy/LbO1f3 https://juniorjobs.short.gy/gjno2F
# Desde stdin
echo "url1 url2" | python main.pyfrom src import profile, scraper, matcher, cover, sender
cv = profile.load()
job = scraper.fetch_job("https://juniorjobs.short.gy/LbO1f3")
match = matcher.score(job, cv)
if match["fit"] in ("alta", "media"):
carta = cover.generate(job, cv)
sender.send(
to_email="hr@empresa.com",
subject=f"Candidatura: {job['title']}",
body=carta,
attachment="./profile/cv.pdf",
)Editar profile/cv.md con tu informacion:
- Stack tecnologico
- Experiencia laboral
- Educacion y certificaciones
- Preferencias (remoto, presencial, ubicacion)
El matcher usa este archivo para calcular la compatibilidad con cada oferta.
El sistema evalua cada oferta contra tu perfil y asigna un score:
- Alta (>40%): procede a generar carta y enviar
- Media (20-40%): procede con precaucion
- Baja (<20%): descartada automaticamente
MIT