joermd commited on
Commit
81169a3
·
verified ·
1 Parent(s): 42d6d72

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +255 -440
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import streamlit as st
 
2
  from streamlit_option_menu import option_menu
3
  import requests
4
  import pandas as pd
@@ -16,506 +17,320 @@ import dns.resolver
16
  from urllib.parse import urlparse
17
  import json
18
  import numpy as np
 
 
 
19
  from PIL import Image
20
  import io
21
  import time
22
- import matplotlib.pyplot as plt
23
- import seaborn as sns
24
- from datetime import timedelta
25
- import tldextract
26
- from concurrent.futures import ThreadPoolExecutor
27
- import re
28
- from collections import Counter
29
- from wordcloud import WordCloud
30
- import advertools as adv
31
 
32
- # تحسين مظهر الصفحة
33
- st.set_page_config(
34
- layout="wide",
35
- page_title="محلل المواقع المتقدم | Website Analyzer Pro",
36
- page_icon="🔍",
37
- initial_sidebar_state="expanded"
38
- )
39
 
40
- # تحسين التصميم باستخدام CSS المحسن
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  st.markdown("""
42
  <style>
43
- @import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;700&display=swap');
44
-
45
- * {
46
- font-family: 'Tajawal', sans-serif;
47
- }
48
-
49
  .main {
50
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
51
- padding: 20px;
52
- }
53
-
54
- .metric-card {
55
- background: white;
56
- border-radius: 15px;
57
- padding: 20px;
58
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
59
- transition: all 0.3s ease;
60
- margin-bottom: 20px;
61
  }
62
-
63
- .metric-card:hover {
64
- transform: translateY(-5px);
65
- box-shadow: 0 8px 25px rgba(0,0,0,0.15);
66
- }
67
-
68
- .metric-value {
69
- font-size: 2em;
70
- font-weight: bold;
71
- color: #2196F3;
72
- }
73
-
74
- .metric-label {
75
- color: #666;
76
- font-size: 1.1em;
77
- }
78
-
79
  .stButton>button {
80
- background: linear-gradient(45deg, #2196F3, #21CBF3);
81
  color: white;
82
- border-radius: 25px;
83
- padding: 15px 30px;
 
84
  border: none;
85
- box-shadow: 0 4px 15px rgba(33,150,243,0.3);
86
- transition: all 0.3s ease;
87
- font-size: 1.1em;
88
- font-weight: 500;
89
- width: 100%;
90
  }
91
-
92
  .stButton>button:hover {
93
- transform: translateY(-2px);
94
- box-shadow: 0 6px 20px rgba(33,150,243,0.4);
95
- }
96
-
97
- h1, h2, h3 {
98
- color: #1E3D59;
99
- font-weight: 700;
100
- }
101
-
102
- .stTextInput>div>div>input {
103
- border-radius: 10px;
104
- border: 2px solid #E0E0E0;
105
- padding: 12px;
106
- font-size: 1.1em;
107
- transition: all 0.3s ease;
108
- }
109
-
110
- .stTextInput>div>div>input:focus {
111
- border-color: #2196F3;
112
- box-shadow: 0 0 0 2px rgba(33,150,243,0.2);
113
  }
114
-
115
- .streamlit-expanderHeader {
116
  background-color: white;
117
  border-radius: 10px;
118
- padding: 10px;
119
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
120
- }
121
-
122
- .stProgress > div > div > div {
123
- background-color: #2196F3;
124
- }
125
-
126
- .tab-content {
127
  padding: 20px;
128
- background: white;
129
- border-radius: 15px;
130
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
131
- }
132
-
133
- .insight-card {
134
- background: #f8f9fa;
135
- border-right: 4px solid #2196F3;
136
- padding: 15px;
137
- margin: 10px 0;
138
- border-radius: 8px;
139
- }
140
-
141
- .chart-container {
142
- background: white;
143
- padding: 20px;
144
- border-radius: 15px;
145
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
146
- margin: 20px 0;
147
  }
148
  </style>
149
  """, unsafe_allow_html=True)
