ranq 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ranq-0.3.0/.gitignore +50 -0
- ranq-0.3.0/CHANGELOG.md +101 -0
- ranq-0.3.0/LICENSE +21 -0
- ranq-0.3.0/PKG-INFO +322 -0
- ranq-0.3.0/README.md +271 -0
- ranq-0.3.0/pyproject.toml +102 -0
- ranq-0.3.0/src/ranq/__init__.py +4 -0
- ranq-0.3.0/src/ranq/analysis/__init__.py +0 -0
- ranq-0.3.0/src/ranq/analysis/aeo.py +250 -0
- ranq-0.3.0/src/ranq/analysis/competitors.py +88 -0
- ranq-0.3.0/src/ranq/analysis/framework.py +62 -0
- ranq-0.3.0/src/ranq/analysis/keywords.py +539 -0
- ranq-0.3.0/src/ranq/analysis/pagetype.py +188 -0
- ranq-0.3.0/src/ranq/analysis/performance.py +154 -0
- ranq-0.3.0/src/ranq/analysis/structured_data.py +258 -0
- ranq-0.3.0/src/ranq/analysis/suggest.py +69 -0
- ranq-0.3.0/src/ranq/audit.py +89 -0
- ranq-0.3.0/src/ranq/checks/__init__.py +12 -0
- ranq-0.3.0/src/ranq/checks/aeo.py +179 -0
- ranq-0.3.0/src/ranq/checks/base.py +124 -0
- ranq-0.3.0/src/ranq/checks/crawl.py +385 -0
- ranq-0.3.0/src/ranq/checks/geo.py +285 -0
- ranq-0.3.0/src/ranq/checks/keywords.py +34 -0
- ranq-0.3.0/src/ranq/checks/onpage.py +431 -0
- ranq-0.3.0/src/ranq/checks/performance.py +114 -0
- ranq-0.3.0/src/ranq/checks/structured_data.py +239 -0
- ranq-0.3.0/src/ranq/checks/technical.py +757 -0
- ranq-0.3.0/src/ranq/cli.py +1208 -0
- ranq-0.3.0/src/ranq/config.py +170 -0
- ranq-0.3.0/src/ranq/crawler/__init__.py +0 -0
- ranq-0.3.0/src/ranq/crawler/crawler.py +305 -0
- ranq-0.3.0/src/ranq/crawler/parser.py +261 -0
- ranq-0.3.0/src/ranq/crawler/renderer.py +52 -0
- ranq-0.3.0/src/ranq/crawler/sitemaps.py +131 -0
- ranq-0.3.0/src/ranq/enrichers.py +67 -0
- ranq-0.3.0/src/ranq/fix/__init__.py +0 -0
- ranq-0.3.0/src/ranq/fix/bundle.py +199 -0
- ranq-0.3.0/src/ranq/generators/__init__.py +0 -0
- ranq-0.3.0/src/ranq/generators/framework.py +277 -0
- ranq-0.3.0/src/ranq/generators/headers.py +140 -0
- ranq-0.3.0/src/ranq/generators/llms.py +150 -0
- ranq-0.3.0/src/ranq/generators/markdown.py +216 -0
- ranq-0.3.0/src/ranq/generators/meta.py +131 -0
- ranq-0.3.0/src/ranq/generators/redirects.py +233 -0
- ranq-0.3.0/src/ranq/generators/robots.py +61 -0
- ranq-0.3.0/src/ranq/generators/schema.py +469 -0
- ranq-0.3.0/src/ranq/generators/sitemap.py +80 -0
- ranq-0.3.0/src/ranq/geo/__init__.py +0 -0
- ranq-0.3.0/src/ranq/geo/ai_bots.py +202 -0
- ranq-0.3.0/src/ranq/geo/analysis.py +79 -0
- ranq-0.3.0/src/ranq/geo/citation.py +125 -0
- ranq-0.3.0/src/ranq/geo/commoncrawl.py +61 -0
- ranq-0.3.0/src/ranq/geo/entity.py +363 -0
- ranq-0.3.0/src/ranq/geo/extras.py +85 -0
- ranq-0.3.0/src/ranq/geo/visibility.py +230 -0
- ranq-0.3.0/src/ranq/http.py +202 -0
- ranq-0.3.0/src/ranq/indexing/__init__.py +0 -0
- ranq-0.3.0/src/ranq/indexing/bing.py +61 -0
- ranq-0.3.0/src/ranq/indexing/diagnose.py +231 -0
- ranq-0.3.0/src/ranq/indexing/engines.py +85 -0
- ranq-0.3.0/src/ranq/indexing/gsc.py +177 -0
- ranq-0.3.0/src/ranq/indexing/indexnow.py +118 -0
- ranq-0.3.0/src/ranq/models.py +275 -0
- ranq-0.3.0/src/ranq/offpage/__init__.py +0 -0
- ranq-0.3.0/src/ranq/offpage/broken_links.py +85 -0
- ranq-0.3.0/src/ranq/offpage/checklist.py +129 -0
- ranq-0.3.0/src/ranq/offpage/mentions.py +140 -0
- ranq-0.3.0/src/ranq/py.typed +0 -0
- ranq-0.3.0/src/ranq/report/__init__.py +0 -0
- ranq-0.3.0/src/ranq/report/builder.py +223 -0
- ranq-0.3.0/src/ranq/report/compare.py +113 -0
- ranq-0.3.0/src/ranq/report/limitations.py +49 -0
- ranq-0.3.0/src/ranq/report/render.py +71 -0
- ranq-0.3.0/src/ranq/report/templates/report.html.j2 +273 -0
- ranq-0.3.0/src/ranq/report/templates/report.md.j2 +177 -0
- ranq-0.3.0/src/ranq/robots.py +189 -0
- ranq-0.3.0/src/ranq/sources.py +47 -0
- ranq-0.3.0/src/ranq/storage.py +210 -0
- ranq-0.3.0/src/ranq/urls.py +81 -0
- ranq-0.3.0/src/ranq/wizard.py +121 -0
- ranq-0.3.0/tests/conftest.py +42 -0
- ranq-0.3.0/tests/plugins/demo_plugin.py +21 -0
- ranq-0.3.0/tests/serve_site.py +120 -0
- ranq-0.3.0/tests/site/about.html +20 -0
- ranq-0.3.0/tests/site/blog/index.html +17 -0
- ranq-0.3.0/tests/site/blog/what-is-widget-testing.html +35 -0
- ranq-0.3.0/tests/site/contact.html +16 -0
- ranq-0.3.0/tests/site/dup-a.html +15 -0
- ranq-0.3.0/tests/site/dup-b.html +15 -0
- ranq-0.3.0/tests/site/index.html +32 -0
- ranq-0.3.0/tests/site/js-app.html +7 -0
- ranq-0.3.0/tests/site/missing-title.html +5 -0
- ranq-0.3.0/tests/site/noindex-page.html +10 -0
- ranq-0.3.0/tests/site/orphan.html +11 -0
- ranq-0.3.0/tests/site/products/widget.html +20 -0
- ranq-0.3.0/tests/site/resources.html +14 -0
- ranq-0.3.0/tests/site/robots.txt +7 -0
- ranq-0.3.0/tests/site/sitemap.xml +10 -0
- ranq-0.3.0/tests/test_analysis_network.py +94 -0
- ranq-0.3.0/tests/test_audit_integration.py +143 -0
- ranq-0.3.0/tests/test_compare.py +76 -0
- ranq-0.3.0/tests/test_fix_integration.py +106 -0
- ranq-0.3.0/tests/test_geo.py +196 -0
- ranq-0.3.0/tests/test_indexing_offpage.py +294 -0
- ranq-0.3.0/tests/test_plugins_and_report.py +60 -0
- ranq-0.3.0/tests/test_wizard.py +53 -0
- ranq-0.3.0/tests/unit/test_aeo_generators.py +105 -0
- ranq-0.3.0/tests/unit/test_config_scope.py +40 -0
- ranq-0.3.0/tests/unit/test_framework.py +45 -0
- ranq-0.3.0/tests/unit/test_headers.py +36 -0
- ranq-0.3.0/tests/unit/test_keywords.py +61 -0
- ranq-0.3.0/tests/unit/test_markdown.py +31 -0
- ranq-0.3.0/tests/unit/test_parsing.py +137 -0
- ranq-0.3.0/tests/unit/test_report_safety.py +85 -0
- ranq-0.3.0/tests/unit/test_robots.py +61 -0
- ranq-0.3.0/tests/unit/test_schema_merge.py +91 -0
- ranq-0.3.0/tests/unit/test_structured_data.py +111 -0
- ranq-0.3.0/tests/unit/test_unreachable.py +150 -0
ranq-0.3.0/.gitignore
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
.venv/
|
|
3
|
+
venv/
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
|
|
10
|
+
# Tooling caches
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.mypy_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# Ranq local data and outputs
|
|
18
|
+
# Your own site config (brand facts, competitors) - keep it local
|
|
19
|
+
/ranq.yaml
|
|
20
|
+
/ranq.yml
|
|
21
|
+
/.ranq.yaml
|
|
22
|
+
.ranq/
|
|
23
|
+
ranq-report/
|
|
24
|
+
ranq-fixes/
|
|
25
|
+
# Names from before the rename to Ranq
|
|
26
|
+
/seoforge.yaml
|
|
27
|
+
/seoforge.yml
|
|
28
|
+
/.seoforge.yaml
|
|
29
|
+
.seoforge/
|
|
30
|
+
seoforge-report/
|
|
31
|
+
seoforge-fixes/
|
|
32
|
+
*.sqlite
|
|
33
|
+
*.db
|
|
34
|
+
|
|
35
|
+
# Secrets and credentials (keys belong in environment variables)
|
|
36
|
+
.env
|
|
37
|
+
.env.*
|
|
38
|
+
*.pem
|
|
39
|
+
gsc-service-account*.json
|
|
40
|
+
*service-account*.json
|
|
41
|
+
credentials*.json
|
|
42
|
+
# Stray downloads such as Google Cloud key files in the repo root
|
|
43
|
+
/*.json
|
|
44
|
+
|
|
45
|
+
# Editors / OS
|
|
46
|
+
.idea/
|
|
47
|
+
.vscode/
|
|
48
|
+
*.swp
|
|
49
|
+
.DS_Store
|
|
50
|
+
Thumbs.db
|
ranq-0.3.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to SEOForge. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - 2026-10-12
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Renamed from SEOForge to Ranq.** The package, the command (`ranq audit ...`), the config
|
|
12
|
+
file (`ranq.yaml`), the output folders (`ranq-report/`, `ranq-fixes/`), the data folder
|
|
13
|
+
(`.ranq/`), the crawler User-Agent (`Ranq/<version>`, robots.txt group `Ranq`) and the
|
|
14
|
+
plugin entry-point group (`ranq.checks`) all use the new name. An existing `seoforge.yaml`
|
|
15
|
+
is still read when there is no `ranq.yaml`. The GitHub repository moved to
|
|
16
|
+
`Dreadonyx/ranq`; the old URL redirects.
|
|
17
|
+
- Built and maintained by [NodeKraft](https://nodekraft.tech): credited in the README, the
|
|
18
|
+
package metadata and the footer of every report.
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
- Report links built from crawled data (such as `sameAs` URLs) only allow `http(s)`; a
|
|
23
|
+
`javascript:` URL could otherwise run in the HTML report when clicked.
|
|
24
|
+
- HTML comments in generated meta tags cannot be closed early by crawled text.
|
|
25
|
+
- CI scans the full history for secrets (gitleaks) and audits dependencies (pip-audit);
|
|
26
|
+
workflow checkouts no longer keep the GitHub token; the PyPI publish action is pinned to a
|
|
27
|
+
commit; Dependabot keeps actions and dependencies updated; `SECURITY.md` explains private
|
|
28
|
+
reporting.
|
|
29
|
+
- `jinja2` minimum raised to 3.1.6.
|
|
30
|
+
|
|
31
|
+
## [0.2.0] - 2026-10-12
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **Per-page scores**: every crawled page gets a score from its own issues, shown in the CLI,
|
|
36
|
+
a Pages tab in the HTML report, the Markdown report and `report.json`.
|
|
37
|
+
- **Audit comparison**: each audit is compared with the previous report for the same site (or
|
|
38
|
+
`--compare old.json`): score change, fixed / new / changed issues and page-score changes.
|
|
39
|
+
Every run is archived in `<out>/history/`. `--no-compare` turns it off.
|
|
40
|
+
- **Guided `seoforge init`**: asks a few questions pre-filled from what the home page already
|
|
41
|
+
says (brand name, description, city, founding year, social profiles). `--example` writes
|
|
42
|
+
the commented template.
|
|
43
|
+
- **Framework detection** (Next.js App/Pages Router, Nuxt, Astro, SvelteKit, Remix, Gatsby,
|
|
44
|
+
Docusaurus, WordPress, Shopify, Wix, Squarespace, Webflow, Framer, Ghost, Hugo, Jekyll,
|
|
45
|
+
Eleventy). `fix` writes `FRAMEWORK.md` saying where each file goes; Next.js App Router sites
|
|
46
|
+
also get `app/robots.ts`, `app/sitemap.ts` and a safe JSON-LD component.
|
|
47
|
+
- New checks:
|
|
48
|
+
- `no-custom-domain` (high): the site runs on a platform subdomain (about 80 hosts,
|
|
49
|
+
including Vercel, Netlify, Hostinger, GitHub Pages, Cloudflare Pages, Wix and Webflow).
|
|
50
|
+
- `site-unreachable` (critical): no crawlable HTML page, with the reason (HTTP status,
|
|
51
|
+
fetch error, robots.txt, off-site redirect).
|
|
52
|
+
- `h1-hidden-in-html` (medium): the H1 is served invisible and revealed by JavaScript,
|
|
53
|
+
which delays Largest Contentful Paint.
|
|
54
|
+
- `og-url-mismatch` (medium): og:url points to another page.
|
|
55
|
+
- `jsonld-webpage-wrong-url` (medium): a WebPage node describes another URL (layout-level
|
|
56
|
+
markup copied to every page).
|
|
57
|
+
- `brand-description-inconsistent` (low): JSON-LD, llms.txt and config describe the brand
|
|
58
|
+
differently.
|
|
59
|
+
- `email-obfuscated` (low): Cloudflare Email Obfuscation hides the contact address.
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- **Health score**: site-wide issues (missing About page, llms.txt, robots.txt, Organization
|
|
64
|
+
markup, HTTPS...) now count in full instead of being diluted by page count. A site where
|
|
65
|
+
nothing could be crawled scores 0. Penalties map to the score on a diminishing curve
|
|
66
|
+
(`100 * e^(-penalty/100)`), so sites with many problems stay comparable instead of all
|
|
67
|
+
bottoming out at 0.
|
|
68
|
+
- `fix` builds JSON-LD on top of the site's existing markup and writes one `@graph` for the
|
|
69
|
+
shared layout plus one per page, so each file can replace what is there without losing
|
|
70
|
+
nodes.
|
|
71
|
+
- `fix` builds security headers from the live response headers and never weakens an existing
|
|
72
|
+
value (for example `X-Frame-Options: DENY`).
|
|
73
|
+
- `fix` keeps a hand-written `llms.txt` and only appends pages it does not link yet.
|
|
74
|
+
- Running from a folder whose `seoforge.yaml` describes another site no longer applies that
|
|
75
|
+
site's entity, competitors and keywords.
|
|
76
|
+
- Markdown mirrors and `llms-full.txt` skip hidden elements and keep words in flex/grid
|
|
77
|
+
layouts apart.
|
|
78
|
+
- Calls to action phrased as questions ("Ready to start?") are no longer treated as AEO
|
|
79
|
+
questions.
|
|
80
|
+
- Images sized by CSS (Next.js `fill`, Tailwind `w-N h-N`, inline width/height or
|
|
81
|
+
aspect-ratio) are no longer reported as missing dimensions.
|
|
82
|
+
- Cloudflare `/cdn-cgi/` endpoints are never crawled.
|
|
83
|
+
- GitHub Actions use checkout, setup-python and upload-artifact v7 (Node 24).
|
|
84
|
+
|
|
85
|
+
### Fixed
|
|
86
|
+
|
|
87
|
+
- The Organization generator dropped the address and founders already published on the site.
|
|
88
|
+
- The Wikidata draft lowercased brand names ("brightWorks") and repeated "<Name> is a".
|
|
89
|
+
- JSON-LD output now escapes `</` so crawled text cannot close the script tag.
|
|
90
|
+
|
|
91
|
+
## [0.1.0] - 2026-10-03
|
|
92
|
+
|
|
93
|
+
First release: robots-aware crawler, technical / on-page / performance audit, structured data
|
|
94
|
+
validation and generators, AEO and E-E-A-T checks, GEO (AI crawler policy, llms.txt, entity
|
|
95
|
+
tools, citation score, AI-visibility tracker), indexing (IndexNow, Search Console, Bing),
|
|
96
|
+
off-page helpers, `seoforge fix`, HTML / Markdown / JSON reports, Docker image, GitHub Action
|
|
97
|
+
and CI.
|
|
98
|
+
|
|
99
|
+
[0.3.0]: https://github.com/Dreadonyx/ranq/compare/v0.2.0...v0.3.0
|
|
100
|
+
[0.2.0]: https://github.com/Dreadonyx/ranq/compare/2e7659f...v0.2.0
|
|
101
|
+
[0.1.0]: https://github.com/Dreadonyx/ranq/tree/2e7659f
|
ranq-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dreadonyx
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ranq-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ranq
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Free, open-source CLI that audits and optimizes websites for SEO, AEO and GEO.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Dreadonyx/ranq
|
|
6
|
+
Project-URL: Repository, https://github.com/Dreadonyx/ranq
|
|
7
|
+
Project-URL: Issues, https://github.com/Dreadonyx/ranq/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/Dreadonyx/ranq/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: NodeKraft, https://nodekraft.tech
|
|
10
|
+
Author: NodeKraft
|
|
11
|
+
Maintainer: Dreadonyx
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: aeo,ai-search,audit,core-web-vitals,crawler,geo,indexnow,json-ld,llms.txt,search-console,seo,structured-data
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Environment :: Console
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Intended Audience :: Information Technology
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
|
|
27
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.11
|
|
30
|
+
Requires-Dist: httpx>=0.27
|
|
31
|
+
Requires-Dist: jinja2>=3.1.6
|
|
32
|
+
Requires-Dist: playwright>=1.44
|
|
33
|
+
Requires-Dist: pydantic>=2.7
|
|
34
|
+
Requires-Dist: pyyaml>=6.0
|
|
35
|
+
Requires-Dist: rich>=13.7
|
|
36
|
+
Requires-Dist: selectolax>=1.0
|
|
37
|
+
Requires-Dist: typer>=0.12
|
|
38
|
+
Requires-Dist: yake>=0.4.8
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
41
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
43
|
+
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
44
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
45
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
46
|
+
Provides-Extra: google
|
|
47
|
+
Requires-Dist: google-auth>=2.29; extra == 'google'
|
|
48
|
+
Provides-Extra: ml
|
|
49
|
+
Requires-Dist: sentence-transformers>=3.0; extra == 'ml'
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# Ranq
|
|
53
|
+
|
|
54
|
+
[](https://github.com/Dreadonyx/ranq/actions/workflows/ci.yml)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
**Free, open-source CLI that audits and optimizes websites for search engines (SEO), answer
|
|
59
|
+
engines (AEO) and AI / generative engines (GEO) - and submits them to every index it
|
|
60
|
+
legitimately can.**
|
|
61
|
+
|
|
62
|
+
No paid APIs. No accounts required for the core audit. MIT licensed.
|
|
63
|
+
|
|
64
|
+
Built and maintained by [NodeKraft](https://nodekraft.tech), a security-first digital agency in
|
|
65
|
+
Chennai. Want the findings fixed for you? [Talk to us](https://nodekraft.tech).
|
|
66
|
+
|
|
67
|
+
> **Honesty first.** No tool can guarantee rankings, indexing of every page or keyword, or that
|
|
68
|
+
> any AI system will know, cite or recommend your site. Ranq maximizes *eligibility,
|
|
69
|
+
> discoverability and citation likelihood*, labels every heuristic as an estimate, cites the
|
|
70
|
+
> guideline behind every finding, and never uses spam tactics. See
|
|
71
|
+
> [What Ranq cannot do](#what-ranq-cannot-do).
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
ranq audit https://example.com # crawl + prioritized HTML/Markdown/JSON report
|
|
75
|
+
ranq fix https://example.com # robots.txt, sitemap, llms.txt, JSON-LD, redirects...
|
|
76
|
+
ranq diagnose https://example.com/page # why isn't this URL indexed?
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
*The HTML report for the bundled test site, which has planted problems. Open the full
|
|
82
|
+
[example report](docs/example-report/report.html) (download it and open it in a browser) or
|
|
83
|
+
read the [Markdown version](docs/example-report/report.md). Changes between versions are in
|
|
84
|
+
the [CHANGELOG](CHANGELOG.md).*
|
|
85
|
+
|
|
86
|
+
## What it does
|
|
87
|
+
|
|
88
|
+
| Area | Highlights |
|
|
89
|
+
|---|---|
|
|
90
|
+
| **Crawler** | Async, robots.txt-aware (RFC 9309 wildcards + longest match), per-host rate limit and `Crawl-delay`, redirect-chain capture, SQLite cache, resumable (`--resume`), optional JavaScript rendering with Playwright, site graph (click depth, inlinks, orphans) |
|
|
91
|
+
| **Technical SEO** | Status codes, broken links, redirect chains/loops, soft 404s (site-wide probe + heuristics), HTTPS/HSTS/mixed content, www/http canonicalization, robots.txt validity, sitemap validity and consistency, canonicals, meta robots / X-Robots-Tag, hreflang, viewport, lang, images, compression, security headers, duplicate and near-duplicate content (SimHash), sites on a platform subdomain instead of their own domain (~80 hosts: Vercel, Netlify, Hostinger, GitHub Pages, Wix...), unreachable sites |
|
|
92
|
+
| **Performance** | Core Web Vitals field data (CrUX via the free PageSpeed Insights API) and lab data (PageSpeed / local Lighthouse CLI); H1 served invisible until JavaScript runs (entrance animations that delay LCP); images sized by width/height, CSS aspect-ratio or utility classes |
|
|
93
|
+
| **On-page & keywords** | Titles, meta descriptions, headings, thin content, JS-only content, anchors, internal linking, Open Graph (including og:url pointing to another page); keyword extraction (TF-IDF, YAKE, n-grams), rule-based intent, topic clustering (local sentence-transformers if installed, else TF-IDF), cannibalization, competitor content gap, opt-in cached Google Autosuggest ideas, keyword-to-page map |
|
|
94
|
+
| **Structured data** | JSON-LD extraction and validation (required/recommended properties, nested entities, ISO dates, `@graph`, FAQ visibility policy, WebPage nodes describing another URL); generators for Organization, WebSite (+SearchAction), WebPage, Article, Product, FAQPage, HowTo, BreadcrumbList, Person with `sameAs`, merged with the markup already on the site into one `@graph` for the layout and one per page |
|
|
95
|
+
| **AEO** | Question-style headings (calls to action such as "Ready to start?" excluded), 40-60 word answer blocks, answer drafts trimmed from *your own* text, FAQ/definition/steps/table detection, E-E-A-T checks (author, author page, dates, citations, About/Contact/Privacy) |
|
|
96
|
+
| **GEO** | Per-bot robots.txt policy for 15 AI crawlers/tokens (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-SearchBot, Claude-User, PerplexityBot, Perplexity-User, Google-Extended, Applebot-Extended, CCBot, Bytespider, Meta-ExternalAgent, Amazonbot, cohere-ai); `/llms.txt`, `/llms-full.txt` and Markdown mirrors; entity fact sheet; sameAs graph, brand-consistency checklist and a check that markup, llms.txt and config describe the brand the same way; contact email hidden by Cloudflare obfuscation; Wikidata search + QuickStatements draft; citation-friendliness score; Common Crawl presence; AI-visibility tracker (SQLite) |
|
|
97
|
+
| **Indexing** | Sitemap generator, IndexNow (key file + verified batch submission of changed URLs), Google Search Console API (sitemap submit, quota-aware URL Inspection), Bing Webmaster API, engine coverage table, "why isn't this indexed?" diagnostic |
|
|
98
|
+
| **Off-page** | Opportunity checklist, curated directory list, robots-aware broken-link finder with Wayback lookups, unlinked brand-mention finder (Hacker News + Wikipedia APIs), strict anti-spam policy |
|
|
99
|
+
| **Reporting** | Prioritized issues (Critical/High/Medium/Low) with impact, effort, exact fix, source and evidence; health score where site-wide issues count in full, plus a score for every page; framework detection; standalone HTML dashboard + Markdown + JSON; 30/60/90-day plan; "what this tool cannot do" section |
|
|
100
|
+
| **`ranq fix`** | Ready-to-paste robots.txt, sitemap.xml, llms.txt (extends a hand-written one instead of replacing it), JSON-LD, meta tags, security headers (built from your live headers, never weakening them) and redirects for nginx, Apache, Vercel, Netlify and Cloudflare. A FRAMEWORK.md says where each file goes in the detected stack, and Next.js App Router sites also get `app/robots.ts`, `app/sitemap.ts` and a JSON-LD component. Applies to a *local* directory only after a diff preview and per-file confirmation |
|
|
101
|
+
|
|
102
|
+
## Install
|
|
103
|
+
|
|
104
|
+
Requires Python 3.11+.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pipx install ranq # or: pip install ranq
|
|
108
|
+
pipx install git+https://github.com/Dreadonyx/ranq # latest main, before a PyPI release
|
|
109
|
+
# Optional extras:
|
|
110
|
+
playwright install chromium # JavaScript rendering (--render)
|
|
111
|
+
pipx inject ranq google-auth # Search Console via service account (pip: "ranq[google]")
|
|
112
|
+
pipx inject ranq sentence-transformers # local semantic clustering (large: PyTorch)
|
|
113
|
+
npm install -g lighthouse # local Lighthouse lab runs (--lighthouse)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
From source:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
git clone https://github.com/Dreadonyx/ranq && cd ranq
|
|
120
|
+
python -m venv .venv && source .venv/bin/activate
|
|
121
|
+
pip install -e ".[google]"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Or with Docker:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
docker build -t ranq . # add --build-arg WITH_BROWSER=1 for --render
|
|
128
|
+
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work" ranq audit https://example.com
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Quickstart
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
ranq init --site https://example.com # a few questions, pre-filled from your home page (optional)
|
|
135
|
+
ranq audit https://example.com # report in ./ranq-report/report.html
|
|
136
|
+
ranq audit https://example.com --render --max-pages 1000 --competitor https://rival.com/page
|
|
137
|
+
ranq fix https://example.com # files in ./ranq-fixes (see INDEX.md)
|
|
138
|
+
ranq fix https://example.com --apply-to ./public # diff + confirm, then write locally
|
|
139
|
+
ranq diagnose https://example.com/missing-page --crawl-pages 200
|
|
140
|
+
ranq geo bots https://example.com # which AI crawlers can access the site
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Commands
|
|
144
|
+
|
|
145
|
+
| Command | Purpose |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `audit URL` | Crawl and report. Options: `--max-pages`, `--depth`, `--delay`, `--concurrency`, `--render`, `--resume`, `--cached`, `--check-external`, `--competitor URL` (repeatable), `--suggest`, `--lighthouse`, `--no-pagespeed`, `--format html/md/json`, `--fail-on critical/high/medium/low`, `--compare old.json` / `--no-compare` (by default each audit is compared with the previous report for the same site; every run is also kept in `<out>/history/`) |
|
|
148
|
+
| `fix URL` | Generate fix files. `--ai-policy allow-all/search-only/deny-all`, `--redirect-map old_new.csv`, `--apply-to DIR [--yes]` |
|
|
149
|
+
| `diagnose URL` | Robots, status, redirects, noindex, canonical, rendering, soft 404, sitemap, internal links, optional `--gsc` URL Inspection |
|
|
150
|
+
| `geo bots / llms / entity / commoncrawl` | AI crawler access table; llms.txt + mirrors only; entity + Wikidata; Common Crawl presence |
|
|
151
|
+
| `visibility init / add / prompts / log / import / run / report` | AI-visibility tracker (see below) |
|
|
152
|
+
| `index engines / indexnow-key / indexnow / gsc-sitemap / gsc-inspect / bing` | Submissions. All ask for confirmation; all support `--dry-run` |
|
|
153
|
+
| `offpage checklist / policy / broken-links / mentions` | Ethical link-building helpers |
|
|
154
|
+
|
|
155
|
+
### AI-visibility tracker
|
|
156
|
+
|
|
157
|
+
AI assistants have no free, permitted API for "does this assistant mention my brand", and
|
|
158
|
+
automating their consumer apps violates their terms. So the tracker is a log:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
ranq visibility init # prompts from entity.name/category/city
|
|
162
|
+
ranq visibility add "best kanban app for small teams"
|
|
163
|
+
ranq visibility prompts
|
|
164
|
+
ranq visibility log 3 perplexity --mentioned --cited-url https://example.com/guide
|
|
165
|
+
ranq visibility import answers.csv # prompt,engine,date,mentioned,cited_url,position,notes
|
|
166
|
+
ranq visibility run gemini --cmd "python my_gemini_free_tier.py {prompt}" # your own permitted tool
|
|
167
|
+
ranq visibility report --json visibility.json
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`run` executes *your* command once per prompt (no shell; `{prompt}` is passed as one
|
|
171
|
+
argument) and detects brand mentions and links to your domain in its output.
|
|
172
|
+
|
|
173
|
+
## Configuration (`ranq.yaml`)
|
|
174
|
+
|
|
175
|
+
Every field is optional; `ranq init` writes one by asking a few questions (pre-filled from what your home page already says), and `ranq init --example` writes a commented file with every setting. API keys are read from
|
|
176
|
+
environment variables, never from the YAML.
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
site: https://example.com
|
|
180
|
+
crawl: {max_pages: 500, delay_seconds: 0.5, concurrency: 4, render_js: false, exclude: ["/wp-admin"]}
|
|
181
|
+
entity:
|
|
182
|
+
name: Example Inc.
|
|
183
|
+
description: One factual sentence about what you do.
|
|
184
|
+
category: project management software
|
|
185
|
+
city: Chennai
|
|
186
|
+
founded: "2019"
|
|
187
|
+
same_as: [https://github.com/example, https://www.linkedin.com/company/example]
|
|
188
|
+
geo:
|
|
189
|
+
ai_bot_preset: search-only # allow-all | search-only | deny-all | custom
|
|
190
|
+
ai_bots: {CCBot: deny} # per-bot overrides
|
|
191
|
+
competitors: [https://competitor.example/pricing]
|
|
192
|
+
seed_keywords: [project management]
|
|
193
|
+
report: {output_dir: ranq-report, fail_on: never}
|
|
194
|
+
disabled_checks: [deep-pages]
|
|
195
|
+
plugins: [mycompany.ranq_checks]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`search-only` (the default) allows AI search and user-initiated fetchers - so answers can cite
|
|
199
|
+
and link you - and blocks training crawlers. Allowed bots inherit your `*` rules, so private
|
|
200
|
+
paths stay private.
|
|
201
|
+
|
|
202
|
+
## Free API keys
|
|
203
|
+
|
|
204
|
+
All optional; the audit works without any of them.
|
|
205
|
+
|
|
206
|
+
**PageSpeed Insights (Core Web Vitals)** - works without a key at low volume. For a higher
|
|
207
|
+
free quota: Google Cloud Console → create a project → *APIs & Services* → enable **PageSpeed
|
|
208
|
+
Insights API** → *Credentials* → **Create API key**. Then `export PAGESPEED_API_KEY=...`.
|
|
209
|
+
|
|
210
|
+
**Google Search Console** (sitemap submission, URL Inspection):
|
|
211
|
+
1. Verify your site in [Search Console](https://search.google.com/search-console).
|
|
212
|
+
2. Google Cloud Console → enable the **Google Search Console API**.
|
|
213
|
+
3. Either: create a **service account**, download its JSON key, `export
|
|
214
|
+
GSC_SERVICE_ACCOUNT_FILE=/path/key.json`, `pip install -e ".[google]"`, and add the
|
|
215
|
+
service account's email under Search Console → *Settings → Users and permissions* (Full
|
|
216
|
+
permission; Owner if sitemap submission is refused).
|
|
217
|
+
Or: `gcloud auth application-default login --scopes=https://www.googleapis.com/auth/webmasters,https://www.googleapis.com/auth/cloud-platform`
|
|
218
|
+
then `export GSC_ACCESS_TOKEN=$(gcloud auth application-default print-access-token)`.
|
|
219
|
+
4. Quotas (enforced locally): URL Inspection 2,000/day and 600/minute per property.
|
|
220
|
+
|
|
221
|
+
**Bing Webmaster Tools**: verify the site at [bing.com/webmasters](https://www.bing.com/webmasters)
|
|
222
|
+
(you can import from Search Console) → *Settings → API access* → generate an **API key** →
|
|
223
|
+
`export BING_WEBMASTER_API_KEY=...`.
|
|
224
|
+
|
|
225
|
+
**IndexNow** (Bing, Yandex, Seznam.cz, Naver, Yep): `ranq index indexnow-key`, upload
|
|
226
|
+
the generated `<key>.txt` to your site root, `export INDEXNOW_KEY=<key>`, then
|
|
227
|
+
`ranq index indexnow https://example.com` (submits only URLs whose content changed).
|
|
228
|
+
|
|
229
|
+
## GitHub Action
|
|
230
|
+
|
|
231
|
+
Audit every deploy and upload the report as an artifact (see `docs/github-action.md`):
|
|
232
|
+
|
|
233
|
+
```yaml
|
|
234
|
+
- uses: Dreadonyx/ranq@main
|
|
235
|
+
with:
|
|
236
|
+
url: https://example.com
|
|
237
|
+
fail-on: critical
|
|
238
|
+
pagespeed-api-key: ${{ secrets.PAGESPEED_API_KEY }}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Plugins
|
|
242
|
+
|
|
243
|
+
Checks are plain functions registered with `@check`. Ship them in any module and list it under
|
|
244
|
+
`plugins:` or expose it via the `ranq.checks` entry-point group. See
|
|
245
|
+
[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) and `tests/plugins/demo_plugin.py`.
|
|
246
|
+
|
|
247
|
+
## Ethics
|
|
248
|
+
|
|
249
|
+
- Obeys robots.txt on every request and redirect hop (including competitor, mention and
|
|
250
|
+
broken-link checks), rate-limits per host and honours `Crawl-delay`.
|
|
251
|
+
- Never cloaks, generates doorway pages, fake reviews, or spam links; never scrapes search
|
|
252
|
+
result pages or consumer AI apps.
|
|
253
|
+
- Never uses Google's Indexing API (it is only for JobPosting and BroadcastEvent pages).
|
|
254
|
+
- Generated markup and copy use only data from your pages or `ranq.yaml`; anything unknown
|
|
255
|
+
is left as a TODO rather than invented.
|
|
256
|
+
- Never modifies a site without a diff preview and confirmation, and only in a local directory.
|
|
257
|
+
|
|
258
|
+
## Assumptions
|
|
259
|
+
|
|
260
|
+
- "Submit to every index" means the legitimate channels that exist: Google Search Console,
|
|
261
|
+
Bing Webmaster Tools, IndexNow, sitemaps and robots.txt. Other engines either use those
|
|
262
|
+
indexes or offer no submission (listed by `ranq index engines`).
|
|
263
|
+
- Keyword volumes are omitted rather than estimated, because no free, reliable source exists.
|
|
264
|
+
- The default AI crawler policy is `search-only`; change it in `ranq.yaml`.
|
|
265
|
+
- The crawler identifies itself as `Ranq/<version>` and matches robots.txt groups for
|
|
266
|
+
`Ranq` (falling back to `*`).
|
|
267
|
+
- Response cache is written on every crawl but only read with `--resume` / `--cached`, so a
|
|
268
|
+
normal audit always reflects the live site.
|
|
269
|
+
- Thresholds such as "title > 60 characters", "under 200 words" or "40-60 word answers" are
|
|
270
|
+
common heuristics, not search engine rules; findings based on them are labelled estimates.
|
|
271
|
+
|
|
272
|
+
## What Ranq cannot do
|
|
273
|
+
|
|
274
|
+
- Guarantee rankings, indexing of any page, or traffic.
|
|
275
|
+
- Make any LLM know, cite or recommend your site; it can only make your content easier to
|
|
276
|
+
retrieve, quote and attribute.
|
|
277
|
+
- Provide search volume, keyword difficulty or a full backlink index (use Search Console and
|
|
278
|
+
Bing Webmaster Tools "Links" reports for your real backlinks).
|
|
279
|
+
- Enforce robots.txt: bots that ignore it must be blocked at the server/CDN/WAF.
|
|
280
|
+
- Promise anything from llms.txt: it is a community proposal that major AI providers have not
|
|
281
|
+
confirmed using.
|
|
282
|
+
- Bring back FAQ/HowTo rich results in Google (restricted/removed in 2023), or the sitelinks
|
|
283
|
+
search box (retired in 2024).
|
|
284
|
+
- Render pages exactly like Googlebot; headless Chromium is a close approximation.
|
|
285
|
+
- Measure Core Web Vitals field data for low-traffic URLs (CrUX has no data for them).
|
|
286
|
+
|
|
287
|
+
## Roadmap
|
|
288
|
+
|
|
289
|
+
- Incremental crawls driven by sitemap `lastmod` and HTTP conditional requests
|
|
290
|
+
- Log-file analysis (real Googlebot/AI-bot hits vs. robots policy)
|
|
291
|
+
- Search Console Search Analytics import (queries, CTR, position) into the keyword map
|
|
292
|
+
- More rich-result validators (Event, JobPosting, Recipe, VideoObject details) and SHACL export
|
|
293
|
+
- Multi-language keyword extraction (stopword lists beyond English)
|
|
294
|
+
- Trend charts across the audit history (`<out>/history/`)
|
|
295
|
+
- WordPress / static-site generator integrations for `fix --apply-to`
|
|
296
|
+
|
|
297
|
+
## Development
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
pip install -e ".[dev]" && playwright install chromium
|
|
301
|
+
pre-commit install
|
|
302
|
+
pytest -q # spins up a local test site with planted issues
|
|
303
|
+
ruff check src tests && ruff format --check src tests && mypy
|
|
304
|
+
python tests/serve_site.py 8765 & ranq audit http://127.0.0.1:8765 --no-pagespeed --delay 0
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Releasing
|
|
308
|
+
|
|
309
|
+
1. Bump `__version__` in `src/ranq/__init__.py` and add a section to `CHANGELOG.md`.
|
|
310
|
+
2. Commit, then `git tag v<version> && git push origin v<version>`.
|
|
311
|
+
3. The Release workflow checks the tag matches the version, runs the tests, builds, creates the
|
|
312
|
+
GitHub Release with the changelog notes and the sdist/wheel attached, and publishes to PyPI.
|
|
313
|
+
|
|
314
|
+
One-time PyPI setup (no API token needed): on pypi.org, *Your projects -> Publishing -> Add a
|
|
315
|
+
new pending publisher* with owner `Dreadonyx`, repository `ranq`, workflow `release.yml`,
|
|
316
|
+
environment `pypi`. Then in the GitHub repository settings add the variable
|
|
317
|
+
`PUBLISH_TO_PYPI` = `true` (*Settings -> Secrets and variables -> Actions -> Variables*).
|
|
318
|
+
Until then the PyPI job is skipped and releases go to GitHub only.
|
|
319
|
+
|
|
320
|
+
## License
|
|
321
|
+
|
|
322
|
+
MIT - see [LICENSE](LICENSE).
|