A job scraping and AI-powered CV matching application that helps candidates find and apply to relevant job postings by automatically analyzing job descriptions and generating tailored CVs.
Jobbed is an intelligent job market tool that:
- Scrapes job postings from multiple career boards and job listing websites
- Detects ATS systems (Applicant Tracking Systems) used by employers
- Analyzes job fit using AI (Claude) to grade how well each posting matches your CV
- Generates tailored CVs using AI that rewrite your CV to emphasize skills relevant to each opportunity
- Manages data in a local SQLite database for tracking analyses and generated CVs
- Visualizes results through a Streamlit web interface for easy exploration and management
The application operates as a multi-stage pipeline, visualized in a Streamlit dashboard:
Page: 2_Scrape_boards.py
- Reads a list of job boards from
user_info/job_boards.csv - Scrapes each board using multiple detection strategies (JSON-LD, link pagination, API discovery, optional JS rendering)
- Detects which ATS system each posting uses
- Outputs raw postings to
temp/jobs.csv - Optional: Use Playwright to render JavaScript-heavy boards
Page: 3_Scrape_jobs.py
- Reads postings from
temp/jobs.csv - Fetches full job descriptions from posting URLs
- Applies first-pass filtering using keywords and blacklist
- Stores unfiltered jobs in SQLite
job_datatable - Outputs filtered postings to
temp/filtered_detailed_jobs.csv
Page: 4_AI_analysis.py
- Reads unanalyzed jobs from the database
- Sends each job description to Claude (Haiku model) with your CV
- Claude grades the posting (0-100) and provides analysis of fit
- Stores results in SQLite
ai_analysistable - Identifies postings within the past 24 hours awaiting analysis
Page: 5_CV_generation.py
- Reads analyzed jobs from
ai_analysistable - Sends job posting + analysis to Claude (Sonnet model) with your CV template
- Generates a tailored CV emphasizing relevant skills
- Stores generated CV as JSON in
generated_cvtable - Renders CV to DOCX format using your CV template
Page: Home.py
- Displays summary statistics from the last scrape
- Shows database metrics (stored jobs, completed analyses, pending analysis queue)
- Visualizes posting distribution by company and scraping strategy
Input: Job board URLs from user_info/job_boards.csv
- Columns:
company,url
Output: Raw job postings to temp/jobs.csv
- Columns:
company,title,url,place,via(scraping strategy),ats(detected system)
Scraped Fields:
- Company name
- Job title
- Posting URL
- Location
- Detection method (which strategy found it)
- ATS system type
-
Your CV (from
user_info/my_cv.md)- Full CV in Markdown format
- Included in every grade request for context
-
Grading Prompt (from
ai/grade-job.md)- Instructions on how Claude should evaluate the fit
- Scoring criteria
-
Job Description
- Full posting text from the scraped URL
Output: JSON with two fields
{
"adequation_grade": 75,
"depth_analysis": "This role emphasizes Python and Django, which aligns well with your backend experience..."
}-
Your CV (from
user_info/my_cv.md)- Template to guide structure and content
-
Generation Prompt (from
ai/generate-cv.md)- Instructions on how to tailor the CV
- Required sections and format
-
Job Analysis
- The
depth_analysisfrom the grading stage (what Claude already identified as important)
- The
-
Job Description
- Full posting text
Output: JSON with locale and full CV structure
{
"locale": "en",
"cv": {
"cv_introduction": "...",
"profile_text": "...",
"skills": [...],
"experiences": [...],
"education": [...]
}
}- All Claude API calls are made via the Claude CLI (
claudecommand) withCLAUDE_CODE_DISABLE_AUTO_MEMORYset, preventing context memory storage - Job descriptions and your CV are sent to Anthropic for analysis, not stored locally in a memory system
- Generated CVs are stored locally in SQLite as JSON
All configuration files are in user_info/ directory. Examples are provided with _example suffix.
List of career boards to scrape.
Format:
company,url
Company A,https://careers.companya.com
Company B,https://careers.companyb.com
LinkedIn,https://linkedin.com/jobs
Indeed,https://indeed.comRequired columns:
company: Display name for the boardurl: URL to scrape
Your CV in Markdown format. Used by Claude for grading jobs and as a template for generating tailored CVs.
Should include:
- Personal summary
- Key skills (grouped by competence area)
- Work experience (with dates, companies, locations, achievements)
- Education
Example structure:
# [Your Name]
## Summary
5 years of full-stack development experience...
## Skills
### Backend
- Python, Django, PostgreSQL
### Frontend
- React, TypeScript, CSS
## Experience
### Senior Software Engineer
**Company X** | Location | 2020-Present
- Achievement 1
- Achievement 2
## Education
### Bachelor's in Computer Science
University Name | 2015-2019A DOCX template for rendering generated CVs.
Setup:
- Download a template or create a new one in Microsoft Word
- Add placeholders like
[NAME],[SUMMARY],[EXPERIENCE]in the template - Save as
CV_placeholder.docxinuser_info/
The application will replace these placeholders with generated content.
Keywords for first-pass filtering. Jobs containing any of these keywords pass the first filter.
Format (one per line):
Python
Django
Backend
Full-stack
Remote
Keywords that disqualify a job. Postings containing these are filtered out.
Format (one per line):
PHP
Require relocation
Requires clearance
db/joblister.db- SQLite database (auto-created on first run)job_data- Scraped postingsai_analysis- AI grades and analysesgenerated_cv- Generated CVs for each analyzed posting
All in temp/ directory, created on each stage:
jobs.csv- Raw scraped postingsfirst_filtered_file.csv- After keyword/blacklist filteringdetailed_jobs.csv- With full descriptionsfiltered_detailed_jobs.csv- Final filtered listno_jobs.csv- Boards that returned no results
-
Python 3.11+
python --version # Must be 3.11 or higher -
Claude CLI installed
- Install: https://github.com/anthropics/claude-code
- Verify:
claude --version - Must have API access configured
-
Dependencies
pip install -r requirements.txt
-
Optional: Playwright (for JS rendering)
pip install playwright playwright install chromium
-
Clone/Navigate to project:
cd joblister -
Install dependencies:
pip install -r requirements.txt
-
Configure your profile:
- Copy examples:
cp user_info/*_example.* user_info/ - Edit
user_info/my_cv.mdwith your CV - Edit
user_info/job_boards.csvwith boards to scrape - Edit
user_info/keywords.txtwith relevant keywords - Edit
user_info/blacklist.txtwith disqualifying keywords - Replace
user_info/CV_placeholder.docxwith your template
- Copy examples:
-
Optional: Set up a CV template
- Create or download a CV template in Microsoft Word
- Add placeholders (the app will replace them)
- Save as
user_info/CV_placeholder.docx
streamlit run visualization/Home.pyThis launches the Streamlit web interface at http://localhost:8501 with five pages:
- Home - Dashboard overview
- 1_Boards_without_jobs - Boards that had no postings
- 2_Scrape_boards - Launch board scraping
- 3_Scrape_jobs - Fetch full descriptions and apply filters
- 4_AI_analysis - Grade jobs against your CV
- 5_CV_generation - Generate tailored CVs
- Open
Homepage to see current database state - Go to
2_Scrape_boardsand click "Scrape boards"- Adjust the limit if scraping only specific boards
- Enable "Render JS listings" for JavaScript-heavy sites
- Review scraped postings
- Go to
3_Scrape_jobsto fetch full descriptions and filter - Check
4_AI_analysispage to see pending jobs - Click "Analyze jobs" to run Claude grading on pending postings
- Once graded, go to
5_CV_generationto generate tailored CVs - Download generated CVs as DOCX files
pytest tests/Run specific test:
pytest tests/test_analysis.py -vWith coverage:
pytest tests/ --cov=.If you want to run stages manually outside the UI:
# Scrape boards only
python -m job_scraper.main_scraper
# Analyze jobs with Claude
python -m ai.main_analysis
# Generate CVs
python -m cv_generator.docx_genjobbed/
├── job_scraper/ # Board scraping & job fetching
│ ├── strategies/ # Detection strategies (JSON-LD, links, API, etc.)
│ ├── detector.py # ATS detection
│ ├── main_scraper.py # Board scraping orchestration
│ ├── post_scraper.py # Individual job fetching
│ └── paths.py # Filesystem paths
├── ai/ # AI integration (Claude)
│ ├── call_model.py # CLI wrapper for Claude
│ ├── analysis.py # Job grading
│ ├── cv_generation.py # CV tailoring
│ ├── grade-job.md # Claude grading prompt
│ └── generate-cv.md # Claude CV generation prompt
├── db/ # Database management
│ └── db_connection.py # SQLite schema & queries
├── cv_generator/ # CV rendering to DOCX
│ └── docx_gen.py # DOCX generation
├── visualization/ # Streamlit dashboard
│ ├── Home.py # Main dashboard
│ ├── pages/ # Dashboard pages (5 total)
│ └── common.py # Shared utilities
├── tests/ # Test suite
├── user_info/ # Configuration (gitignored)
│ ├── my_cv.md # Your CV
│ ├── job_boards.csv # Boards to scrape
│ ├── keywords.txt # Include keywords
│ ├── blacklist.txt # Exclude keywords
│ └── CV_placeholder.docx # CV template
├── db/ # Database (gitignored)
│ └── joblister.db # SQLite database
└── temp/ # Temp CSVs (gitignored)
Scraped job postings
- id (INTEGER PRIMARY KEY)
- company (TEXT)
- title (TEXT)
- description (TEXT)
- url (TEXT UNIQUE)
- place (TEXT)
- timestamp (DATETIME)Claude's job grades and analyses
- id (INTEGER PRIMARY KEY)
- adequation_grade (INT 0-100)
- depth_analysis (TEXT)
- ai_model (TEXT) - "haiku"
- job_id (FOREIGN KEY)Generated CVs for analyzed jobs
- id (INTEGER PRIMARY KEY)
- locale (TEXT) - "en", "es", "fr", "pt"
- cv (JSON) - Full CV structure
- job_id (FOREIGN KEY)
- ai_analysis_id (FOREIGN KEY)The application uses the following environment variable (set automatically):
CLAUDE_CODE_DISABLE_AUTO_MEMORY=1- Prevents Claude CLI from storing conversation memory during analyses (privacy)
If you're using a custom Claude installation, ensure the claude command is in your PATH.
# Install Claude CLI
pip install claude-cli
# Or configure if already installed
which claude- Run
2_Scrape_boardsfirst to populate the jobs list - Check that
user_info/job_boards.csvhas valid board URLs
- Ensure you have write permissions to the
db/directory - Delete
db/joblister.dbto reset the database - Check that
user_info/my_cv.mdexists and is readable
- Verify Claude CLI is installed:
claude --version - Check that you have API access:
claude -p "test" - Ensure
user_info/my_cv.mdis valid Markdown - Check
ai/grade-job.mdandai/generate-cv.mdexist
- Ensure you've graded jobs first (run AI analysis)
- Check that
user_info/CV_placeholder.docxexists - Verify your CV has the required sections (skills, experience, education)
- Check the board URL is still active and publicly accessible
- Some boards may require authentication
- Try enabling "Render JS listings" if the board is JavaScript-heavy
- Review
temp/no_jobs.csvfor error details
- First run: Can take 10-30 minutes depending on number of boards (network I/O bound)
- AI analysis: ~10 seconds per job (Claude API call overhead)
- CV generation: ~20 seconds per job (larger model, more computation)
- Database queries: Fast on databases <10K jobs; consider archiving old data if larger
To contribute improvements:
- Create a feature branch
- Run tests:
pytest tests/ - Ensure lint passes:
flake8 . - Submit a pull request
[Add your license here]
For issues, feature requests, or questions:
- GitHub Issues: [Link to repo]
- Email: [Your email]