150
 
151
- class AdvancedWebsiteAnalyzer:
152
  def __init__(self):
153
  self.headers = {
154
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
155
  }
156
- self.history = self.load_history()
157
-
158
- def load_history(self):
159
- try:
160
- return pd.read_csv('analysis_history.csv')
161
- except:
162
- return pd.DataFrame(columns=['url', 'timestamp', 'performance_score', 'seo_score', 'security_score'])
163
-
164
- def save_history(self, data):
165
- self.history = self.history.append(data, ignore_index=True)
166
- self.history.to_csv('analysis_history.csv', index=False)
167
-
168
  async def analyze_performance(self, url):
169
  try:
170
  start_time = time.time()
171
  async with httpx.AsyncClient() as client:
172
  response = await client.get(url)
173
  load_time = time.time() - start_time
174
- page_size = len(response.content) / 1024
175
 
176
- # تحليل الصور والموارد
177
- soup = BeautifulSoup(response.text, 'html.parser')
178
- images = soup.find_all('img')
179
- scripts = soup.find_all('script')
180
- css_files = soup.find_all('link', {'rel': 'stylesheet'})
181
-
182
- performance_metrics = {
183
- "زمن التحميل": round(load_time, 2),
184
- "حجم الصفحة": round(page_size, 2),
185
- "حالة الاستجابة": response.status_code,
186
- "عدد الصور": len(images),
187
- "عدد ملفات JavaScript": len(scripts),
188
- "عدد ملفات CSS": len(css_files),
189
- "تقييم الأداء": self._calculate_performance_score(load_time, page_size, len(images), len(scripts)),
190
- "توصيات التحسين": self._get_performance_recommendations(load_time, page_size, len(images), len(scripts))
191
  }
192
-
193
- # إضافة تحليل الموارد
194
- resources_analysis = await self._analyze_resources(url)
195
- performance_metrics.update(resources_analysis)
196
-
197
- return performance_metrics
198
  except Exception as e:
199
- return {"error": f"خطأ في تحليل الأداء: {str(e)}"}
200
 
201
- async def _analyze_resources(self, url):
202
  try:
203
  async with httpx.AsyncClient() as client:
204
  response = await client.get(url)
205
  soup = BeautifulSoup(response.text, 'html.parser')
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  # تحليل الصور
208
  images = soup.find_all('img')
209
- image_sizes = []
210
- for img in images[:5]: # تحليل أول 5 صور فقط لتجنب البطء
211
- if img.get('src'):
212
- try:
213
- img_response = await client.get(img['src'])
214
- image_sizes.append(len(img_response.content) / 1024)
215
- except:
216
- continue
 
 
 
 
 
 
 
 
 
 
 
217
 
218
  return {
219
- "تحليل الموارد": {
220
- "متوسط حجم الصور": round(np.mean(image_sizes), 2) if image_sizes else 0,
221
- "عدد الموارد الخارجية": len(soup.find_all(['script', 'link', 'img'])),
222
- "توصيات تحسين الموارد": self._get_resource_recommendations(image_sizes)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  }
224
  }
225
  except Exception as e:
226
- return {"error": f"خطأ في تحليل الموارد: {str(e)}"}
227
 
228
- def _calculate_performance_score(self, load_time, page_size, image_count, script_count):
229
- score = 100
230
-
231
- # تأثير زمن التحميل
232
- if load_time > 2:
233
- score -= min(30, (load_time - 2) * 10)
234
 
235
- # تأثير حجم الصفحة
236
- if page_size > 1000:
237
- score -= min(20, (page_size - 1000) / 100)
 
 
238
 
239
- # تأثير عدد الصور
240
- if image_count > 10:
241
- score -= min(15, (image_count - 10) * 1.5)
 
 
 
242
 
243
- # تأثير عدد ملفات JavaScript
244
- if script_count > 5:
245
- score -= min(15, (script_count - 5) * 2)
246
 
247
- return max(0, round(score))
 
 
 
 
 
 
 
 
 
 
 
248
 
