教程演示如何用Python结合开源库,对简历PDF提取文本并基于技能匹配度打分排序,全程无需付费平台,代码可直接复用于内部招聘流程自动化。
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.
Most companies rely on expensive, black-box hiring platforms that cost thousands per month and often lack transparency. By building your own, you get:
Plus, building this project teaches you core NLP concepts: text extraction, tokenization, and semantic similarity—skills that are gold in the data science world.
Our screener will follow a three-step pipeline:
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.
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.
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.
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']}")
Skill Extraction: We pull potential tech terms from both the job description and resume. In a production app, you'd use spaCy or 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.
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.
You've built a working screener. Now, level it up:
You don't need a $50,000 hiring platform to find great candidates. With Python and a few open-source libraries, you can build a resume screener that works as well—or better—than expensive enterprise solutions.
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.