Waseem7711 commited on
Commit
ff90bee
Β·
verified Β·
1 Parent(s): 932c5b4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +504 -242
app.py CHANGED
@@ -1,271 +1,533 @@
1
- from seo_analyzer.app import main
2
- if __name__ == "__main__":
3
- main()
4
-
5
  import requests
6
- from bs4 import BeautifulSoup
7
- import json
8
- import re
9
- from urllib.parse import urljoin, urlparse
10
 
 
 
 
 
 
 
 
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- class SEOAnalyzer:
14
- def __init__(self):
15
- self.session = requests.Session()
16
- self.session.headers.update({
17
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
18
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- def analyze_website(self, url):
21
- """Main analysis function"""
22
- try:
23
- # Fetch the webpage
24
- response = self.session.get(url, timeout=10, allow_redirects=True)
25
- response.raise_for_status()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # Parse HTML
28
- soup = BeautifulSoup(response.text, 'html.parser')
 
 
 
 
 
 
29
 
30
- # Extract metadata
31
- metadata = self.extract_metadata(soup)
32
 
33
- # Extract structured data
34
- structured_data = self.extract_structured_data(soup)
 
35
 
36
- # Calculate SEO score
37
- seo_score = self.calculate_seo_score(metadata)
38
 
39
- # Generate recommendations
40
- recommendations = self.generate_recommendations(metadata, url)
 
41
 
42
- return {
43
- 'success': True,
44
- 'metadata': metadata,
45
- 'structured_data': structured_data,
46
- 'seo_score': seo_score,
47
- 'recommendations': recommendations,
48
- 'html_head': str(soup.head) if soup.head else "Head section not found"
49
- }
50
 
51
- except requests.exceptions.RequestException as e:
52
- return {
53
- 'success': False,
54
- 'error': f"Failed to fetch website: {str(e)}"
55
- }
56
- except Exception as e:
57
- return {
58
- 'success': False,
59
- 'error': f"Analysis error: {str(e)}"
60
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- def extract_metadata(self, soup):
63
- """Extract all relevant metadata from the page"""
64
- metadata = {}
65
-
66
- # Basic meta tags
67
- if soup.title:
68
- metadata['title'] = soup.title.string.strip() if soup.title.string else ""
69
-
70
- # Meta tags
71
- meta_tags = soup.find_all('meta')
72
- for tag in meta_tags:
73
- # Standard meta tags
74
- if tag.get('name'):
75
- name = tag.get('name').lower()
76
- content = tag.get('content', '')
77
- metadata[name] = content
78
-
79
- # Property meta tags (Open Graph, etc.)
80
- elif tag.get('property'):
81
- prop = tag.get('property').lower()
82
- content = tag.get('content', '')
83
- metadata[prop] = content
84
-
85
- # HTTP-equiv meta tags
86
- elif tag.get('http-equiv'):
87
- equiv = tag.get('http-equiv').lower()
88
- content = tag.get('content', '')
89
- metadata[f'http-equiv-{equiv}'] = content
90
-
91
- # Charset
92
- elif tag.get('charset'):
93
- metadata['charset'] = tag.get('charset')
94
-
95
- # Link tags (canonical, etc.)
96
- link_tags = soup.find_all('link')
97
- for tag in link_tags:
98
- rel = tag.get('rel')
99
- if rel:
100
- rel_str = ' '.join(rel) if isinstance(rel, list) else rel
101
- if rel_str in ['canonical', 'alternate', 'prev', 'next']:
102
- metadata[f'link-{rel_str}'] = tag.get('href', '')
103
-
104
- return metadata
105
 
106
- def extract_structured_data(self, soup):
107
- """Extract JSON-LD structured data"""
108
- structured_data = []
109
-
110
- scripts = soup.find_all('script', type='application/ld+json')
111
- for script in scripts:
112
- try:
113
- if script.string:
114
- data = json.loads(script.string.strip())
115
- structured_data.append(data)
116
- except json.JSONDecodeError:
117
- continue
118
-
119
- return structured_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- def calculate_seo_score(self, metadata):
122
- """Calculate SEO score based on best practices"""
123
- score = 0
124
- max_score = 100
125
-
126
- # Title tag (20 points)
127
- title = metadata.get('title', '')
128
- if title:
129
- score += 10
130
- if 30 <= len(title) <= 60:
131
- score += 10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
- # Meta description (20 points)
134
- description = metadata.get('description', '')
135
- if description:
136
- score += 10
137
- if 120 <= len(description) <= 160:
138
- score += 10
139
 
140
- # Open Graph tags (20 points)
141
- og_title = metadata.get('og:title', '')
142
- og_description = metadata.get('og:description', '')
143
- og_image = metadata.get('og:image', '')
 
 
 
144
 
145
- if og_title:
146
- score += 7
147
- if og_description:
148
- score += 7
149
- if og_image:
150
- score += 6
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
- # Twitter Card (15 points)
153
- twitter_card = metadata.get('twitter:card', '')
154
- twitter_title = metadata.get('twitter:title', '')
155
- twitter_description = metadata.get('twitter:description', '')
156
 
157
- if twitter_card:
158
- score += 5
159
- if twitter_title:
160
- score += 5
161
- if twitter_description:
162
- score += 5
 
 
 
163
 
164
- # Technical SEO (25 points)
165
- if metadata.get('viewport'):
166
- score += 5
167
- if metadata.get('charset'):
168
- score += 5
169
- if metadata.get('robots'):
170
- score += 5
171
- if metadata.get('link-canonical'):
172
- score += 5
173
- if not metadata.get('robots') or 'noindex' not in metadata.get('robots', '').lower():
174
- score += 5
175
 
176
- return min(score, max_score)
 
 
 
 
 
 
 
 
177
 
178
- def generate_recommendations(self, metadata, url):
179
- """Generate actionable SEO recommendations"""
180
- recommendations = []
181
-
182
- # Title tag recommendations
183
- title = metadata.get('title', '')
184
- if not title:
185
- recommendations.append({
186
- 'type': 'error',
187
- 'message': 'Missing title tag. Add a descriptive title between 30-60 characters.'
188
- })
189
- elif len(title) < 30:
190
- recommendations.append({
191
- 'type': 'warning',
192
- 'message': f'Title tag is too short ({len(title)} chars). Aim for 30-60 characters.'
193
- })
194
- elif len(title) > 60:
195
- recommendations.append({
196
- 'type': 'warning',
197
- 'message': f'Title tag is too long ({len(title)} chars). Keep it under 60 characters to avoid truncation.'
198
- })
199
-
200
- # Meta description recommendations
201
- description = metadata.get('description', '')
202
- if not description:
203
- recommendations.append({
204
- 'type': 'error',
205
- 'message': 'Missing meta description. Add a compelling description between 120-160 characters.'
206
- })
207
- elif len(description) < 120:
208
- recommendations.append({
209
- 'type': 'warning',
210
- 'message': f'Meta description is too short ({len(description)} chars). Aim for 120-160 characters.'
211
- })
212
- elif len(description) > 160:
213
- recommendations.append({
214
- 'type': 'warning',
215
- 'message': f'Meta description is too long ({len(description)} chars). Keep it under 160 characters.'
216
- })
217
-
218
- # Open Graph recommendations
219
- if not metadata.get('og:title'):
220
- recommendations.append({
221
- 'type': 'warning',
222
- 'message': 'Missing Open Graph title. Add og:title for better social media sharing.'
223
- })
224
 
225
- if not metadata.get('og:description'):
226
- recommendations.append({
227
- 'type': 'warning',
228
- 'message': 'Missing Open Graph description. Add og:description for social media previews.'
229
- })
230
-
231
- if not metadata.get('og:image'):
232
- recommendations.append({
233
- 'type': 'warning',
234
- 'message': 'Missing Open Graph image. Add og:image (1200x630px recommended) for social sharing.'
235
- })
236
-
237
- # Twitter Card recommendations
238
- if not metadata.get('twitter:card'):
239
- recommendations.append({
240
- 'type': 'info',
241
- 'message': 'Consider adding Twitter Card meta tags for better Twitter sharing experience.'
242
- })
243
 
244
- # Technical SEO recommendations
245
- if not metadata.get('viewport'):
246
- recommendations.append({
247
- 'type': 'error',
248
- 'message': 'Missing viewport meta tag. Add <meta name="viewport" content="width=device-width, initial-scale=1"> for mobile optimization.'
249
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
 
251
- if not metadata.get('charset'):
252
- recommendations.append({
253
- 'type': 'warning',
254
- 'message': 'Missing charset declaration. Add <meta charset="UTF-8"> in the head section.'
255
- })
256
 
257
- if not metadata.get('link-canonical'):
258
- recommendations.append({
259
- 'type': 'info',
260
- 'message': 'Consider adding a canonical URL to prevent duplicate content issues.'
261
- })
262
 
263
- # Robots meta tag
264
- robots = metadata.get('robots', '')
265
- if 'noindex' in robots.lower():
266
- recommendations.append({
267
- 'type': 'warning',
268
- 'message': 'Page is set to noindex. Remove this if you want the page to be indexed by search engines.'
269
- })
 
 
 
 
 
 
 
270
 
271
- return recommendations
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
 
 
 
2
  import requests
3
+ from urllib.parse import urlparse
4
+ import time
5
+ from seo_analyzer import SEOAnalyzer
6
+ from preview_generators import PreviewGenerator
7
 
8
+ # Page configuration
9
+ st.set_page_config(
10
+ page_title="SEO Metadata Analyzer",
11
+ page_icon="πŸ”",
12
+ layout="wide",
13
+ initial_sidebar_state="expanded"
14
+ )
15
 
16
+ def validate_url(url):
17
+ """Validate and normalize URL"""
18
+ if not url:
19
+ return None, "Please enter a URL"
20
+
21
+ if not url.startswith(('http://', 'https://')):
22
+ url = 'https://' + url
23
+
24
+ try:
25
+ result = urlparse(url)
26
+ if not all([result.scheme, result.netloc]):
27
+ return None, "Invalid URL format"
28
+ return url, None
29
+ except Exception as e:
30
+ return None, f"Invalid URL: {str(e)}"
31
 
32
+ def main():
33
+ # Custom CSS for responsive design
34
+ st.markdown("""
35
+ <style>
36
+ /* Responsive Main Header */
37
+ .main-header {
38
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
39
+ padding: 2rem 1rem;
40
+ border-radius: 10px;
41
+ margin-bottom: 2rem;
42
+ text-align: center;
43
+ }
44
+ .main-header h1 {
45
+ color: white;
46
+ margin: 0;
47
+ font-size: clamp(1.8rem, 4vw, 2.5rem);
48
+ font-weight: 700;
49
+ }
50
+ .main-header p {
51
+ color: #f0f0f0;
52
+ margin: 0.5rem 0 0 0;
53
+ font-size: clamp(1rem, 2.5vw, 1.2rem);
54
+ line-height: 1.4;
55
+ }
56
 
57
+ /* Responsive Buttons */
58
+ .stButton > button {
59
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
60
+ color: white;
61
+ border: none;
62
+ border-radius: 12px;
63
+ font-weight: 600;
64
+ width: 100%;
65
+ padding: clamp(12px, 3vw, 16px) clamp(16px, 4vw, 24px);
66
+ font-size: clamp(14px, 3.5vw, 16px);
67
+ transition: all 0.3s ease;
68
+ margin: 8px 0;
69
+ min-height: 44px;
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: center;
73
+ text-align: center;
74
+ }
75
+ .stButton > button:hover {
76
+ transform: translateY(-1px);
77
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
78
+ background: linear-gradient(90deg, #5a6fd8 0%, #6a4190 100%);
79
+ }
80
+ </style>
81
+ """, unsafe_allow_html=True)
82
+
83
+ # Main header
84
+ st.markdown("""
85
+ <div class="main-header">
86
+ <h1>πŸ” SEO Metadata Analyzer</h1>
87
+ <p>Analyze your website's SEO performance and get actionable insights with visual previews</p>
88
+ </div>
89
+ """, unsafe_allow_html=True)
90
+
91
+ # Sidebar for controls
92
+ with st.sidebar:
93
+ st.markdown("### 🎯 Analysis Controls")
94
+
95
+ # Enhanced URL input with visual https:// prefix
96
+ st.markdown("**🌐 Website URL**")
97
+
98
+ # Create a visual input with https:// prefix
99
+ domain_input = st.text_input(
100
+ "Domain",
101
+ placeholder="google.com or github.com",
102
+ help="Just enter the domain name - https:// is added automatically!",
103
+ key="domain_input"
104
+ )
105
+
106
+ # Display the full URL visually
107
+ if domain_input:
108
+ url_input = f"https://{domain_input.strip().replace('https://', '').replace('http://', '')}"
109
+ st.markdown(f"""
110
+ <div style="
111
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
112
+ color: white;
113
+ padding: 0.5rem 1rem;
114
+ border-radius: 8px;
115
+ margin: 0.5rem 0;
116
+ font-family: monospace;
117
+ font-size: 0.9rem;
118
+ word-break: break-all;
119
+ ">
120
+ πŸ“Ž Full URL: {url_input}
121
+ </div>
122
+ """, unsafe_allow_html=True)
123
+ else:
124
+ url_input = ""
125
+
126
+ st.markdown("---")
127
+
128
+ # Feature highlights
129
+ st.markdown("""
130
+ **πŸš€ What You'll Get:**
131
+ - SEO Score & Status
132
+ - Google Preview
133
+ - Social Media Previews
134
+ - Detailed Analysis
135
+ - Action Items
136
+ """)
137
+
138
+ analyze_button = st.button("πŸš€ Analyze SEO", type="primary", use_container_width=True)
139
+
140
+ if url_input:
141
+ normalized_url, error = validate_url(url_input)
142
+ if error:
143
+ st.error(error)
144
+ elif normalized_url:
145
+ st.success(f"βœ… Ready to analyze: {normalized_url}")
146
+
147
+ st.markdown("---")
148
+ st.markdown("πŸ’‘ **Pro Tip:** Try analyzing popular sites like GitHub, Twitter, or LinkedIn to see examples of good SEO!")
149
+
150
+ # Main content area
151
+ if analyze_button and url_input:
152
+ normalized_url, error = validate_url(url_input)
153
+
154
+ if error:
155
+ st.error(error)
156
+ return
157
 
158
+ # Progress indicator
159
+ progress_bar = st.progress(0)
160
+ status_text = st.empty()
161
+
162
+ try:
163
+ # Initialize analyzer
164
+ status_text.text("🌐 Fetching website data...")
165
+ progress_bar.progress(20)
166
 
167
+ analyzer = SEOAnalyzer()
168
+ analysis_result = analyzer.analyze_website(normalized_url)
169
 
170
+ if not analysis_result['success']:
171
+ st.error(f"❌ Failed to analyze website: {analysis_result['error']}")
172
+ return
173
 
174
+ progress_bar.progress(60)
175
+ status_text.text("πŸ” Analyzing SEO metadata...")
176
 
177
+ # Generate previews
178
+ progress_bar.progress(80)
179
+ status_text.text("🎨 Generating previews...")
180
 
181
+ preview_gen = PreviewGenerator(analysis_result['metadata'])
 
 
 
 
 
 
 
182
 
183
+ progress_bar. me show you all the complete code files properly! I'll provide each file one by one:
184
+
185
+ ## πŸ“ File 1: `app.py` (Main Application)
186
+
187
+ ```python
188
+ import streamlit as st
189
+ import requests
190
+ from urllib.parse import urlparse
191
+ import time
192
+ from seo_analyzer import SEOAnalyzer
193
+ from preview_generators import PreviewGenerator
194
+
195
+ # Page configuration
196
+ st.set_page_config(
197
+ page_title="SEO Metadata Analyzer",
198
+ page_icon="πŸ”",
199
+ layout="wide",
200
+ initial_sidebar_state="expanded"
201
+ )
202
+
203
+ def validate_url(url):
204
+ """Validate and normalize URL"""
205
+ if not url:
206
+ return None, "Please enter a URL"
207
 
208
+ if not url.startswith(('http://', 'https://')):
209
+ url = 'https://' + url
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
 
211
+ try:
212
+ result = urlparse(url)
213
+ if not all([result.scheme, result.netloc]):
214
+ return None, "Invalid URL format"
215
+ return url, None
216
+ except Exception as e:
217
+ return None, f"Invalid URL: {str(e)}"
218
+
219
+ def main():
220
+ # Custom CSS for responsive design
221
+ st.markdown("""
222
+ <style>
223
+ /* Responsive Main Header */
224
+ .main-header {
225
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
226
+ padding: 2rem 1rem;
227
+ border-radius: 10px;
228
+ margin-bottom: 2rem;
229
+ text-align: center;
230
+ }
231
+ .main-header h1 {
232
+ color: white;
233
+ margin: 0;
234
+ font-size: clamp(1.8rem, 4vw, 2.5rem);
235
+ font-weight: 700;
236
+ }
237
+ .main-header p {
238
+ color: #f0f0f0;
239
+ margin: 0.5rem 0 0 0;
240
+ font-size: clamp(1rem, 2.5vw, 1.2rem);
241
+ line-height: 1.4;
242
+ }
243
 
244
+ /* Responsive Buttons */
245
+ .stButton > button {
246
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
247
+ color: white;
248
+ border: none;
249
+ border-radius: 12px;
250
+ font-weight: 600;
251
+ width: 100%;
252
+ padding: clamp(12px, 3vw, 16px) clamp(16px, 4vw, 24px);
253
+ font-size: clamp(14px, 3.5vw, 16px);
254
+ transition: all 0.3s ease;
255
+ margin: 8px 0;
256
+ min-height: 44px;
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ text-align: center;
261
+ }
262
+ .stButton > button:hover {
263
+ transform: translateY(-1px);
264
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.3);
265
+ background: linear-gradient(90deg, #5a6fd8 0%, #6a4190 100%);
266
+ }
267
+ </style>
268
+ """, unsafe_allow_html=True)
269
+
270
+ # Main header
271
+ st.markdown("""
272
+ <div class="main-header">
273
+ <h1>πŸ” SEO Metadata Analyzer</h1>
274
+ <p>Analyze your website's SEO performance and get actionable insights with visual previews</p>
275
+ </div>
276
+ """, unsafe_allow_html=True)
277
+
278
+ # Sidebar for controls
279
+ with st.sidebar:
280
+ st.markdown("### 🎯 Analysis Controls")
281
 
282
+ # Enhanced URL input with visual https:// prefix
283
+ st.markdown("**🌐 Website URL**")
 
 
 
 
284
 
285
+ # Create a visual input with https:// prefix
286
+ domain_input = st.text_input(
287
+ "Domain",
288
+ placeholder="google.com or github.com",
289
+ help="Just enter the domain name - https:// is added automatically!",
290
+ key="domain_input"
291
+ )
292
 
293
+ # Display the full URL visually
294
+ if domain_input:
295
+ url_input = f"https://{domain_input.strip().replace('https://', '').replace('http://', '')}"
296
+ st.markdown(f"""
297
+ <div style="
298
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
299
+ color: white;
300
+ padding: 0.5rem 1rem;
301
+ border-radius: 8px;
302
+ margin: 0.5rem 0;
303
+ font-family: monospace;
304
+ font-size: 0.9rem;
305
+ word-break: break-all;
306
+ ">
307
+ πŸ“Ž Full URL: {url_input}
308
+ </div>
309
+ """, unsafe_allow_html=True)
310
+ else:
311
+ url_input = ""
312
 
313
+ st.markdown("---")
 
 
 
314
 
315
+ # Feature highlights
316
+ st.markdown("""
317
+ **πŸš€ What You'll Get:**
318
+ - SEO Score & Status
319
+ - Google Preview
320
+ - Social Media Previews
321
+ - Detailed Analysis
322
+ - Action Items
323
+ """)
324
 
325
+ analyze_button = st.button("πŸš€ Analyze SEO", type="primary", use_container_width=True)
 
 
 
 
 
 
 
 
 
 
326
 
327
+ if url_input:
328
+ normalized_url, error = validate_url(url_input)
329
+ if error:
330
+ st.error(error)
331
+ elif normalized_url:
332
+ st.success(f"βœ… Ready to analyze: {normalized_url}")
333
+
334
+ st.markdown("---")
335
+ st.markdown("πŸ’‘ **Pro Tip:** Try analyzing popular sites like GitHub, Twitter, or LinkedIn to see examples of good SEO!")
336
 
337
+ # Main content area
338
+ if analyze_button and url_input:
339
+ normalized_url, error = validate_url(url_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
 
341
+ if error:
342
+ st.error(error)
343
+ return
344
+
345
+ # Progress indicator
346
+ progress_bar = st.progress(0)
347
+ status_text = st.empty()
 
 
 
 
 
 
 
 
 
 
 
348
 
349
+ try:
350
+ # Initialize analyzer
351
+ status_text.text("🌐 Fetching website data...")
352
+ progress_bar.progress(20)
353
+
354
+ analyzer = SEOAnalyzer()
355
+ analysis_result = analyzer.analyze_website(normalized_url)
356
+
357
+ if not analysis_result['success']:
358
+ st.error(f"❌ Failed to analyze website: {analysis_result['error']}")
359
+ return
360
+
361
+ progress_bar.progress(60)
362
+ status_text.text("πŸ” Analyzing SEO metadata...")
363
+
364
+ # Generate previews
365
+ progress_bar.progress(80)
366
+ status_text.text("🎨 Generating previews...")
367
+
368
+ preview_gen = PreviewGenerator(analysis_result['metadata'])
369
+
370
+ progress_bar.progress(100)
371
+ status_text.text("βœ… Analysis complete!")
372
+ results
373
+ display_results(analysis_result, preview_gen, normalized_url)
374
+
375
+ except Exception as e:
376
+ st.error(f"❌ An error occurred during analysis: {str(e)}")
377
+ progress_bar.empty()
378
+ status_text.empty()
379
+
380
+ else:
381
+ # Show welcome dashboard when no analysis is running
382
+ col1, col2, col3 = st.columns([1, 2, 1])
383
+ with col2:
384
+ st.markdown("""
385
+ <div style="
386
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
387
+ padding: 2rem;
388
+ border-radius: 15px;
389
+ margin: 2rem 0;
390
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
391
+ ">
392
+ <h3 style="text-align: center; color: #2c3e50; margin-bottom: 1.5rem;">🎯 How It Works</h3>
393
+
394
+ <div style="display: flex; align-items: center; margin-bottom: 1rem;">
395
+ <div style="background: #3498db; color: white; border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; margin-right: 1rem; font-weight: bold;">1</div>
396
+ <span><strong>Enter a URL</strong> in the sidebar (e.g., google.com, github.com)</span>
397
+ </div>
398
+
399
+ <div style="display: flex; align-items: center; margin-bottom: 1rem;">
400
+ <div style="background: #e74c3c; color: white; border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; margin-right: 1rem; font-weight: bold;">2</div>
401
+ <span><strong>Click Analyze SEO</strong> to start the comprehensive analysis</span>
402
+ </div>
403
+
404
+ <div style="display: flex; align-items: center; margin-bottom: 1.5rem;">
405
+ <div style="background: #27ae60; color: white; border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; margin-right: 1rem; font-weight: bold;">3</div>
406
+ <span><strong>Review detailed results</strong> with actionable insights</span>
407
+ </div>
408
+
409
+ <h4 style="color: #2c3e50; margin-bottom: 1rem;">πŸ“Š Complete SEO Analysis Includes:</h4>
410
+ <ul style="color: #34495e; line-height: 1.6;">
411
+ <li><strong>SEO Score & Status</strong> - Overall website rating</li>
412
+ <li><strong>Meta Tags Analysis</strong> - Title, description, keywords</li>
413
+ <li><strong>Social Media Previews</strong> - Facebook, Twitter cards</li>
414
+ <li><strong>Google Search Preview</strong> - How your site appears in search</li>
415
+ <li><strong>Technical SEO Check</strong> - Viewport, canonical URLs, robots</li>
416
+ <li><strong>Structured Data</strong> - JSON-LD schema markup</li>
417
+ <li><strong>Priority Recommendations</strong> - Step-by-step improvement guide</li>
418
+ </ul>
419
+ </div>
420
+ """, unsafe_allow_html=True)
421
 
422
+ # Example websites to try
423
+ st.markdown("### 🌟 Try These Popular Websites:")
 
 
 
424
 
425
+ example_col1, example_col2, example_col3 = st.columns(3)
 
 
 
 
426
 
427
+ with example_col1:
428
+ if st.button("πŸ“š GitHub", use_container_width=True):
429
+ st.session_state.example_url = "github.com"
430
+ st.rerun()
431
+
432
+ with example_col2:
433
+ if st.button("πŸ” Google", use_container_width=True):
434
+ st.session_state.example_url = "google.com"
435
+ st.rerun()
436
+
437
+ with example_col3:
438
+ if st.button("πŸ’Ό LinkedIn", use_container_width=True):
439
+ st.session_state.example_url = "linkedin.com"
440
+ st.rerun()
441
 
442
+ # Handle example URL selection
443
+ if hasattr(st.session_state, 'example_url'):
444
+ st.info(f"πŸ’‘ Example selected: {st.session_state.example_url} - Enter this in the sidebar and click Analyze!")
445
+
446
+ def display_results(analysis_result, preview_gen, url):
447
+ """Display the complete analysis results"""
448
+ metadata = analysis_result['metadata']
449
+ seo_score = analysis_result['seo_score']
450
+ recommendations = analysis_result['recommendations']
451
+
452
+ # Overall SEO Status Header
453
+ st.header("🎯 SEO Analysis Report")
454
+
455
+ # Overall Status Card
456
+ if seo_score >= 80:
457
+ status_color = "#28a745"
458
+ status_text = "EXCELLENT"
459
+ status_icon = "🟒"
460
+ status_message = "Your website has excellent SEO optimization!"
461
+ elif seo_score >= 60:
462
+ status_color = "#ffc107"
463
+ status_text = "GOOD"
464
+ status_icon = "🟑"
465
+ status_message = "Your website has good SEO with room for improvement."
466
+ else:
467
+ status_color = "#dc3545"
468
+ status_text = "NEEDS IMPROVEMENT"
469
+ status_icon = "πŸ”΄"
470
+ status_message = "Your website needs significant SEO improvements."
471
+
472
+ # Main Status Display
473
+ st.markdown(f"""
474
+ <div style="
475
+ background: linear-gradient(135deg, {status_color}15, {status_color}05);
476
+ border: 2px solid {status_color};
477
+ border-radius: 15px;
478
+ padding: clamp(15px, 5vw, 25px);
479
+ margin: 20px auto;
480
+ text-align: center;
481
+ max-width: 100%;
482
+ display: flex;
483
+ flex-direction: column;
484
+ align-items: center;
485
+ justify-content: center;
486
+ ">
487
+ <h1 style="
488
+ margin: 0;
489
+ color: {status_color};
490
+ font-size: clamp(2.5rem, 8vw, 4rem);
491
+ font-weight: 800;
492
+ line-height: 1;
493
+ ">{seo_score}</h1>
494
+ <h2 style="
495
+ margin: clamp(8px, 2vw, 15px) 0;
496
+ color: {status_color};
497
+ font-size: clamp(1.2rem, 4vw, 1.8rem);
498
+ font-weight: 600;
499
+ ">{status_icon} {status_text}</h2>
500
+ <p style="
501
+ margin: 0;
502
+ font-size: clamp(1rem, 3vw, 1.2rem);
503
+ color: #666;
504
+ line-height: 1.4;
505
+ text-align: center;
506
+ max-width: 90%;
507
+ ">{status_message}</p>
508
+ </div>
509
+ """, unsafe_allow_html=True)
510
+
511
+ # Previews
512
+ st.header("πŸ‘€ Search & Social Media Previews")
513
+
514
+ # Google Search Preview
515
+ st.subheader("πŸ” Google Search Result Preview")
516
+ google_preview = preview_gen.generate_google_preview(url)
517
+ st.markdown(google_preview, unsafe_allow_html=True)
518
+
519
+ # Social Media Previews
520
+ col1, col2 = st.columns([1, 1])
521
+
522
+ with col1:
523
+ st.subheader("πŸ“˜ Facebook Preview")
524
+ facebook_preview = preview_gen.generate_facebook_preview(url)
525
+ st.markdown(facebook_preview, unsafe_allow_html=True)
526
+
527
+ with col2:
528
+ st.subheader("🐦 Twitter Preview")
529
+ twitter_preview = preview_gen.generate_twitter_preview(url)
530
+ st.markdown(twitter_preview, unsafe_allow_html=True)
531
+
532
+ if __name__ == "__main__":
533
+ main()