
Tech Stack:
Project TL;DR — Python LinkedIn Cover Letter Generator
A Python CLI tool to scrape LinkedIn job postings and generate fully personalized HTML cover letters — all without relying on LLMs (but future-ready to add them).
1. Python CLI Tool
Run a single command:
python main.py "https://www.linkedin.com/jobs/view/<JOB_ID>"
…and instantly generate a tailored HTML cover letter.
2. Web Scraping LinkedIn
Uses Selenium (headless Chrome) and BeautifulSoup4 to navigate LinkedIn job pages like:
https://www.linkedin.com/jobs/view/<JOB_ID>
and extract:
- Job title
- Company name
- Location
- Full job description
Example code snippet:
driver.get(job_url)
title = driver.find_element(By.CSS_SELECTOR, "h1.top-card-layout__title").text
company = driver.find_element(By.CSS_SELECTOR, "a.topcard__org-name-link").text
location = driver.find_element(By.CSS_SELECTOR, "span.topcard__flavor--bullet").text
desc = driver.find_element(By.CSS_SELECTOR, "div.description__text").text
Includes error handling to detect:
- Wrong URLs (e.g. search results instead of a single job view)
- Missing elements if LinkedIn’s page layout changes
3. NLP Keyword Extraction
Leverages spaCy to parse job descriptions, extract noun phrases (skills and responsibilities), and intelligently select the top 2–3 keywords to personalize your letter.
Example snippet:
import spacy
nlp = spacy.load("en_core_web_sm")
def extract_keywords(text, max_k=6):
doc = nlp(text)
chunks = [chunk.text.strip() for chunk in doc.noun_chunks]
seen, keywords = set(), []
for c in chunks:
c_lower = c.lower()
if c_lower not in seen:
seen.add(c_lower)
keywords.append(c)
if len(keywords) >= max_k:
break
return keywords
4. Smart HTML Templating
Fills a clean, responsive template.html with a single [LETTER_BODY] placeholder. Automatically generates five paragraphs that integrate the extracted keywords into a natural, professional narrative.
Example HTML snippet:
<div class="letter-body">
[LETTER_BODY]
</div>
And smart text filling:
paras.append(
f"<p>I am excited to apply for the <strong>{job}</strong> position at "
f"<strong>{company}</strong>. With my background in {top_keys[0]} and a "
f"passion for innovative solutions, I’m confident I can help drive your team’s success.</p>"
)
5. Robustness & Debugging
- Validates input URLs
- Rejects non-job pages (e.g. search result pages)
- Handles missing page elements with graceful error messages
- Prints debug output to verify scraped data and template replacements
6. Minimal Dependencies
A lightweight project built entirely with:
- Python 3.x
- Selenium
- BeautifulSoup4
- spaCy
No bulky frameworks required. Easy to install in a virtual environment:
pip install requests beautifulsoup4 selenium spacy webdriver-manager
python -m spacy download en_core_web_sm
7. Instant HTML Output
Creates cover_letter.html that:
- Opens in any browser
- Can be printed as a PDF
- Preserves fonts, styling, and layout for a polished professional look
8. Saves Time
Automates the time-consuming process of writing unique cover letters for every job application — transforming hours of work into a single command-line execution.
9. LLM Future Roadmap
Currently avoids LLMs for transparency, simplicity, and full offline capability. However, a future version will optionally integrate LLM APIs (like OpenAI GPT-4) to generate even richer, dynamically written cover letters for enhanced personalization.
Example Workflow
Run your CLI tool:
python main.py "https://www.linkedin.com/jobs/view/4244763442"
Output:
âś… Smart letter saved to cover_letter.html
🎉 Done! Open cover_letter.html to review.
Open your browser and admire your custom cover letter — beautifully styled and tailored to the job.
Note: Always pass a full LinkedIn job-view URL (not search results) to ensure proper scraping:
âś… Correct:
https://www.linkedin.com/jobs/view/<JOB_ID>❌ Incorrect:
https://www.linkedin.com/jobs/search/...
This project was built to reduce reliance on LLMs and keep the logic fully transparent and explainable—while saving serious time on personalized job applications.