249
- def _get_performance_recommendations(self, load_time, page_size, image_count, script_count):
250
- recommendations = []
251
-
252
- if load_time > 2:
253
- recommendations.append({
254
- "المشكلة": "بطء زمن التحميل",
255
- "الحل": "تحسين سرعة الخادم وتفعيل التخزين المؤقت",
256
- "الأولوية": "عالية"
257
- })
258
-
259
- if page_size > 1000:
260
- recommendations.append({
261
- "المشكلة": "حجم الصفحة كبير",
262
- "الحل": "ضغط الملفات وتحسين الكود",
263
- "الأولوية": "متوسطة"
264
- })
265
 
266
- if image_count > 10:
267
- recommendations.append({
268
- "المشكلة": "عدد كبير من الصور",
269
- "الحل": "تحسين حجم الصور واستخدام التحميل الكسول",
270
- "الأولوية": "متوسطة"
271
- })
272
 
273
- if script_count > 5:
274
- recommendations.append({
275
- "المشكلة": "عدد كبير من ملفات JavaScript",
276
- "الحل": "دمج وضغط ملفات JavaScript",
277
- "الأولوية": "عالية"
278
- })
279
 
280
- return recommendations if recommendations else [{"المشكلة": "لا توجد مشاكل", "الحل": "الأداء جيد!", "الأولوية": "منخفضة"}]
281
-
282
- async def analyze_seo(self, url):
283
- try:
284
- async with httpx.AsyncClient() as client:
285
- response = await client.get(url)
286
- soup = BeautifulSoup(response.text, 'html.parser')
287
-
288
- # تحليل المحتوى
289
- content_analysis = self._analyze_content(soup)
290
-
291
- # تحليل الروابط
292
- links_analysis = self._analyze_links(soup)
293
-
294
- # تحليل الكلمات المفتاحية
295
- keywords_analysis = self._extract_keywords(soup)
296
-
297
- seo_analysis = {
298
- "تحليل العنوان": self._analyze_title(soup),
299
- "تحليل الوصف": self._analyze_description(soup),
300
- "تحليل الكلمات المفتاحية": keywords_analysis,
301
- "تحليل العناوين": self._analyze_headings(soup),
302
- "تحليل الروابط": links_analysis,
303
- "تحليل المحتوى": content_analysis,
304
- "تقييم SEO": self._calculate_seo_score(soup),
305
- "توصيات تحسين SEO": self._get_seo_recommendations(soup)
306
- }
307
-
308
- return seo_analysis
309
  except Exception as e:
310
- return {"error": f"خطأ في تحليل SEO: {str(e)}"}
311
-
312
- def _analyze_content(self, soup):
313
- # استخراج النص
314
- text_content = ' '.join([p.text for p in soup.find_all('p')])
315
-
316
- # تحليل طول المحتوى
317
- word_count = len(text_content.split())
318
-
319
- # تحليل قراءة المحتوى
320
- readability_score = self._calculate_readability(text_content)
321
-
322
- # تحليل كثافة الكلمات المفتاحية
323
- keyword_density = self._calculate_keyword_density(text_content)
324
-
325
- return {
326
- "عدد الكلمات": word_count,
327
- "مستوى القراءة": readability_score,
328
- "كثافة الكلمات المفتاحية": keyword_density,
329
- "التقييم": "ممتاز" if word_count > 300 and readability_score > 60 else "يحتاج تحسين"
330
- }
331
-
332
- def _calculate_readability(self, text):
333
- # حساب مؤشر بسيط لسهولة القراءة
334
- sentences = len(re.split(r'[.!?]+', text))
335
- words = len(text.split())
336
- if sentences == 0:
337
- return 0
338
- return min(100, round((words / sentences) * 10))
339
-
340
- def _calculate_keyword_density(self, text):
341
- words = text.lower().split()
342
- word_freq = Counter(words)
343
- total_words = len(words)
344
-
345
- if total_words == 0:
346
- return {}
347
-
348
- return {word: round((count / total_words) * 100, 2)
349
- for word, count in word_freq.most_common(5)}
350
 
351
- def analyze_security(self, url):
352
- try:
353
- domain = urlparse(url).netloc
354
- whois_info = self._get_whois_info(domain)
 
 
 
 
 
 
 
 
 
 
 
355
 
356
- security_analysis = {
357
- "تحليل SSL": self._check_ssl(url),
358
- "تحليل DNS": self._check_dns(domain),
359
- "تحليل Headers": self._check_security_headers(url),
360
- "فحص المخاطر": self._check_security_risks(url),
361
- "معلومات Whois": whois_info,
362
- "تقييم الأمان": self._calculate_security_score(url),
363
- "توصيات الأمان": self._get_security_recommendations(url)
364
- }
365
- return security_analysis
366
- except Exception as e:
367
- return {"error": f"خطأ في تحليل الأمان: {str(e)}"}
368
-
369
- def _get_whois_info(self, domain):
370
- try:
371
- w = whois.whois(domain)
372
- return {
373
- "اسم النطاق": domain,
374
- "تاريخ التسجيل": str(w.creation_date),
375
- "تاريخ الانتهاء": str(w.expiration_date),
376
- "المسجل": w.registrar,
377
- "الحالة": w.status
378
- }
379
- except:
380
- return {"error": "لا يمكن الحصول على معلومات Whois"}
381
-
382
- def _check_ssl(self, url):
383
- try:
384
- context = ssl.create_default_context()
385
- with socket.create_connection((urlparse(url).netloc, 443)) as sock:
386
- with context.wrap_socket(sock, server_hostname=urlparse(url).netloc) as ssock:
387
- cert = ssock.getpeercert()
388
- return {
389
- "الحالة": "آمن ✅",
390
- "نوع الشهادة": cert.get('subject', {}).get('commonName', 'Unknown'),
391
- "تاريخ الإصدار": cert.get('notBefore', 'Unknown'),
392
- "تاريخ الانتهاء": cert.get('notAfter', 'Unknown'),
393
- "الخوارزمية": ssock.cipher()[0],
394
- "قوة التشفير": f"{ssock.cipher()[2]} bits"
395
- }
396
- except:
397
- return {
398
- "الحالة": "غير آمن ❌",
399
- "السبب": "لا يوجد شهادة SSL صالحة"
400
- }
401
-
402
- def _check_security_headers(self, url):
403
- try:
404
- response = requests.get(url)
405
- headers = response.headers
406
- security_headers = {
407
- 'Strict-Transport-Security': 'HSTS',
408
- 'Content-Security-Policy': 'CSP',
409
- 'X-Frame-Options': 'X-Frame',
410
- 'X-Content-Type-Options': 'X-Content-Type',
411
- 'X-XSS-Protection': 'XSS Protection'
412
- }
413
 
414
- results = {}
415
- for header, name in security_headers.items():
416
- results[name] = {
417
- "موجود": header in headers,
418
- "القيمة": headers.get(header, "غير موجود")
419
- }
420
- return results
421
- except:
422
- return {"error": "فشل فحص headers الأمان"}
423
-
424
- def _check_security_risks(self, url):
425
- risks = []
426
-
427
- # فحص بروتوكول HTTP
428
- if not url.startswith('https'):
429
- risks.append({
430
- "المستوى": "عالي",
431
- "النوع": "بروتوكول غير آمن",
432
- "الوصف": "الموقع يستخدم HTTP بدلاً من HTTPS"
433
- })
434
-
435
- # فحص تحديث شهادة SSL
436
- ssl_info = self._check_ssl(url)
437
- if ssl_info.get("الحالة") == "غير آمن ❌":
438
- risks.append({
439
- "المستوى": "عالي",
440
- "النوع": "شهادة SSL",
441
- "الوصف": "شهادة SSL غير صالحة أو منتهية"
442
- })
443
-
444
- # فحص headers الأمان
445
- headers = self._check_security_headers(url)
446
- if isinstance(headers, dict) and not headers.get("HSTS", {}).get("موجود"):
447
- risks.append({
448
- "المستوى": "متوسط",
449
- "النوع": "HSTS غير مفعل",
450
- "الوصف": "عدم وجود حماية النقل الآمن الصارم"
451
- })
452
-
453
- return {
454
- "المخاطر المكتشفة": risks,
455
- "عدد المخاطر": len(risks),
456
- "مستوى الخطورة": "عالي" if any(r["المستوى"] == "عالي" for r in risks) else "متوسط" if risks else "منخفض"
457
- }
458
-
459
- def _calculate_security_score(self, url):
460
- score = 100
461
 
462
- # فحص HTTPS
463
- if not url.startswith('https'):
464
- score -= 30
465
 
466
- # فحص SSL
467
- ssl_info = self._check_ssl(url)
468
- if ssl_info.get("الحالة") == "غير آمن ❌":
469
- score -= 25
470
-
471
- # فحص Headers
472
- headers = self._check_security_headers(url)
473
- if isinstance(headers, dict):
474
- for header_info in headers.values():
475
- if not header_info.get("موجود"):
476
- score -= 5
477
-
478
- # فحص مخاطر الأمان
479
- risks = self._check_security_risks(url)
480
- score -= (risks.get("عدد المخاطر", 0) * 10)
481
-
482
- return max(0, score)
483
 
484
- def _get_security_recommendations(self, url):
485
- recommendations = []
486
-
487
- # فحص HTTPS
488
- if not url.startswith('https'):
489
- recommendations.append({
490
- "المشكلة": "عدم استخدام HTTPS",
491
- "الحل": "قم بتفعيل HTTPS وتثبيت شهادة SSL",
492
- "الأولوية": "عالية"
493
- })
494
-
495
- # فحص SSL
496
- ssl_info = self._check_ssl(url)
497
- if ssl_info.get("الحالة") == "غير آمن ❌":
498
- recommendations.append({
499
- "المشكلة": "شهادة SSL غير صالحة",
500
- "الحل": "قم بتجديد أو تثبيت شهادة SSL جديدة",
501
- "الأولوية": "عالية"
502
- })
503
-
504
- # فحص Headers
505
- headers = self._check_security_headers(url)
506
- if isinstance(headers, dict):
507
- for name, info in headers.items():
508
- if not info.get("موجود"):
509
- recommendations.append({
510
- "المشكلة": f"عدم وجود {name}",
511
- "الحل": f"قم بإضافة header الأمان {name}",
512
- "الأولوية": "متوسطة"
513
- })
514
-
515
- return recommendations if recommendations else [
516
- {
517
- "المشكلة": "لا توجد مشاكل أمنية واضحة",
518
- "الحل": "استمر في مراقبة وتحديث إعدادات الأمان",
519
- "الأولوية": "منخفضة"
520
- }
521
- ]
 
1
  import streamlit as st
2
+ from streamlit_lottie import st_lottie
3
  from streamlit_option_menu import option_menu
4
  import requests
5
  import pandas as pd
 
17
  from urllib.parse import urlparse
18
  import json
19
  import numpy as np
20
+ from selenium import webdriver
21
+ from selenium.webdriver.chrome.options import Options
22
+ from webdriver_manager.chrome import ChromeDriverManager
23
  from PIL import Image
24
  import io
25
  import time
 
 
 
 
 
 
 
 
 
26
 
27
+ # تخصيص المظهر
28
+ st.set_page_config(layout="wide", page_title="محلل المواقع المتقدم")
 
 
 
 
 
29
 
30
+ # تحميل الأنيميشن
31
+ def load_lottieurl(url):
32
+ try:
33
+ r = requests.get(url)
34
+ r.raise_for_status()
35
+ return r.json()
36
+ except Exception as e:
37
+ # Fallback to a basic loading animation JSON
38
+ return {
39
+ "v": "5.5.7",
40
+ "fr": 29.9700012207031,
41
+ "ip": 0,
42
+ "op": 180.00000733155,
43
+ "w": 500,
44
+ "h": 500,
45
+ "nm": "Loading",
46
+ "ddd": 0,
47
+ "assets": [],
48
+ "layers": [
49
+ {
50
+ "ddd": 0,
51
+ "ind": 1,
52
+ "ty": 4,
53
+ "nm": "Loading Circle",
54
+ "sr": 1,
55
+ "ks": {
56
+ "o": {"a": 0, "k": 100, "ix": 11},
57
+ "r": {"a": 1, "k": [{"i": {"x": [0.833], "y": [0.833]}, "o": {"x": [0.167], "y": [0.167]}, "t": 0, "s": [0], "e": [360]}, {"t": 180.00000733155}], "ix": 10}
58
+ }
59
+ }
60
+ ]
61
+ }
62
+
63
+ # تحميل الأنيميشن الافتراضي
64
+ lottie_analyzing = load_lottieurl("https://assets5.lottiefiles.com/packages/lf20_qpwbqki6.json")
65
+
66
+ # تصميم CSS مخصص
67
  st.markdown("""
68
  <style>
 
 
 
 
 
 
69
  .main {
70
+ background-color: #f0f2f6;
 
 
 
 
 
 
 
 
 
 
71
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  .stButton>button {
 
73
  color: white;
74
+ background-color: #ff4b4b;
75
+ border-radius: 10px;
76
+ padding: 15px 25px;
77
  border: none;
 
 
 
 
 
78
  }
 
79
  .stButton>button:hover {
80
+ background-color: #ff6b6b;
81
+ border: none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
+ .metric-card {
 
84
  background-color: white;
85
  border-radius: 10px;
 
 
 
 
 
 
 
 
 
86
  padding: 20px;
87
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
  </style>
90
  """, unsafe_allow_html=True)
91
 
92
+ class WebsiteAnalyzer:
93
  def __init__(self):
94
  self.headers = {
95
+ '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'
96
  }
97
+
 
 
 
 
 
 
 
 
 
 
 
98
  async def analyze_performance(self, url):
99
  try:
100
  start_time = time.time()
101
  async with httpx.AsyncClient() as client:
102
  response = await client.get(url)
103
  load_time = time.time() - start_time
104
+ page_size = len(response.content) / 1024 # KB
105
 
106
+ return {
107
+ "load_time": round(load_time, 2),
108
+ "page_size": round(page_size, 2),
109
+ "status_code": response.status_code
 
 
 
 
 
 
 
 
 
 
 
110
  }
 
 
 
 
 
 
111
  except Exception as e:
112
+ return {"error": str(e)}
113
 
114
+ async def analyze_seo(self, url):
115
  try:
116
  async with httpx.AsyncClient() as client:
117
  response = await client.get(url)
118
  soup = BeautifulSoup(response.text, 'html.parser')
119
 
120
+ # تحليل العناوين مع تقييم
121
+ headings = {
122
+ 'h1': len(soup.find_all('h1')),
123
+ 'h2': len(soup.find_all('h2')),
124
+ 'h3': len(soup.find_all('h3'))
125
+ }
126
+
127
+ # تقييم العناوين
128
+ heading_score = 0
129
+ if headings['h1'] > 0:
130
+ heading_score += 20 # تأكد من وجود H1
131
+ heading_score += min(headings['h2'], 3) * 5 # حتى 3 عناوين H2
132
+ heading_score += min(headings['h3'], 3) * 3 # حتى 3 عناوين H3
133
+
134
+ # تحليل الروابط
135
+ links = soup.find_all('a')
136
+ internal_links = len([link for link in links if urlparse(link.get('href', '')).netloc == urlparse(url).netloc])
137
+ external_links = len(links) - internal_links
138
+
139
  # تحليل الصور
140
  images = soup.find_all('img')
