# enhanced_dashboard.py
import streamlit as st
import requests
import base64
import json
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime
import time
# Page configuration
st.set_page_config(
page_title="Website Intelligence Dashboard",
page_icon="🚀",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for better styling
st.markdown("""
""", unsafe_allow_html=True)
# API Configuration
API_BASE = "https://apexherbert200-playwright-scraper-clean.hf.space"
# Sidebar configuration
st.sidebar.markdown('
', unsafe_allow_html=True)
# API endpoint selection
analysis_type = st.sidebar.selectbox(
"Choose Analysis Type",
["Complete Analysis", "SEO Only", "Performance Only", "Metadata Only", "Screenshot Only"]
)
# Advanced options
st.sidebar.markdown("### ⚙️ Advanced Options")
screenshot_width = st.sidebar.slider("Screenshot Width", 800, 1920, 1200)
screenshot_height = st.sidebar.slider("Screenshot Height", 600, 1080, 800)
full_page_screenshot = st.sidebar.checkbox("Full Page Screenshot", value=True)
# Main dashboard
st.markdown('🚀 Website Intelligence Dashboard
', unsafe_allow_html=True)
# URL input with validation
col1, col2 = st.columns([3, 1])
with col1:
url = st.text_input(
"🌐 Enter Website URL",
value="https://www.example.com",
placeholder="https://www.yourwebsite.com"
)
with col2:
st.markdown("
", unsafe_allow_html=True)
analyze_button = st.button("🔍 Analyze Website", type="primary")
# URL validation
def validate_url(url):
if not url:
return False, "Please enter a URL"
if not url.startswith(('http://', 'https://')):
return False, "URL must start with http:// or https://"
return True, ""
# API request function with error handling
def make_api_request(endpoint, params):
try:
response = requests.get(f"{API_BASE}/{endpoint}", params=params)
response.raise_for_status()
return response.json(), None
except requests.exceptions.Timeout:
return None, "Request timed out. Please try again."
except requests.exceptions.ConnectionError:
return None, "Connection error. Please check your internet connection."
except requests.exceptions.HTTPError as e:
return None, f"HTTP error: {e.response.status_code}"
except Exception as e:
return None, f"Unexpected error: {str(e)}"
# Main analysis logic
if analyze_button:
is_valid, error_msg = validate_url(url)
if not is_valid:
st.error(f"❌ {error_msg}")
else:
# Progress tracking
progress_bar = st.progress(0)
status_text = st.empty()
# Initialize data containers
seo_data = None
perf_data = None
meta_data = None
screenshot_data = None
try:
# Metadata Analysis
if analysis_type in ["Complete Analysis", "Metadata Only"]:
status_text.text("📄 Analyzing metadata...")
progress_bar.progress(20)
meta_data, error = make_api_request("metadata", {"url": url})
if error:
st.error(f"Metadata error: {error}")
# SEO Analysis
if analysis_type in ["Complete Analysis", "SEO Only"]:
status_text.text("🔍 Performing SEO audit...")
progress_bar.progress(40)
seo_data, error = make_api_request("seo", {"url": url})
if error:
st.error(f"SEO error: {error}")
# Performance Analysis
if analysis_type in ["Complete Analysis", "Performance Only"]:
status_text.text("⚡ Measuring performance...")
progress_bar.progress(60)
perf_data, error = make_api_request("performance", {"url": url})
if error:
st.error(f"Performance error: {error}")
# Screenshot
if analysis_type in ["Complete Analysis", "Screenshot Only"]:
status_text.text("📸 Capturing screenshot...")
progress_bar.progress(80)
screenshot_params = {
"url": url,
"width": screenshot_width,
"height": screenshot_height,
"full_page": full_page_screenshot
}
screenshot_response, error = make_api_request("screenshot", screenshot_params)
if error:
st.error(f"Screenshot error: {error}")
else:
screenshot_data = screenshot_response.get("screenshot")
progress_bar.progress(100)
status_text.text("✅ Analysis complete!")
time.sleep(1)
progress_bar.empty()
status_text.empty()
except Exception as e:
st.error(f"❌ Analysis failed: {str(e)}")
st.stop()
# Display Results
st.markdown("---")
# Overview Section
if any([meta_data, seo_data, perf_data]):
st.header("📊 Website Overview")
col1, col2, col3, col4 = st.columns(4)
with col1:
if meta_data and meta_data.get('title'):
st.metric("📄 Page Title", "✅ Found" if meta_data['title'] else "❌ Missing")
with col2:
if seo_data:
h1_count = seo_data.get('h1_count', 0)
h1_status = "✅ Good" if h1_count == 1 else f"⚠️ {h1_count} H1s"
st.metric("🏷️ H1 Tags", h1_status)
with col3:
if seo_data:
missing_alts = len(seo_data.get('missing_image_alts', []))
alt_status = "✅ All Good" if missing_alts == 0 else f"❌ {missing_alts} Missing"
st.metric("🖼️ Image Alt Tags", alt_status)
with col4:
if perf_data and perf_data.get('page_load_time_ms'):
load_time = perf_data['page_load_time_ms']
if load_time < 2000:
load_status = "🚀 Fast"
elif load_time < 4000:
load_status = "⚠️ Moderate"
else:
load_status = "🐌 Slow"
st.metric("⚡ Load Time", f"{load_time:.0f}ms", delta=load_status)
# Metadata Section
if meta_data:
st.header("📄 Metadata Analysis")
col1, col2 = st.columns(2)
with col1:
st.subheader("Basic Information")
st.write(f"**Title:** {meta_data.get('title', 'Not found')}")
st.write(f"**Description:** {meta_data.get('description', 'Not found')}")
st.write(f"**Canonical URL:** {meta_data.get('canonical', 'Not found')}")
if meta_data.get('favicon'):
st.write(f"**Favicon:** ✅ Found")
st.image(meta_data['favicon'], width=32)
with col2:
st.subheader("Social Media")
og_data = meta_data.get('og', {})
twitter_data = meta_data.get('twitter', {})
if og_data.get('og:title'):
st.write(f"**OG Title:** {og_data['og:title']}")
if og_data.get('og:description'):
st.write(f"**OG Description:** {og_data['og:description']}")
if twitter_data.get('twitter:title'):
st.write(f"**Twitter Title:** {twitter_data['twitter:title']}")
# SEO Section
if seo_data:
st.header("🔍 SEO Analysis")
col1, col2, col3 = st.columns(3)
with col1:
st.markdown('', unsafe_allow_html=True)
st.metric("H1 Tags Count", seo_data.get('h1_count', 0))
if seo_data.get('h1_count', 0) != 1:
st.warning("⚠️ Should have exactly 1 H1 tag")
st.markdown('
', unsafe_allow_html=True)
with col2:
st.markdown('', unsafe_allow_html=True)
internal_links = seo_data.get('internal_links', 0)
external_links = seo_data.get('external_links', 0)
st.metric("Internal Links", internal_links)
st.metric("External Links", external_links)
st.markdown('
', unsafe_allow_html=True)
with col3:
st.markdown('', unsafe_allow_html=True)
missing_alts = seo_data.get('missing_image_alts', [])
st.metric("Missing Alt Tags", len(missing_alts))
if missing_alts:
st.warning(f"⚠️ {len(missing_alts)} images missing alt text")
st.markdown('
', unsafe_allow_html=True)
# SEO Details
st.subheader("SEO Details")
col1, col2 = st.columns(2)
with col1:
st.write(f"**Robots Meta:** {seo_data.get('robots_meta', 'Not found')}")
st.write(f"**Has Canonical:** {'✅ Yes' if seo_data.get('has_canonical') else '❌ No'}")
st.write(f"**Meta Keywords:** {seo_data.get('meta_keywords', 'Not found')}")
with col2:
if missing_alts:
st.write("**Images Missing Alt Text:**")
for img in missing_alts[:5]: # Show first 5
st.write(f"- {img}")
if len(missing_alts) > 5:
st.write(f"... and {len(missing_alts) - 5} more")
# Performance Section
if perf_data:
st.header("⚡ Performance Metrics")
# Create performance chart
metrics = []
values = []
colors = []
if perf_data.get('page_load_time_ms'):
metrics.append('Page Load Time (ms)')
values.append(perf_data['page_load_time_ms'])
colors.append('#1f77b4')
if perf_data.get('first_contentful_paint'):
metrics.append('First Contentful Paint (ms)')
values.append(perf_data['first_contentful_paint'])
colors.append('#ff7f0e')
if perf_data.get('largest_contentful_paint'):
metrics.append('Largest Contentful Paint (ms)')
values.append(perf_data['largest_contentful_paint'])
colors.append('#2ca02c')
if metrics:
fig = px.bar(
x=metrics,
y=values,
title="Performance Metrics",
color=metrics,
color_discrete_sequence=colors
)
fig.update_layout(showlegend=False)
st.plotly_chart(fig, use_container_width=True)
# Performance details
col1, col2 = st.columns(2)
with col1:
st.subheader("Core Web Vitals")
if perf_data.get('first_contentful_paint'):
fcp = perf_data['first_contentful_paint']
fcp_status = "🟢 Good" if fcp < 1800 else "🟡 Needs Improvement" if fcp < 3000 else "🔴 Poor"
st.metric("First Contentful Paint", f"{fcp:.0f}ms", delta=fcp_status)
if perf_data.get('largest_contentful_paint'):
lcp = perf_data['largest_contentful_paint']
lcp_status = "🟢 Good" if lcp < 2500 else "🟡 Needs Improvement" if lcp < 4000 else "🔴 Poor"
st.metric("Largest Contentful Paint", f"{lcp:.0f}ms", delta=lcp_status)
with col2:
st.subheader("Additional Metrics")
if perf_data.get('cumulative_layout_shift'):
cls = perf_data['cumulative_layout_shift']
cls_status = "🟢 Good" if cls < 0.1 else "🟡 Needs Improvement" if cls < 0.25 else "🔴 Poor"
st.metric("Cumulative Layout Shift", f"{cls:.3f}", delta=cls_status)
if perf_data.get('page_load_time_ms'):
load_time = perf_data['page_load_time_ms']
st.metric("Total Load Time", f"{load_time:.0f}ms")
# Screenshot Section
if screenshot_data:
st.header("📸 Website Screenshot")
try:
screenshot_bytes = base64.b64decode(screenshot_data)
st.image(screenshot_bytes, caption=f"Screenshot of {url}", use_column_width=True)
# Download button for screenshot
st.download_button(
label="📥 Download Screenshot",
data=screenshot_bytes,
file_name=f"screenshot_{url.replace('https://', '').replace('http://', '').replace('/', '_')}.png",
mime="image/png"
)
except Exception as e:
st.error(f"Failed to display screenshot: {str(e)}")
# Footer
st.markdown("---")
st.markdown("""
🚀 Website Intelligence Dashboard | Powered by Advanced Web Analysis APIs
Built with ❤️ using Streamlit | © 2024
""", unsafe_allow_html=True)
# Sidebar additional info
st.sidebar.markdown("---")
st.sidebar.markdown("### 📊 Analysis Features")
st.sidebar.markdown("""
- **SEO Audit**: H1 tags, meta data, links analysis
- **Performance**: Core Web Vitals, load times
- **Metadata**: Social media tags, canonical URLs
- **Screenshots**: Visual website capture
- **Real-time**: Live website analysis
""")
st.sidebar.markdown("### 🔧 API Status")
try:
health_response = requests.get(f"{API_BASE}/health", timeout=5)
if health_response.status_code == 200:
st.sidebar.success("🟢 API Online")
else:
st.sidebar.error("🔴 API Issues")
except:
st.sidebar.warning("🟡 API Status Unknown")