Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -60,6 +60,20 @@ def format_seo_result(result):
|
|
| 60 |
output += f"- H1: {page.get('h1_count', 0)}, H2: {page.get('h2_count', 0)}, H3: {page.get('h3_count', 0)}\n"
|
| 61 |
output += f"- Images: {page.get('total_images', 0)} (missing alt: {page.get('missing_alt_tags', 0)})\n"
|
| 62 |
output += f"- Internal/External links: {page.get('internal_links', 0)}/{page.get('external_links', 0)}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
if page.get('ai_suggestions'):
|
| 64 |
output += f"- AI Suggestions: {page['ai_suggestions'][:200]}...\n"
|
| 65 |
output += "\n"
|
|
@@ -146,4 +160,166 @@ with gr.Blocks(title="SEO & AI Visibility Analyzer") as demo:
|
|
| 146 |
)
|
| 147 |
|
| 148 |
if __name__ == "__main__":
|
| 149 |
-
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
output += f"- H1: {page.get('h1_count', 0)}, H2: {page.get('h2_count', 0)}, H3: {page.get('h3_count', 0)}\n"
|
| 61 |
output += f"- Images: {page.get('total_images', 0)} (missing alt: {page.get('missing_alt_tags', 0)})\n"
|
| 62 |
output += f"- Internal/External links: {page.get('internal_links', 0)}/{page.get('external_links', 0)}\n"
|
| 63 |
+
# ----- NEW FIELDS ADDED -----
|
| 64 |
+
output += f"- Readability: {page.get('readability_score', 0)}\n"
|
| 65 |
+
output += f"- Grammar Errors: {page.get('grammar_errors', 0)}\n"
|
| 66 |
+
output += f"- Canonical Tag: {'Yes' if page.get('canonical_tag') else 'No'}\n"
|
| 67 |
+
output += f"- OpenGraph Tags: {page.get('opengraph_tags', 0)}\n"
|
| 68 |
+
output += f"- Twitter Cards: {page.get('twitter_tags', 0)}\n"
|
| 69 |
+
output += f"- Robots Meta: {page.get('robots_meta', 'none')}\n"
|
| 70 |
+
output += f"- Viewport: {'Yes' if page.get('viewport_present') else 'No'}\n"
|
| 71 |
+
output += f"- Schema Types: {page.get('schema_types', 'none')}\n"
|
| 72 |
+
output += f"- Text/HTML Ratio: {page.get('text_to_html_ratio', 0)}%\n"
|
| 73 |
+
output += f"- Load Time: {page.get('load_time', 0)}s\n"
|
| 74 |
+
output += f"- Meta Description: {page.get('meta_description', '')}\n"
|
| 75 |
+
output += f"- Heading Order: {page.get('heading_order', '')}\n"
|
| 76 |
+
# ---------------------------
|
| 77 |
if page.get('ai_suggestions'):
|
| 78 |
output += f"- AI Suggestions: {page['ai_suggestions'][:200]}...\n"
|
| 79 |
output += "\n"
|
|
|
|
| 160 |
)
|
| 161 |
|
| 162 |
if __name__ == "__main__":
|
| 163 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
# import gradio as gr
|
| 178 |
+
# import asyncio
|
| 179 |
+
# import subprocess
|
| 180 |
+
# import sys
|
| 181 |
+
# import os
|
| 182 |
+
# from datetime import datetime
|
| 183 |
+
# os.environ.setdefault("PLAYWRIGHT_BROWSERS_PATH", "/home/user/.cache/ms-playwright")
|
| 184 |
+
# def install_playwright_browsers():
|
| 185 |
+
# """Install Playwright Chromium browsers β works on Hugging Face Spaces."""
|
| 186 |
+
# marker = os.path.join(os.environ["PLAYWRIGHT_BROWSERS_PATH"], ".installed")
|
| 187 |
+
# if os.path.exists(marker):
|
| 188 |
+
# print("β
Playwright browsers already installed, skipping.")
|
| 189 |
+
# return
|
| 190 |
+
# try:
|
| 191 |
+
# print("π¦ Installing Playwright browsers...")
|
| 192 |
+
# subprocess.check_call(
|
| 193 |
+
# [sys.executable, "-m", "playwright", "install", "chromium"],
|
| 194 |
+
# )
|
| 195 |
+
# os.makedirs(os.environ["PLAYWRIGHT_BROWSERS_PATH"], exist_ok=True)
|
| 196 |
+
# open(marker, "w").close()
|
| 197 |
+
# print("β
Playwright browsers installed successfully.")
|
| 198 |
+
# except subprocess.CalledProcessError as e:
|
| 199 |
+
# print(f"β Playwright install failed with code {e.returncode}")
|
| 200 |
+
# except Exception as e:
|
| 201 |
+
# print(f"β Unexpected error during install: {e}")
|
| 202 |
+
|
| 203 |
+
# install_playwright_browsers()
|
| 204 |
+
|
| 205 |
+
# try:
|
| 206 |
+
# import spaces
|
| 207 |
+
# HAS_SPACES = True
|
| 208 |
+
# except ImportError:
|
| 209 |
+
# HAS_SPACES = False
|
| 210 |
+
|
| 211 |
+
# if HAS_SPACES:
|
| 212 |
+
# @spaces.GPU
|
| 213 |
+
# def dummy_gpu():
|
| 214 |
+
# pass # This makes ZeroGPU happy
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
# from seo_analyzer import run_seo_analysis_fastapi
|
| 218 |
+
# from ai_visibility import run_ai_visibility_analysis
|
| 219 |
+
|
| 220 |
+
# # ---- Helper functions to format output ----
|
| 221 |
+
# def format_seo_result(result):
|
| 222 |
+
# if isinstance(result, tuple):
|
| 223 |
+
# data, csv_path = result
|
| 224 |
+
# if not data:
|
| 225 |
+
# return "No data returned."
|
| 226 |
+
# output = f"## SEO Analysis Results\n"
|
| 227 |
+
# output += f"**Pages analyzed:** {len(data)}\n"
|
| 228 |
+
# scores = [p.get('seo_score', 0) for p in data]
|
| 229 |
+
# avg = sum(scores) / len(scores) if scores else 0
|
| 230 |
+
# output += f"**Average SEO Score:** {avg:.1f}/100\n\n"
|
| 231 |
+
# for i, page in enumerate(data, 1):
|
| 232 |
+
# output += f"### Page {i}: {page.get('url', '')}\n"
|
| 233 |
+
# output += f"- Score: {page.get('seo_score', 0)}/100\n"
|
| 234 |
+
# output += f"- Title: {page.get('title', 'No title')}\n"
|
| 235 |
+
# output += f"- Word Count: {page.get('word_count', 0)}\n"
|
| 236 |
+
# output += f"- H1: {page.get('h1_count', 0)}, H2: {page.get('h2_count', 0)}, H3: {page.get('h3_count', 0)}\n"
|
| 237 |
+
# output += f"- Images: {page.get('total_images', 0)} (missing alt: {page.get('missing_alt_tags', 0)})\n"
|
| 238 |
+
# output += f"- Internal/External links: {page.get('internal_links', 0)}/{page.get('external_links', 0)}\n"
|
| 239 |
+
# if page.get('ai_suggestions'):
|
| 240 |
+
# output += f"- AI Suggestions: {page['ai_suggestions'][:200]}...\n"
|
| 241 |
+
# output += "\n"
|
| 242 |
+
# return output
|
| 243 |
+
# else:
|
| 244 |
+
# return f"β Error: {result.get('message', 'Unknown error')}"
|
| 245 |
+
|
| 246 |
+
# def format_ai_result(result):
|
| 247 |
+
# if result.get('status') == 'error':
|
| 248 |
+
# return f"β Error: {result.get('message', 'Unknown error')}"
|
| 249 |
+
# output = f"## AI Visibility / Readiness Analysis\n"
|
| 250 |
+
# output += f"**URL:** {result.get('url', '')}\n"
|
| 251 |
+
# output += f"**Pages analyzed:** {result.get('pages_analyzed', 0)}\n"
|
| 252 |
+
# output += f"**Overall AI Readiness Score:** {result.get('ai_readiness_score', 0)}/100\n"
|
| 253 |
+
# output += f"**Page types detected:** {result.get('page_type_breakdown', {})}\n\n"
|
| 254 |
+
# cat_scores = result.get('category_scores', {})
|
| 255 |
+
# if cat_scores:
|
| 256 |
+
# output += "### Category Scores\n"
|
| 257 |
+
# for k, v in cat_scores.items():
|
| 258 |
+
# output += f"- {k.replace('_score', '').replace('_', ' ').title()}: {v if v is not None else 'N/A'}\n"
|
| 259 |
+
# output += "\n"
|
| 260 |
+
# previews = result.get('results_preview', [])
|
| 261 |
+
# if previews:
|
| 262 |
+
# output += "### Per-Page Details\n"
|
| 263 |
+
# for p in previews:
|
| 264 |
+
# output += f"**URL:** {p.get('url', '')}\n"
|
| 265 |
+
# output += f"- Page Type: {p.get('page_type', 'unknown')} (conf: {p.get('page_type_confidence', 0):.2f})\n"
|
| 266 |
+
# output += f"- Readiness Score: {p.get('ai_readiness_score', 0)}/100\n"
|
| 267 |
+
# output += f"- Topic Clarity: {p.get('topic_clarity', 0)}\n"
|
| 268 |
+
# output += f"- Content Completeness: {p.get('content_completeness', 0)}\n"
|
| 269 |
+
# output += f"- Entity Clarity: {p.get('entity_clarity', 'N/A')}\n"
|
| 270 |
+
# output += f"- Freshness: {p.get('freshness_status', 'unknown')}\n\n"
|
| 271 |
+
# return output
|
| 272 |
+
|
| 273 |
+
# # ---- Async analysis wrappers (Gradio will handle async functions) ----
|
| 274 |
+
# async def analyze_seo_async(url, max_pages, max_concurrent, use_ai):
|
| 275 |
+
# result = await run_seo_analysis_fastapi(
|
| 276 |
+
# base_url=url,
|
| 277 |
+
# max_pages=int(max_pages),
|
| 278 |
+
# use_ai=use_ai,
|
| 279 |
+
# max_concurrent=int(max_concurrent)
|
| 280 |
+
# )
|
| 281 |
+
# return format_seo_result(result)
|
| 282 |
+
|
| 283 |
+
# async def analyze_ai_async(url, max_pages, max_concurrent, use_ai):
|
| 284 |
+
# result = await run_ai_visibility_analysis(
|
| 285 |
+
# base_url=url,
|
| 286 |
+
# max_pages=int(max_pages),
|
| 287 |
+
# max_concurrent=int(max_concurrent),
|
| 288 |
+
# use_ai=use_ai
|
| 289 |
+
# )
|
| 290 |
+
# return format_ai_result(result)
|
| 291 |
+
|
| 292 |
+
# # ---- Gradio Interface ----
|
| 293 |
+
# with gr.Blocks(title="SEO & AI Visibility Analyzer") as demo:
|
| 294 |
+
# gr.Markdown("# π SEO & AI Visibility Analysis Tool")
|
| 295 |
+
# gr.Markdown("Enter a website URL to analyze its SEO health and AI search readiness.")
|
| 296 |
+
|
| 297 |
+
# with gr.Row():
|
| 298 |
+
# with gr.Column(scale=2):
|
| 299 |
+
# url_input = gr.Textbox(label="Website URL", placeholder="https://example.com", value="https://example.com")
|
| 300 |
+
# with gr.Column(scale=1):
|
| 301 |
+
# max_pages_input = gr.Number(label="Max Pages", value=3, minimum=1, maximum=20, step=1)
|
| 302 |
+
# with gr.Column(scale=1):
|
| 303 |
+
# max_concurrent_input = gr.Number(label="Concurrent Browsers", value=1, minimum=1, maximum=5, step=1)
|
| 304 |
+
# with gr.Column(scale=1):
|
| 305 |
+
# use_ai_check = gr.Checkbox(label="Enable AI Suggestions", value=True)
|
| 306 |
+
|
| 307 |
+
# with gr.Row():
|
| 308 |
+
# seo_btn = gr.Button("π Analyze SEO", variant="primary")
|
| 309 |
+
# ai_btn = gr.Button("π€ Analyze AI Visibility", variant="secondary")
|
| 310 |
+
|
| 311 |
+
# output = gr.Markdown(label="Results")
|
| 312 |
+
|
| 313 |
+
# seo_btn.click(
|
| 314 |
+
# fn=analyze_seo_async,
|
| 315 |
+
# inputs=[url_input, max_pages_input, max_concurrent_input, use_ai_check],
|
| 316 |
+
# outputs=output
|
| 317 |
+
# )
|
| 318 |
+
# ai_btn.click(
|
| 319 |
+
# fn=analyze_ai_async,
|
| 320 |
+
# inputs=[url_input, max_pages_input, max_concurrent_input, use_ai_check],
|
| 321 |
+
# outputs=output
|
| 322 |
+
# )
|
| 323 |
+
|
| 324 |
+
# if __name__ == "__main__":
|
| 325 |
+
# demo.launch(server_name="0.0.0.0", server_port=7860)
|