141
+ images_without_alt = len([img for img in images if not img.get('alt')])
142
+ images_alt_score = max(0, 100 - (images_without_alt * 5)) # خصم 5 نقاط لكل صورة بدون Alt
143
+
144
+ # تحليل العنوان والوصف
145
+ title = soup.title.string if soup.title else "لا يوجد عنوان"
146
+ meta_description = soup.find("meta", {"name": "description"}) or soup.find("meta", {"property": "og:description"})
147
+ description_text = meta_description.get("content") if meta_description else "لا يوجد وصف"
148
+
149
+ # حساب نقاط العنوان والوصف
150
+ title_score = 0
151
+ if title and len(title) > 10 and len(title) <= 60:
152
+ title_score = 20
153
+
154
+ description_score = 0
155
+ if description_text != "لا يوجد وصف" and 70 <= len(description_text) <= 160:
156
+ description_score = 20
157
+
158
+ # حساب النقاط الإجمالية
159
+ total_seo_score = heading_score + images_alt_score + title_score + description_score
160
 
161
  return {
162
+ "title": {
163
+ "text": title,
164
+ "score": title_score,
165
+ "recommendation": "يجب أن يكون العنوان بين 10-60 حرفًا" if title_score == 0 else "العنوان مثالي"
166
+ },
167
+ "meta_description": {
168
+ "text": description_text,
169
+ "score": description_score,
170
+ "recommendation": "يجب أن يكون الوصف بين 70-160 حرفًا" if description_score == 0 else "الوصف مثالي"
171
+ },
172
+ "headings": {
173
+ "details": headings,
174
+ "score": heading_score,
175
+ "recommendation": "تأكد من وجود عنوان H1 واستخدم العناوين الفرعية بشكل هرمي"
176
+ },
177
+ "internal_links": {
178
+ "count": internal_links,
179
+ "score": min(internal_links, 20),
180
+ "recommendation": "الروابط الداخلية جيدة للتنقل وتحسين SEO"
181
+ },
182
+ "external_links": {
183
+ "count": external_links,
184
+ "recommendation": "استخدم الروابط الخارجية بحكمة للمصداقية"
185
+ },
186
+ "images": {
187
+ "total": len(images),
188
+ "without_alt": images_without_alt,
189
+ "alt_score": images_alt_score,
190
+ "recommendation": f"أضف نص بديل لـ {images_without_alt} صورة"
191
+ },
192
+ "total_seo_score": {
193
+ "score": total_seo_score,
194
+ "max_score": 100,
195
+ "grade": (
196
+ "ممتاز" if total_seo_score >= 80 else
197
+ "جيد" if total_seo_score >= 60 else
198
+ "متوسط" if total_seo_score >= 40 else
199
+ "ضعيف"
200
+ )
201
  }
202
  }
203
  except Exception as e:
204
+ return {"error": str(e)}
205
 
206
+ def analyze_security(self, url):
207
+ try:
208
+ domain = urlparse(url).netloc
 
 
 
209
 
210
+ # فحص SSL
211
+ ctx = ssl.create_default_context()
212
+ with ctx.wrap_socket(socket.socket(), server_hostname=domain) as s:
213
+ s.connect((domain, 443))
214
+ cert = s.getpeercert()
215
 
216
+ # فحص DNS
217
+ dns_records = {
218
+ 'A': [str(r) for r in dns.resolver.resolve(domain, 'A')],
219
+ 'MX': [str(r) for r in dns.resolver.resolve(domain, 'MX')],
220
+ 'TXT': [str(r) for r in dns.resolver.resolve(domain, 'TXT')]
221
+ }
222
 
223
+ # معلومات WHOIS
224
+ domain_info = whois.whois(domain)
 
225
 
226
+ return {
227
+ "ssl_valid": True,
228
+ "ssl_expiry": cert['notAfter'],
229
+ "dns_records": dns_records,
230
+ "domain_info": {
231
+ "registrar": domain_info.registrar,
232
+ "creation_date": domain_info.creation_date,
233
+ "expiration_date": domain_info.expiration_date
234
+ }
235
+ }
236
+ except Exception as e:
237
+ return {"error": str(e)}
238
 
239
+ async def take_screenshot(self, url):
240
+ try:
241
+ chrome_options = Options()
242
+ chrome_options.add_argument('--headless')
243
+ chrome_options.add_argument('--no-sandbox')
244
+ chrome_options.add_argument('--disable-dev-shm-usage')
 
 
 
 
 
 
 
 
 
 
245
 
