Local-first desktop SEO crawler
Project description
xseo
A local-first desktop SEO crawler. Audit your site on your own machine — no cloud, no accounts, no data leaves your computer.
xseo is a desktop application that crawls a website, extracts on-page SEO signals, detects common issues and content duplication, and renders the results in a clean UI. Everything runs locally and persists to a single SQLite file under ~/.xseo/.
Features
- Live crawling with a real-time progress view and a threaded background worker that keeps the UI responsive.
- Polite by default — respects
robots.txtand applies a configurable per-request delay so you don't hammer the sites you audit. - On-page extraction of titles, meta descriptions, headings, canonicals, robots directives, internal/external links, and more via
selectolax. - Issue detection for missing/duplicate titles and descriptions, thin content, heading problems, broken links, and other common SEO defects.
- Duplicate content detection through content hashing and grouped read models.
- Sortable result tables for pages, issues, and duplicate groups, with a double-click page detail dialog.
- CSV export for every result view, so you can pipe findings into spreadsheets or other tools.
- Local persistence in SQLite at
~/.xseo/xseo.sqlite3. The last crawl is restored automatically on launch. - Clean architecture — domain, application, and adapter layers are strictly separated, with ports/adapters for HTTP, persistence, export, and the UI.
Screenshots
Configure a crawl, then watch progress stream in live:
| Control | Progress |
|---|---|
Review crawled pages, detected issues, and duplicate content groups:
| Pages | Duplicates |
|---|---|
Double-click any page for full detail — headings, links, redirects, and per-page issues:
Tech stack
- Python 3.12+
- PySide6 for the desktop UI
- httpx for HTTP fetching
- selectolax for fast HTML parsing
- SQLite for local storage
- pytest, hypothesis, and pytest-qt for unit, property-based, and UI tests
Install
Download a ready-to-run build (no Python needed)
Grab the latest .zip for your OS from the Releases page, unzip it, and run the xseo executable inside.
The builds are not code-signed yet, so the OS may warn you the first time:
- macOS: right-click the app → Open → Open (or
System Settings → Privacy & Security → Open Anyway).- Windows: on the SmartScreen prompt, click More info → Run anyway.
Install from PyPI (for Python users)
pipx install xseo # isolated, recommended
# or
pip install xseo
Then launch with xseo-ui. Requires Python 3.12 or newer.
From source
python3 -m pip install -e '.[test]'
Run
Launch the desktop UI:
xseo-ui
Or from the source tree:
python3 -m xseo.ui.app
Enter a URL, click Start Crawl, and watch the progress tab fill in. When the crawl finishes, browse pages, issues, and duplicate groups in their respective tabs. Double-click any page row for full detail, or export any view to CSV.
Verify
python3 -m compileall src tests
python3 -m pytest -q
The current suite has 145 tests covering domain logic, adapters, integration, property-based invariants, and UI smoke tests.
Project layout
src/xseo/
├── domain/ # entities, value objects, ports, validation, events
│ ├── crawler/ # frontier + crawl engine
│ ├── extraction/ # HTML extraction
│ ├── analysis/ # SEO issue detection
│ └── duplicates/ # content duplicate detection
├── application/ # services, commands, queries, read models
├── adapters/ # HTTP, persistence, export, background worker, event bridge
└── ui/ # PySide6 app, widgets, controller
Contributing
Contributions are welcome — see CONTRIBUTING.md for dev setup, how to run the checks (ruff + pytest), and the project conventions.
About
I built xseo because I needed it. I was starting a new project and wanted a fast way to scan it for SEO issues without uploading URLs to a third-party tool, paying for another subscription, or fighting a heavy web dashboard. I wanted something that ran on my desktop, was honest about what it found, and stored results in a file I owned — so I wrote it, and I'm sharing it in case it's useful to anyone else who wants a small, local, hackable SEO crawler.
This is an early prototype. It works end-to-end and I use it on my own projects, but expect rough edges. Issues and PRs are welcome.
Built by Yuri Silva — @yurisilvapi on X/Twitter.
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 xseo-0.2.0.tar.gz.
File metadata
- Download URL: xseo-0.2.0.tar.gz
- Upload date:
- Size: 46.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2460676387a37d96bbe20690670e86103deb0a143f24d5dcfb6d054a4c950735
|
|
| MD5 |
b1429f67e05dab84fc40d072d897ded1
|
|
| BLAKE2b-256 |
c0b03fa109255be45a4fa9ce1b2d2066903c13f8ea453fe91a1849ef1696e51c
|
Provenance
The following attestation bundles were made for xseo-0.2.0.tar.gz:
Publisher:
release-please.yml on yuripinto/xseo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xseo-0.2.0.tar.gz -
Subject digest:
2460676387a37d96bbe20690670e86103deb0a143f24d5dcfb6d054a4c950735 - Sigstore transparency entry: 1661045579
- Sigstore integration time:
-
Permalink:
yuripinto/xseo@50d24e43fe03131c8f551197ee53ea5e202a79a0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/yuripinto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@50d24e43fe03131c8f551197ee53ea5e202a79a0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file xseo-0.2.0-py3-none-any.whl.
File metadata
- Download URL: xseo-0.2.0-py3-none-any.whl
- Upload date:
- Size: 67.0 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 |
60df68cb99d7fae00083717ff3a35590fd49ba516fa2c2fd5365ff3c27418eab
|
|
| MD5 |
fea6fb1880dad045c0c15e17b0c15064
|
|
| BLAKE2b-256 |
8ff68d7e2d997f75268af33fe0df5d82474d83937a01a66f78c0754ce5c382e1
|
Provenance
The following attestation bundles were made for xseo-0.2.0-py3-none-any.whl:
Publisher:
release-please.yml on yuripinto/xseo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xseo-0.2.0-py3-none-any.whl -
Subject digest:
60df68cb99d7fae00083717ff3a35590fd49ba516fa2c2fd5365ff3c27418eab - Sigstore transparency entry: 1661045669
- Sigstore integration time:
-
Permalink:
yuripinto/xseo@50d24e43fe03131c8f551197ee53ea5e202a79a0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/yuripinto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@50d24e43fe03131c8f551197ee53ea5e202a79a0 -
Trigger Event:
push
-
Statement type: