Build an AI-Powered Resume Screener with Python
tags: python, ai, automation, tools
Build an AI-Powered Resume Screener with Python
Imagine a hiring manager drowning in 500 PDF resumes for a single junior developer role. They spend hours scrolling, copying, and pasting text into spreadsheets, only to realize they missed the perfect candidate buried on page 43. That’s not just inefficient; it’s a war on talent. But what if you could automate the first pass of screening, instantly ranking candidates based on how well their skills match the job description? You can, and you can build it today with Python, a few open-source libraries, and a clear strategy.
This isn’t about replacing human judgment. It’s about freeing recruiters to focus on the why behind a hire, rather than the what of keyword matching. Let’s build a practical, AI-powered resume screener that extracts text, analyzes skill fit, and returns a match score—no enterprise budget required.
Why Build Your Own Screener?
Most companies rely on expensive, black-box hiring platforms that cost thousands per month and often lack transparency. By building your own, you get:
- Full control over matching logic (keywords, semantic similarity, or LLM-based analysis)
- Zero cost beyond your existing Python environment
- Customizability for niche roles (e.g., matching “Rust” skills for a blockchain role)
- Privacy by keeping candidate data on your own servers
Plus, building this project teaches you core NLP concepts: text extraction, tokenization, and semantic similarity—skills that are gold in the data science world.
The Core Architecture
Our screener will follow a three-step pipeline:
- Text Extraction: Pull readable text from PDF resumes.
- Skill Matching: Compare resume skills against job description requirements.
- Scoring & Ranking: Calculate a match percentage and rank candidates.
We’ll start with a semantic similarity approach using sentence-transformers, which is far more robust than simple keyword matching. It understands that “Python” and “Python programming” are related, even if the exact words differ.
Step 1: Setting Up the Environment
First, install the necessary libraries. We’ll use PyMuPDF for fast PDF text extraction and sentence-transformers for AI-powered matching.
pip install PyMuPDF sentence-transformers
We also need to download a pre-trained model. The all-MiniLM-L6-v2 model is lightweight, fast, and excellent for semantic similarity tasks.
Step 2: Extracting Text from PDF Resumes
Resumes come in various formats, but PDF is the most common. Scanned PDFs are tricky, but for standard digital PDFs, PyMuPDF (imported as fitz) works brilliantly.
Here’s a reusable function to extract text:
import fitz # PyMuPDF
def extract_text_from_pdf(file_path: str) -> str:
"""
Extracts clean text from a PDF resume file.
"""
doc = fitz.open(file_path)
text = ""
for page in doc:
text += page.get_text()
doc.close()
return text.strip()
# Example usage
resume_text = extract_text_from_pdf("candidate_resume.pdf")
print(resume_text[:200]) # Print first 200 characters to verify
This function loops through every page, grabs the text, and returns a clean string. If you encounter scanned PDFs later, you can plug in OCR using pytesseract, but for now, this handles 90% of modern resumes.
Step 3: Semantic Skill Matching with AI
Simple keyword matching fails when a resume says “worked with React” instead of “React experience.” Semantic similarity solves this by measuring the meaning behind the words.
We’ll use the sentence-transformers library to compute similarity scores between job requirements and resume content.
from sentence_transformers import SentenceTransformer
import re
# Load the pre-trained model
model = SentenceTransformer('all-MiniLM-L6-v2')
def extract_skills(text: str) -> list[str]:
"""
Extract potential keywords/skills from text using simple regex.
In a production system, you'd use a dedicated NLP library like spaCy.
"""
# Simple heuristic: extract capitalized words or common tech terms
# This is a placeholder; real apps use better NLP
tech_keywords = ["python", "java", "react", "sql", "docker", "aws", "kubernetes", "ml", "ai"]
found_skills = [kw for kw in tech_keywords if kw in text.lower()]
return found_skills
def calculate_match_score(job_desc: str, resume_text: str) -> dict:
"""
Calculates semantic similarity between job description and resume.
Returns matched skills, missing skills, and a match score.
"""
# Extract skills from job description (simplified for demo)
job_skills = extract_skills(job_desc)
resume_skills = extract_skills(resume_text)
# Generate embeddings for job and resume skills
job_embeddings = model.encode(job_skills)
resume_embeddings = model.encode(resume_skills)
matched_skills = []
missing_skills = []
# Calculate similarity for each job skill
for i, job_skill in enumerate(job_skills):
similarities = model.similarity(job_embeddings[i], resume_embeddings)
best_match_idx = similarities.argmax()
best_score = similarities[best_match_idx]
# Threshold: 0.35 is a good starting point for semantic similarity
if best_score > 0.35:
matched_skills.append({
"skill": job_skill,
"score": round(best_score, 2)
})
else:
missing_skills.append(job_skill)
# Calculate overall match score
match_score = len(matched_skills) / len(job_skills) if job_skills else 0
return {
"matched_skills": matched_skills,
"missing_skills": missing_skills,
"match_score": round(match_score, 2)
}
# Example usage
job_description = "We need a Python developer with React, SQL, and AWS experience."
resume = "candidate_resume.pdf"
resume_text = extract_text_from_pdf(resume)
result = calculate_match_score(job_description, resume_text)
print(f"Match Score: {result['match_score']*100}%")
print(f"Matched: {result['matched_skills']}")
print(f"Missing: {result['missing_skills']}")
How It Works
-
Skill Extraction: We pull potential tech terms from both the job description and resume. In a production app, you’d use
spaCyor a custom dictionary for better accuracy. - Embedding: The model converts each skill into a vector (a list of numbers representing meaning).
- Similarity Check: We compare vectors using cosine similarity. If the score is above 0.35, we consider it a match.
- Scoring: The final score is the percentage of job skills found in the resume.
This approach is practical and actionable. You can run this script against a folder of 1,000 resumes in seconds, ranking them instantly.
Step 4: Scaling to Bulk Screening
To screen hundreds of resumes, wrap the logic in a loop:
import os
def screen_bulk_resumes(folder_path: str, job_desc: str) -> list[dict]:
results = []
for filename in os.listdir(folder_path):
if filename.endswith(".pdf"):
path = os.path.join(folder_path, filename)
text = extract_text_from_pdf(path)
score_data = calculate_match_score(job_desc, text)
results.append({
"filename": filename,
"score": score_data["match_score"],
"matched": score_data["matched_skills"],
"missing": score_data["missing_skills"]
})
# Sort by score descending
return sorted(results, key=lambda x: x["score"], reverse=True)
# Usage
top_candidates = screen_bulk_resumes("./resumes", job_description)
for candidate in top_candidates[:5]:
print(f"{candidate['filename']}: {candidate['score']*100}% match")
This gives you a ranked list of the top 5 candidates, ready for human review.
What’s Next? Going Beyond the Basics
You’ve built a working screener. Now, level it up:
-
Add NLP: Use
spaCyto extract named entities (e.g., “Google”, “2023”) for better context. - LLM Integration: Swap the semantic model for GPT-4 or Gemini to generate natural language summaries like “Strong Python background but lacks cloud experience.”
- Web UI: Wrap this in a Flask app (as shown in [1][7]) so recruiters can upload PDFs and see results instantly.
-
OCR Support: Add
pytesseractfor scanned resumes.
Start Screening Today
You don’t need a $50,000 hiring platform to
If you found this helpful, consider buying me a coffee ☕ — it keeps these articles coming!
Also check out my AI tools collection: AI 次元世界 — free AI tools for developers.
Top comments (0)