246
+ driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options)
247
+ driver.get(url)
248
+ driver.set_window_size(1920, 1080)
 
 
 
249
 
250
+ screenshot = driver.get_screenshot_as_png()
251
+ driver.quit()
 
 
 
 
252
 
253
+ return Image.open(io.BytesIO(screenshot))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  except Exception as e:
255
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
 
257
+ def main():
258
+ st.title("🔍 محلل المواقع المتقدم")
259
+
260
+ # إضافة قائمة جانبية
261
+ with st.sidebar:
262
+ selected = option_menu(
263
+ menu_title="القائمة الرئيسية",
264
+ options=["تحليل جديد", "التقارير السابقة", "الإعدادات"],
265
+ icons=["search", "file-text", "gear"],
266
+ menu_icon="cast",
267
+ default_index=0,
268
+ )
269
+
270
+ if selected == "تحليل جديد":
271
+ col1, col2 = st.columns([2, 1])
272
 
273
+ with col1:
274
+ url = st.text_input("أدخل رابط الموقع", "https://example.com")
275
+ if st.button("بدء التحليل"):
276
+ with st.spinner("جاري التحليل..."):
277
+ st_lottie(lottie_analyzing, height=200)
278
+
279
+ analyzer = WebsiteAnalyzer()
280
+
281
+ # تحليل متزامن
282
+ loop = asyncio.new_event_loop()
283
+ asyncio.set_event_loop(loop)
284
+ performance_data = loop.run_until_complete(analyzer.analyze_performance(url))
285
+ seo_data = loop.run_until_complete(analyzer.analyze_seo(url))
286
+ security_data = analyzer.analyze_security(url)
287
+ screenshot = loop.run_until_complete(analyzer.take_screenshot(url))
288
+
289
+ # عرض النتائج
290
+ st.success("تم اكتمال التحليل!")
291
+
292
+ # عرض البطاقات الإحصائية
293
+ cols = st.columns(3)
294
+ with cols[0]:
295
+ st.metric("زمن التحميل", f"{performance_data.get('load_time', 'N/A')}s")
296
+ with cols[1]:
297
+ st.metric("حجم الصفحة", f"{performance_data.get('page_size', 'N/A')} KB")
298
+ with cols[2]:
299
+ st.metric("الروابط الداخلية", seo_data.get('internal_links', 'N/A')['count'])
300
+
301
+ # عرض تحليل SEO
302
+ with st.expander("تحليل SEO", expanded=True):
303
+ st.json(seo_data)
304
+
305
+ # رسم بياني للعناوين
306
+ if 'headings' in seo_data and 'details' in seo_data['headings']:
307
+ fig = px.bar(
308
+ x=list(seo_data['headings']['details'].keys()),
309
+ y=list(seo_data['headings']['details'].values()),
310
+ title="توزيع العناوين",
311
+ labels={'x': 'نوع العنوان', 'y': 'العدد'}
312
+ )
313
+ st.plotly_chart(fig)
314
+
315
+ # عرض تحليل الأمان
316
+ with st.expander("تحليل الأمان", expanded=True):
317
+ st.json(security_data)
318
+
319
+ # عرض لقطة الشاشة
320
+ if screenshot:
321
+ st.image(screenshot, caption="لقطة شاشة للموقع", use_column_width=True)
 
 
 
 
 
 
 
 
322
 
323
+ with col2:
324
+ st.subheader("آخر التحليلات")
325
+ # هنا يمكن إضافة قائمة بآخر المواقع التي تم تحليلها
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
+ elif selected == "التقارير السابقة":
328
+ st.subheader("التقارير السابقة")
329
+ # هنا يمكن إضافة عرض للتقارير السابقة
330
 
331
+ elif selected == "الإعدادات":
332
+ st.subheader("إعدادات التحليل")
333
+ # هنا يمكن إضافة إعدادات التحليل
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
+ if __name__ == "__main__":
336
+ main()