Crawl a website and audit SEO, accessibility, performance & broken links — zero dependencies.
Project description
██████╗ ██████╗ ██╗ ██╗ ██╗
██╔══██╗██╔════╝ ██║ ██║ ██║
██████╔╝██║ ███╗██║ ██║ ██║
██╔═══╝ ██║ ██║██║ ██║ ██║
██║ ╚██████╔╝███████╗╚██████╔╝
╚═╝ ╚═════╝ ╚══════╝ ╚═════╝
PGLU · site-doctor
🩺 site-doctor
Crawl any website and audit SEO · Accessibility · Performance · Broken links — from your terminal.
An agency-grade website health checker in pure Python. Get a scored report in seconds, output JSON for dashboards, or gate your CI/CD on a minimum health score.
site-doctor — https://example.com
Pages audited: 12 · Links checked: 140
Health Score: 82.4/100
Grade: B — good
SEO ████████████████░░ 88.0/100
Accessibility ██████████████░░░░ 79.0/100
Performance ██████████████████ 96.0/100
Links ████████████████░░ 88.0/100
Top issues
[error] a11y: 4/9 images missing an alt attribute. (7 pages)
[warn] seo: Meta description is 182 chars (>160). (3 pages)
...
Broken links (2)
✗ 404 https://example.com/old-page
📖 New here? Read the full User Manual (USAGE.md) — install, first audit, every option, and troubleshooting (including the "command not recognized" fix).
✨ What it checks
| Category | Examples |
|---|---|
| 🔍 SEO | title length, meta description, single H1, canonical, Open Graph, viewport, JSON-LD, thin content |
| ♿ Accessibility | <html lang>, image alt text, vague/empty link text, form-field labels, heading hierarchy |
| ⚡ Performance | measured page load time, HTML size, script/stylesheet count, large inline JS, image count |
| 🔒 Security | HTTPS, security headers (HSTS, CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy), mixed content |
| 🔗 Broken links | parallel HTTP checks of internal and external links, with status codes |
| 🗂️ Site-wide | duplicate <title> and duplicate meta descriptions across pages |
Plus a weighted overall health score and letter grade.
Smart link checking: genuinely broken links (4xx/5xx) hurt your score, but links that
merely can't be verified — bot-blocked (LinkedIn 999, social WAFs) or unreachable —
are reported separately and don't unfairly tank your score. Crawling is concurrent,
and ANSI colors work even in legacy Windows consoles.
🚀 Install
pip install site-doctor
Or run from source (no install): PYTHONPATH=src python -m sitedoctor <url>
🕹️ Usage
site-doctor example.com # full audit (crawls up to 20 pages)
site-doctor https://mysite.com --max-pages 50 --max-depth 3
site-doctor mysite.com --json > report.json # machine-readable
site-doctor mysite.com --no-external # skip external link checks (faster)
site-doctor mysite.com --fail-under 80 # exit 1 if score < 80 (great for CI)
site-doctor mysite.com --sitemap # audit every URL in /sitemap.xml
site-doctor mysite.com --html report.html # client-ready HTML report
site-doctor mysite.com --md report.md # Markdown report
site-doctor mysite.com --csv issues.csv # CSV of all issues & links (spreadsheets)
Options
| Flag | Description |
|---|---|
--max-pages N |
Max pages to crawl (default 20) |
--max-depth N |
Max crawl depth (default 2) |
--max-links N |
Max links to check (default 200) |
--timeout N |
Per-request timeout in seconds (default 15) |
--sitemap |
Audit URLs from sitemap.xml instead of crawling |
--sitemap-url URL |
Use an explicit sitemap URL (implies --sitemap) |
--html PATH |
Also write a self-contained HTML report |
--md PATH |
Also write a Markdown report |
--csv PATH |
Also write a CSV of all issues & links |
--junit PATH |
Also write a JUnit XML report (for CI test dashboards) |
--ignore CODE |
Suppress a check by code (repeatable), e.g. --ignore og-missing |
--min-seo / --min-a11y / --min-performance / --min-security / --min-links N |
Per-category CI gates |
--no-robots |
Ignore robots.txt (it's respected by default) |
--no-external |
Don't check external links |
--fail-under N |
Exit non-zero if health score < N |
--json / --no-color |
JSON output / plain text |
--no-banner |
Hide the PGLU banner |
🤖 Gate your deploys on site health (GitHub Actions)
- name: Audit site health
run: |
pip install site-doctor
site-doctor https://your-site.com --fail-under 80 --min-security 70 --junit site.xml
The build fails if the overall score — or any category you gate on — regresses below
your threshold. Emit --junit XML for your CI's test dashboard.
🧱 How it works
Pure standard library: a breadth-first crawler (urllib), an html.parser-based
extractor, pure-function check rules, weighted scoring, and a ThreadPoolExecutor-powered
parallel link checker. No browser, no third-party packages.
crawler → parser → checks (seo/a11y/perf) → scoring → report → render
links checker ↗
📦 Publishing (maintainers)
Publishing to PyPI is a manual action via
publish.yml using Trusted Publishing (no API
tokens). One-time setup on pypi.org: Your account → Publishing →
Add a pending publisher with project site-doctor, owner aashishbharti04, repo
site-doctor, workflow publish.yml, environment pypi. Then run the workflow from the
Actions tab.
🛠️ Develop
pip install -e ".[dev]"
pytest -q
🤝 Contributing
New checks, output formats, or fixes are welcome — see CONTRIBUTING.md.
📄 License
MIT © Aashish Bharti
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file site_doctor-0.3.1.tar.gz.
File metadata
- Download URL: site_doctor-0.3.1.tar.gz
- Upload date:
- Size: 28.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4201358e1c5621170b4d2a772cb28a3303df5217d4de4818c5d2815a2e9ff18f
|
|
| MD5 |
e705abb37d9d8ee85d78ebf846b80f74
|
|
| BLAKE2b-256 |
ca45ce5775d4278ca844ece003fc290efc051e8021e515ee86bc0d3bb183620b
|
Provenance
The following attestation bundles were made for site_doctor-0.3.1.tar.gz:
Publisher:
publish.yml on aashishbharti04/site-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
site_doctor-0.3.1.tar.gz -
Subject digest:
4201358e1c5621170b4d2a772cb28a3303df5217d4de4818c5d2815a2e9ff18f - Sigstore transparency entry: 1837728086
- Sigstore integration time:
-
Permalink:
aashishbharti04/site-doctor@a60e0a2cedbb7c94db23755b2c9eef360c4bbea8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/aashishbharti04
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a60e0a2cedbb7c94db23755b2c9eef360c4bbea8 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file site_doctor-0.3.1-py3-none-any.whl.
File metadata
- Download URL: site_doctor-0.3.1-py3-none-any.whl
- Upload date:
- Size: 26.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14b30dfe2897fa388dc2a1918e81445f07e936a75ce71a42994e5d178c05963e
|
|
| MD5 |
92934875f38806bd5268df0854dfb8b7
|
|
| BLAKE2b-256 |
e3591d014c012822933d903d14104feda0df06e54391bbc1a582636b91a3cab4
|
Provenance
The following attestation bundles were made for site_doctor-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on aashishbharti04/site-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
site_doctor-0.3.1-py3-none-any.whl -
Subject digest:
14b30dfe2897fa388dc2a1918e81445f07e936a75ce71a42994e5d178c05963e - Sigstore transparency entry: 1837728153
- Sigstore integration time:
-
Permalink:
aashishbharti04/site-doctor@a60e0a2cedbb7c94db23755b2c9eef360c4bbea8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/aashishbharti04
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a60e0a2cedbb7c94db23755b2c9eef360c4bbea8 -
Trigger Event:
workflow_dispatch
-
Statement type: