joermd commited on
Commit
3a8824c
·
verified ·
1 Parent(s): 9275131

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -583
app.py CHANGED
@@ -14,7 +14,7 @@ import whois
14
  import ssl
15
  import socket
16
  import dns.resolver
17
- from urllib.parse import urlparse
18
  import json
19
  import numpy as np
20
  from selenium import webdriver
@@ -29,6 +29,10 @@ from fake_useragent import UserAgent
29
  from concurrent.futures import ThreadPoolExecutor
30
  import re
31
  from urllib.robotparser import RobotFileParser
 
 
 
 
32
 
33
  # تهيئة المتغيرات العامة
34
  TIMEOUT = 10
@@ -63,7 +67,6 @@ class WebsiteAnalyzer:
63
 
64
  async def analyze_performance(self, url):
65
  try:
66
- # قياس سرعة التحميل بشكل واقعي
67
  performance_metrics = {
68
  'dns_lookup': [],
69
  'tcp_handshake': [],
@@ -71,640 +74,199 @@ class WebsiteAnalyzer:
71
  'content_download': []
72
  }
73
 
74
- for _ in range(3): # أخذ متوسط 3 قياسات
 
 
 
 
 
 
75
  start_time = time.time()
76
 
77
- # DNS Lookup
 
 
 
 
 
 
 
 
78
  domain = urlparse(url).netloc
79
  dns_start = time.time()
80
  socket.gethostbyname(domain)
81
  performance_metrics['dns_lookup'].append(time.time() - dns_start)
82
 
83
- # قياس باقي المؤشرات
84
  response = await self._fetch_with_retry(url)
85
-
86
  performance_metrics['ttfb'].append(response.elapsed.total_seconds())
87
  performance_metrics['content_download'].append(time.time() - start_time - response.elapsed.total_seconds())
88
 
89
- # حساب المتوسطات
90
- avg_metrics = {k: np.mean(v) for k, v in performance_metrics.items()}
91
-
92
- # تحليل حجم الصفحة ومكوناتها
93
  soup = BeautifulSoup(response.text, 'html.parser')
94
-
95
- # تحليل الموارد
96
- resources = {
97
- 'images': len(soup.find_all('img')),
98
- 'scripts': len(soup.find_all('script')),
99
- 'stylesheets': len(soup.find_all('link', rel='stylesheet')),
100
- 'total_size': len(response.content) / 1024 # KB
101
- }
102
-
103
- # تقدير حركة المرور باستخدام بيانات من مصادر مختلفة
104
- traffic_estimate = await self._estimate_real_traffic(url)
105
 
106
  return {
107
  "أداء الموقع": {
108
- "زمن التحميل الكلي": f"{sum(avg_metrics.values()):.2f} ثانية",
109
- "زمن الاستجابة الأول": f"{avg_metrics['ttfb']:.2f} ثانية",
110
- "تقييم السرعة": self._evaluate_speed(sum(avg_metrics.values())),
111
- "حجم الصفحة": f"{resources['total_size']:.1f} KB"
112
- },
113
- "تحليل الموارد": {
114
- "عدد الصور": resources['images'],
115
- "عدد ملفات JavaScript": resources['scripts'],
116
- "عدد ملفات CSS": resources['stylesheets']
117
- },
118
- "إحصائيات الزوار": {
119
- "متوسط الزيارات الشهرية": f"{traffic_estimate:,}",
120
- "تقدير المستخدمين النشطين": f"{int(traffic_estimate * 0.4):,}",
121
- "معدل الارتداد التقريبي": f"{random.randint(35, 65)}%"
122
- },
123
- "التوصيات": self._generate_performance_recommendations(avg_metrics, resources)
124
  }
125
  except Exception as e:
126
  return {"error": f"حدث خطأ أثناء تحليل الأداء: {str(e)}"}
127
 
128
- async def _estimate_real_traffic(self, url):
129
- """تقدير حركة المرور باستخدام مصادر متعددة"""
130
- domain = urlparse(url).netloc
131
-
132
- try:
133
- # استخدام بيانات من SimilarWeb API (تحتاج لمفتاح API حقيقي)
134
- similar_web_traffic = await self._get_similarweb_data(domain)
135
-
136
- # استخدام بيانات من Alexa (إذا كانت متوفرة)
137
- alexa_rank = await self._get_alexa_rank(domain)
138
-
139
- # حساب تقدير مركب
140
- if similar_web_traffic and alexa_rank:
141
- estimated_traffic = (similar_web_traffic + self._rank_to_traffic(alexa_rank)) / 2
142
- else:
143
- estimated_traffic = similar_web_traffic or self._rank_to_traffic(alexa_rank) or self._estimate_baseline_traffic(domain)
144
-
145
- return int(estimated_traffic)
146
- except:
147
- return self._estimate_baseline_traffic(domain)
148
 
149
- def _estimate_baseline_traffic(self, domain):
150
- """تقدير أساسي للحركة بناءً على عمر النطاق وعوامل أخرى"""
151
- try:
152
- domain_info = whois.whois(domain)
153
- domain_age = (datetime.now() - domain_info.creation_date[0]).days if isinstance(domain_info.creation_date, list) else (datetime.now() - domain_info.creation_date).days
154
-
155
- # معادلة تقريبية تأخذ في الاعتبار عمر النطاق
156
- base_traffic = np.random.normal(5000, 1000) # توزيع طبيعي للحركة الأساسية
157
- age_factor = min(domain_age / 365, 5) # تأثير العمر حتى 5 سنوات
158
-
159
- estimated_traffic = base_traffic * (1 + age_factor * 0.5)
160
- return int(max(500, min(estimated_traffic, 100000))) # حدود معقولة
161
- except:
162
- return random.randint(1000, 10000)
163
 
164
- async def analyze_seo(self, url):
 
165
  try:
166
- response = await self._fetch_with_retry(url)
167
- soup = BeautifulSoup(response.text, 'html.parser')
168
 
169
- # تحليل العنوان
170
- title = soup.title.string if soup.title else ""
171
- title_score = self._analyze_title(title)
172
-
173
- # تحليل الوصف
174
- meta_description = soup.find("meta", {"name": "description"})
175
- description = meta_description['content'] if meta_description else ""
176
- description_score = self._analyze_description(description)
177
-
178
- # تحليل الكلمات المفتاحية
179
- keywords = self._extract_keywords(soup)
180
-
181
- # تحليل الروابط
182
- internal_links, external_links = self._analyze_links(soup, url)
183
-
184
- # تحليل المحتوى
185
- content_analysis = self._analyze_content(soup)
186
 
187
  return {
188
- "تحليل العناوين": {
189
- "العنوان الرئيسي": title[:60] + "..." if len(title) > 60 else title,
190
- "طول العنوان": len(title),
191
- "تقييم العنوان": title_score['score'],
192
- "التوصيات": title_score['recommendations']
193
- },
194
- "تحليل الوصف": {
195
- "نص الوصف": description[:100] + "..." if len(description) > 100 else description,
196
- "طول الوصف": len(description),
197
- "تقييم الوصف": description_score['score'],
198
- "التوصيات": description_score['recommendations']
199
- },
200
- "تحليل الكلمات المفتاحية": {
201
- "الكلمات الرئيسية المكتشفة": keywords[:5],
202
- "كثافة الكلمات المفتاحية": content_analysis['keyword_density'],
203
- "التوصيات": content_analysis['recommendations']
204
- },
205
- "تحليل الروابط": {
206
- "الروابط الداخلية": len(internal_links),
207
- "الروابط الخارجية": len(external_links),
208
- "نسبة الروابط الداخلية/الخارجية": f"{len(internal_links)/max(len(external_links), 1):.1f}"
209
- },
210
- "تحليل المحتوى": {
211
- "عدد الكلمات": content_analysis['word_count'],
212
- "تنوع المحتوى": content_analysis['content_diversity'],
213
- "قابلية القراءة": content_analysis['readability']
214
- }
215
  }
216
  except Exception as e:
217
- return {"error": f"حدث خطأ أثناء تحليل SEO: {str(e)}"}
218
 
219
- def _analyze_title(self, title):
220
- if not title:
221
- return {
222
- 'score': "0/10",
223
- 'recommendations': ["يجب إضافة عنوان للصفحة"]
224
- }
225
 
226
- score = 10
227
- recommendations = []
 
 
 
 
 
 
228
 
229
- if len(title) < 30:
230
- score -= 2
231
- recommendations.append("العنوان قصير جداً، يُفضل أن يكون بين 50-60 حرفاً")
232
- elif len(title) > 60:
233
- score -= 2
234
- recommendations.append("العنوان طويل جداً، يجب تقصيره إلى 60 حرفاً كحد أقصى")
235
-
236
- if not any(char.isupper() for char in title):
237
- score -= 1
238
- recommendations.append("استخدم بعض الأحرف الكبيرة في بداية الكلمات المهمة")
239
-
240
  return {
241
- 'score': f"{score}/10",
242
- 'recommendations': recommendations
 
 
 
 
 
 
 
243
  }
244
 
245
- def analyze_security(self, url):
 
246
  try:
247
- domain = urlparse(url).netloc
248
-
249
- # فحص شهادة SSL
250
- ssl_info = self._check_ssl(domain)
251
 
252
- # فحص سجلات DNS
253
- dns_info = self._check_dns(domain)
254
 
255
- # فحص مستوى الحماية من التهديدات
256
- security_headers = self._check_security_headers(url)
257
-
258
- # فحص معلومات التسجيل
259
- registration_info = self._get_domain_info(domain)
260
 
261
  return {
262
- "تحليل الأمان": {
263
- "شهادة SSL": ssl_info,
264
- "سجلات DNS": dns_info,
265
- "رؤوس الأمان": security_headers,
266
- "معلومات التسجيل": registration_info,
267
- "درجة الأمان الكلية": self._calculate_security_score(ssl_info, security_headers)
268
  }
269
  }
270
  except Exception as e:
271
- return {"error": f"حدث خطأ أثناء تحليل الأمان: {str(e)}"}
272
 
273
- def _check_ssl(self, domain):
 
274
  try:
275
- context = ssl.create_default_context()
276
- with socket.create_connection((domain, 443)) as sock:
277
- with context.wrap_socket(sock, server_hostname=domain) as ssock:
278
- cert = ssock.getpeercert()
279
-
280
- not_after = datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
281
- days_left = (not_after - datetime.now()).days
282
-
283
- return {
284
- "الحالة": "✅ آمن" if days_left > 0 else "❌ منتهي",
285
- "نوع الشهادة": cert.get('issuer')[1][0][1],
286
- "تاريخ الانتهاء": not_after.strftime('%Y-%m-%d'),
287
- "الأيام المتبقية": days_left,
288
- "مستوى التشفير": "عالي (TLS 1.3)" if ssl.PROTOCOL_TLSv1_3 else "متوسط (TLS 1.2)"
289
- }
290
- except Exception as e:
291
- return {
292
- "الحالة": "❌ غير آمن",
293
- "السبب": str(e)
294
  }
295
-
296
- def _check_security_headers(self, url):
297
- try:
298
- response = requests.get(url)
299
- headers = response.headers
300
 
301
- security_headers = {
302
- 'Strict-Transport-Security': 'HSTS',
303
- 'Content-Security-Policy': 'CSP',
304
- 'X-Frame-Options': 'X-Frame',
305
- 'X-Content-Type-Options': 'X-Content-Type',
306
- 'X-XSS-Protection': 'XSS Protection'
307
- }
308
 
309
- results = {}
310
- for header, name in security_headers.items
311
- def _check_security_headers(self, url):
312
- try:
313
- response = requests.get(url)
314
- headers = response.headers
315
-
316
- security_headers = {
317
- 'Strict-Transport-Security': 'HSTS',
318
- 'Content-Security-Policy': 'CSP',
319
- 'X-Frame-Options': 'X-Frame',
320
- 'X-Content-Type-Options': 'X-Content-Type',
321
- 'X-XSS-Protection': 'XSS Protection',
322
- 'Referrer-Policy': 'Referrer Policy',
323
- 'Permissions-Policy': 'Permissions Policy',
324
- 'Cross-Origin-Embedder-Policy': 'COEP',
325
- 'Cross-Origin-Opener-Policy': 'COOP',
326
- 'Cross-Origin-Resource-Policy': 'CORP'
327
- }
328
-
329
- results = {}
330
- score = 100
331
- recommendations = []
332
-
333
- for header, name in security_headers.items(): # Fixed here: added () to items
334
- if header in headers:
335
- results[name] = {
336
- "موجود": "✅",
337
- "القيمة": headers[header]
338
- }
339
- else:
340
- results[name] = {
341
- "موجود": "❌",
342
- "التوصية": self._get_header_recommendation(header)
343
  }
344
- score -= 10
345
- recommendations.append(f"إضافة رأس {name}")
346
-
347
- return {
348
- "الرؤوس الموجودة": results,
349
- "درجة الأمان": f"{max(score, 0)}/100",
350
- "التوصيات": recommendations,
351
- "المستوى العام": self._get_security_level(score)
352
- }
353
- except Exception as e:
354
- return {"error": f"خطأ في فحص رؤوس الأمان: {str(e)}"}
355
-
356
- def _get_header_recommendation(self, header):
357
- recommendations = {
358
- 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
359
- 'Content-Security-Policy': "default-src 'self'",
360
- 'X-Frame-Options': 'SAMEORIGIN',
361
- 'X-Content-Type-Options': 'nosniff',
362
- 'X-XSS-Protection': '1; mode=block',
363
- 'Referrer-Policy': 'strict-origin-when-cross-origin',
364
- 'Permissions-Policy': 'geolocation=(), microphone=()',
365
- 'Cross-Origin-Embedder-Policy': 'require-corp',
366
- 'Cross-Origin-Opener-Policy': 'same-origin',
367
- 'Cross-Origin-Resource-Policy': 'same-origin'
368
- }
369
- return recommendations.get(header, 'قيمة موصى بها غير متوفرة')
370
-
371
- def _get_security_level(self, score):
372
- if score >= 90:
373
- return "ممتاز 🔒"
374
- elif score >= 70:
375
- return "جيد 🔔"
376
- elif score >= 50:
377
- return "متوسط ⚠️"
378
- else:
379
- return "ضعيف ⛔"
380
-
381
- def _check_dns(self, domain):
382
- try:
383
- results = {
384
- "سجلات": {},
385
- "توصيات": [],
386
- "درجة الأمان": 100
387
  }
388
-
389
- # فحص سجلات A
390
- try:
391
- a_records = dns.resolver.resolve(domain, 'A')
392
- results["سجلات"]["A"] = [str(record) for record in a_records]
393
- except:
394
- results["توصيات"].append("تعذر الوصول لسجلات A")
395
- results["درجة الأمان"] -= 20
396
-
397
- # فحص سجلات MX
398
- try:
399
- mx_records = dns.resolver.resolve(domain, 'MX')
400
- results["سجلات"]["MX"] = [str(record.exchange) for record in mx_records]
401
- except:
402
- results["توصيات"].append("تعذر الوصول لسجلات MX")
403
- results["درجة الأمان"] -= 10
404
-
405
- # فحص سجلات TXT
406
- try:
407
- txt_records = dns.resolver.resolve(domain, 'TXT')
408
- results["سجلات"]["TXT"] = [str(record) for record in txt_records]
409
-
410
- # التحقق من وجود سجلات SPF و DMARC
411
- spf_found = any("v=spf1" in str(record) for record in txt_records)
412
- dmarc_found = any("v=DMARC1" in str(record) for record in txt_records)
413
-
414
- if not spf_found:
415
- results["توصيات"].append("إضافة سجل SPF لحماية البريد الإلكتروني")
416
- results["درجة الأمان"] -= 15
417
- if not dmarc_found:
418
- results["توصيات"].append("إضافة سجل DMARC لحماية البريد الإلكتروني")
419
- results["درجة الأمان"] -= 15
420
-
421
- except:
422
- results["توصيات"].append("تعذر الوصول لسجلات TXT")
423
- results["درجة الأمان"] -= 10
424
-
425
- # فحص سجلات AAAA (IPv6)
426
- try:
427
- aaaa_records = dns.resolver.resolve(domain, 'AAAA')
428
- results["سجلات"]["AAAA"] = [str(record) for record in aaaa_records]
429
- except:
430
- results["توصيات"].append("لا يوجد دعم IPv6")
431
- results["درجة الأمان"] -= 5
432
-
433
- # إضافة تقييم عام
434
- results["التقييم العام"] = self._evaluate_dns_security(results["درجة الأمان"])
435
-
436
- return results
437
  except Exception as e:
438
- return {"error": f"خطأ في فحص سجلات DNS: {str(e)}"}
439
-
440
- def _evaluate_dns_security(self, score):
441
- if score >= 90:
442
- return "حماية DNS ممتازة ✅"
443
- elif score >= 70:
444
- return "حماية DNS جيدة 🔔"
445
- elif score >= 50:
446
- return "حماية DNS متوسطة ⚠️"
447
- else:
448
- return "حماية DNS ضعيفة ⛔"
449
 
450
- def _get_domain_info(self, domain):
 
451
  try:
452
- domain_info = whois.whois(domain)
453
-
454
- # تحويل التواريخ إلى صيغة مقروءة
455
- creation_date = domain_info.creation_date
456
- expiration_date = domain_info.expiration_date
457
-
458
- if isinstance(creation_date, list):
459
- creation_date = creation_date[0]
460
- if isinstance(expiration_date, list):
461
- expiration_date = expiration_date[0]
462
-
463
- # حساب عمر النطاق
464
- domain_age = (datetime.now() - creation_date).days if creation_date else None
465
- days_to_expiry = (expiration_date - datetime.now()).days if expiration_date else None
466
-
467
- return {
468
- "معلومات التسجيل": {
469
- "اسم النطاق": domain,
470
- "تاريخ التسجيل": creation_date.strftime('%Y-%m-%d') if creation_date else "غير متوفر",
471
- "تاريخ الانتهاء": expiration_date.strftime('%Y-%m-%d') if expiration_date else "غير متوفر",
472
- "عمر النطاق": f"{domain_age} يوم" if domain_age else "غير متوفر",
473
- "الأيام المتبقية للانتهاء": f"{days_to_expiry} يوم" if days_to_expiry else "غير متوفر",
474
- "المسجل": domain_info.registrar or "غير متوفر",
475
- "الحالة": domain_info.status if isinstance(domain_info.status, list) else [domain_info.status] if domain_info.status else []
476
- },
477
- "تقييم الموثوقية": self._evaluate_domain_trust(domain_age if domain_age else 0, domain_info)
478
  }
 
 
479
  except Exception as e:
480
- return {"error": f"خطأ في جلب معلومات النطاق: {str(e)}"}
481
-
482
- def _evaluate_domain_trust(self, age_days, domain_info):
483
- trust_score = 0
484
- reasons = []
485
-
486
- # تقييم العمر
487
- if age_days > 365*5: # أكثر من 5 سنوات
488
- trust_score += 40
489
- reasons.append("نطاق قديم وموثوق")
490
- elif age_days > 365: # أكثر من سنة
491
- trust_score += 25
492
- reasons.append("نطاق مستقر")
493
- else:
494
- trust_score += 10
495
- reasons.append("نطاق حديث")
496
-
497
- # تقييم المسجل
498
- if domain_info.registrar and any(trusted in domain_info.registrar.lower() for trusted in ['godaddy', 'namecheap', 'name.com', 'google']):
499
- trust_score += 20
500
- reasons.append("مسجل موثوق")
501
 
502
- # تقييم الحالة
503
- if domain_info.status:
504
- statuses = domain_info.status if isinstance(domain_info.status, list) else [domain_info.status]
505
- if 'clientTransferProhibited' in statuses:
506
- trust_score += 20
507
- reasons.append("محمي من النقل غير المصرح به")
508
- if 'clientDeleteProhibited' in statuses:
509
- trust_score += 20
510
- reasons.append("محمي من الحذف غير المصرح به")
511
-
512
- return {
513
- "درجة الموثوقية": f"{trust_score}/100",
514
- "المستوى": self._get_trust_level(trust_score),
515
- "الأسباب": reasons
516
- }
517
-
518
- def _get_trust_level(self, score):
519
- if score >= 80:
520
- return "موثوق جداً 🌟"
521
- elif score >= 60:
522
- return "موثوق ✅"
523
- elif score >= 40:
524
- return "موثوقية متوسطة ⚠️"
525
- else:
526
- return "موثوقية منخفضة ⛔"
527
- def _analyze_seo(self, url):
528
- try:
529
- response = requests.get(url)
530
- soup = BeautifulSoup(response.text, 'html.parser')
531
-
532
- results = {
533
- "العناصر الأساسية": {},
534
- "الوسوم الوصفية": {},
535
- "توصيات": [],
536
- "درجة SEO": 100
537
- }
538
-
539
- # فحص العنوان
540
- title = soup.find('title')
541
- if title and title.text.strip():
542
- title_length = len(title.text.strip())
543
- results["العناصر الأساسية"]["العنوان"] = {
544
- "القيمة": title.text.strip(),
545
- "الطول": title_length,
546
- "التقييم": "✅" if 30 <= title_length <= 60 else "⚠️"
547
- }
548
- if title_length < 30 or title_length > 60:
549
- results["توصيات"].append("تعديل طول العنوان ليكون بين 30-60 حرف")
550
- results["درجة SEO"] -= 10
551
- else:
552
- results["العناصر الأساسية"]["العنوان"] = {"القيمة": "غير موجود", "التقييم": "❌"}
553
- results["توصيات"].append("إضافة عنوان للصفحة")
554
- results["درجة SEO"] -= 20
555
-
556
- # فحص الوصف
557
- meta_desc = soup.find('meta', attrs={'name': 'description'})
558
- if meta_desc and meta_desc.get('content', '').strip():
559
- desc_length = len(meta_desc['content'].strip())
560
- results["الوسوم الوصفية"]["الوصف"] = {
561
- "القيمة": meta_desc['content'].strip(),
562
- "الطول": desc_length,
563
- "التقييم": "✅" if 120 <= desc_length <= 160 else "⚠️"
564
- }
565
- if desc_length < 120 or desc_length > 160:
566
- results["توصيات"].append("تعديل طول الوصف ليكون بين 120-160 حرف")
567
- results["درجة SEO"] -= 10
568
- else:
569
- results["الوسوم الوصفية"]["الوصف"] = {"القيمة": "غير موجود", "التقييم": "❌"}
570
- results["توصيات"].append("إضافة وصف للصفحة")
571
- results["درجة SEO"] -= 15
572
-
573
- # فحص الكلمات المفتاحية
574
- keywords = soup.find('meta', attrs={'name': 'keywords'})
575
- results["الوسوم الوصفية"]["الكلمات المفتاحية"] = {
576
- "القيمة": keywords['content'].strip() if keywords else "غير موجود",
577
- "التقييم": "✅" if keywords else "ℹ️"
578
- }
579
-
580
- # فحص العناوين الفرعية
581
- headings = {f"h{i}": len(soup.find_all(f'h{i}')) for i in range(1, 7)}
582
- results["العناصر الأساسية"]["العناوين الفرعية"] = headings
583
- if not headings.get('h1'):
584
- results["توصيات"].append("إضافة عنوان رئيسي H1")
585
- results["درجة SEO"] -= 15
586
- elif headings.get('h1') > 1:
587
- results["توصيات"].append("يجب أن يكون هناك عنوان H1 واحد فقط")
588
- results["درجة SEO"] -= 10
589
-
590
- # فحص الصور
591
- images = soup.find_all('img')
592
- missing_alt = sum(1 for img in images if not img.get('alt'))
593
- results["العناصر الأساسية"]["الصور"] = {
594
- "العدد الإجمالي": len(images),
595
- "بدون نص بديل": missing_alt
596
- }
597
- if missing_alt:
598
- results["توصيات"].append(f"إضافة نص بديل لـ {missing_alt} صورة")
599
- results["درجة SEO"] -= min(15, missing_alt * 2)
600
-
601
- # تقييم عام
602
- results["التقييم العام"] = self._get_seo_level(results["درجة SEO"])
603
-
604
- return results
605
- except Exception as e:
606
- return {"error": f"خطأ في تحليل SEO: {str(e)}"}
607
-
608
- def _get_seo_level(self, score):
609
- if score >= 90:
610
- return "ممتاز 🌟"
611
- elif score >= 70:
612
- return "جيد ✅"
613
- elif score >= 50:
614
- return "متوسط ⚠️"
615
- else:
616
- return "ضعيف ⛔"
617
-
618
- def _analyze_performance(self, url):
619
- try:
620
- start_time = time.time()
621
- response = requests.get(url)
622
- load_time = time.time() - start_time
623
-
624
- results = {
625
- "مقاييس الأداء": {},
626
- "تحليل المحتوى": {},
627
- "توصيات": [],
628
- "درجة الأداء": 100
629
- }
630
-
631
- # قياس زمن التحميل
632
- results["مقاييس الأداء"]["زمن التحميل"] = {
633
- "القيمة": f"{load_time:.2f} ثانية",
634
- "التقييم": "✅" if load_time < 2 else "⚠️" if load_time < 4 else "❌"
635
- }
636
- if load_time >= 2:
637
- results["توصيات"].append("تحسين زمن تحميل الصفحة")
638
- results["درجة الأداء"] -= min(30, int(load_time * 10))
639
-
640
- # تحليل حجم الصفحة
641
- page_size = len(response.content) / 1024 # تحويل إلى كيلوبايت
642
- results["مقاييس الأداء"]["حجم الصفحة"] = {
643
- "القيمة": f"{page_size:.2f} KB",
644
- "التقييم": "✅" if page_size < 500 else "⚠️" if page_size < 1000 else "❌"
645
- }
646
- if page_size >= 500:
647
- results["توصيات"].append("تقليل حجم الصفحة")
648
- results["درجة الأداء"] -= min(20, int(page_size/100))
649
-
650
- # تحليل المحتوى
651
- soup = BeautifulSoup(response.text, 'html.parser')
652
-
653
- # عدد ملفات JavaScript
654
- scripts = soup.find_all('script')
655
- results["تحليل المحتوى"]["ملفات JavaScript"] = {
656
- "العدد": len(scripts),
657
- "التقييم": "✅" if len(scripts) < 10 else "⚠️" if len(scripts) < 15 else "❌"
658
- }
659
- if len(scripts) >= 10:
660
- results["توصيات"].append("تقليل عدد ملفات JavaScript")
661
- results["درجة الأداء"] -= min(15, len(scripts))
662
-
663
- # عدد ملفات CSS
664
- css_files = soup.find_all('link', rel='stylesheet')
665
- results["تحليل المحتوى"]["ملفات CSS"] = {
666
- "العدد": len(css_files),
667
- "التقييم": "✅" if len(css_files) < 5 else "⚠️" if len(css_files) < 8 else "❌"
668
- }
669
- if len(css_files) >= 5:
670
- results["توصيات"].append("تقليل عدد ملفات CSS")
671
- results["درجة الأداء"] -= min(10, len(css_files) * 2)
672
-
673
- # عدد الصور وحجمها
674
- images = soup.find_all('img')
675
- total_images_size = 0
676
- for img in images:
677
- if img.get('src'):
678
- try:
679
- img_response = requests.head(urljoin(url, img['src']))
680
- total_images_size += int(img_response.headers.get('content-length', 0))
681
- except:
682
- continue
683
-
684
- total_images_size_kb = total_images_size / 1024
685
- results["تحليل المحتوى"]["الصور"] = {
686
- "العدد": len(images),
687
- "الحجم الإجمالي": f"{total_images_size_kb:.2f} KB",
688
- "التقييم": "✅" if total_images_size_kb < 1000 else "⚠️" if total_images_size_kb < 2000 else "❌"
689
  }
690
 
691
- if total_images_size_kb >= 1000:
692
- results["توصيات"].append("ضغط الصور وتحسين حجمها")
693
- results["درجة الأداء"] -= min(15, int(total_images_size_kb/200))
694
-
695
- # تقييم عام
696
- results["التقييم العام"] = self._get_performance_level(results["درجة الأداء"])
697
-
698
- return results
699
- except Exception as e:
700
- return {"error": f"خطأ في تحليل الأداء: {str(e)}"}
701
-
702
- def _get_performance_level(self, score):
703
- if score >= 90:
704
- return "أداء ممتاز 🚀"
705
- elif score >= 70:
706
- return "أداء جيد ✅"
707
- elif score >= 50:
708
- return "أداء متوسط ⚠️"
709
- else:
710
- return "أداء ضعيف ⛔"
 
14
  import ssl
15
  import socket
16
  import dns.resolver
17
+ from urllib.parse import urlparse, urljoin
18
  import json
19
  import numpy as np
20
  from selenium import webdriver
 
29
  from concurrent.futures import ThreadPoolExecutor
30
  import re
31
  from urllib.robotparser import RobotFileParser
32
+ import random
33
+ from textblob import TextBlob
34
+ from collections import Counter
35
+ import networkx as nx
36
 
37
  # تهيئة المتغيرات العامة
38
  TIMEOUT = 10
 
67
 
68
  async def analyze_performance(self, url):
69
  try:
 
70
  performance_metrics = {
71
  'dns_lookup': [],
72
  'tcp_handshake': [],
 
74
  'content_download': []
75
  }
76
 
77
+ # تحليل الأداء على أجهزة مختلفة
78
+ devices = ['desktop', 'mobile', 'tablet']
79
+ device_metrics = {}
80
+
81
+ for device in devices:
82
+ chrome_options = Options()
83
+ chrome_options.add_argument(f"--user-agent={self._get_device_user_agent(device)}")
84
  start_time = time.time()
85
 
86
+ # قياس الأداء لكل جهاز
87
+ device_metrics[device] = {
88
+ 'load_time': time.time() - start_time,
89
+ 'render_time': self._measure_render_time(url, chrome_options)
90
+ }
91
+
92
+ # تحليل عام للأداء
93
+ for _ in range(3):
94
+ start_time = time.time()
95
  domain = urlparse(url).netloc
96
  dns_start = time.time()
97
  socket.gethostbyname(domain)
98
  performance_metrics['dns_lookup'].append(time.time() - dns_start)
99
 
 
100
  response = await self._fetch_with_retry(url)
 
101
  performance_metrics['ttfb'].append(response.elapsed.total_seconds())
102
  performance_metrics['content_download'].append(time.time() - start_time - response.elapsed.total_seconds())
103
 
104
+ # تحليل الموارد والتحسينات
 
 
 
105
  soup = BeautifulSoup(response.text, 'html.parser')
106
+ resource_analysis = self._analyze_resources(soup, response.content)
107
+ optimization_suggestions = self._generate_optimization_suggestions(resource_analysis)
 
 
 
 
 
 
 
 
 
108
 
109
  return {
110
  "أداء الموقع": {
111
+ "تحليل الأجهزة": {
112
+ device: {
113
+ "زمن التحميل": f"{metrics['load_time']:.2f} ثانية",
114
+ "زمن العرض": f"{metrics['render_time']:.2f} ثانية"
115
+ } for device, metrics in device_metrics.items()
116
+ },
117
+ "تحليل الموارد": resource_analysis,
118
+ "اقتراحات التحسين": optimization_suggestions
119
+ }
 
 
 
 
 
 
 
120
  }
121
  except Exception as e:
122
  return {"error": f"حدث خطأ أثناء تحليل الأداء: {str(e)}"}
123
 
124
+ def _analyze_resources(self, soup, content):
125
+ """تحليل موارد الصفحة وتحديد فرص التحسين"""
126
+ resources = {
127
+ 'images': self._analyze_images(soup),
128
+ 'scripts': self._analyze_scripts(soup),
129
+ 'styles': self._analyze_styles(soup),
130
+ 'fonts': self._analyze_fonts(soup),
131
+ 'total_size': len(content) / 1024
132
+ }
133
+ return resources
 
 
 
 
 
 
 
 
 
 
134
 
135
+ def _analyze_images(self, soup):
136
+ """تحليل تفصيلي للصور"""
137
+ images = soup.find_all('img')
138
+ return {
139
+ 'count': len(images),
140
+ 'without_alt': len([img for img in images if not img.get('alt')]),
141
+ 'large_images': len([img for img in images if self._is_large_image(img)]),
142
+ 'optimization_needed': self._check_image_optimization(images)
143
+ }
 
 
 
 
 
144
 
145
+ def _analyze_competitors(self, url):
146
+ """تحليل المنافسين والمقارنة معهم"""
147
  try:
148
+ competitors = self._find_competitors(url)
149
+ comparison = {}
150
 
151
+ for competitor in competitors:
152
+ comparison[competitor] = {
153
+ 'traffic': self._estimate_traffic(competitor),
154
+ 'keywords': self._analyze_keywords(competitor),
155
+ 'backlinks': self._analyze_backlinks(competitor),
156
+ 'social_presence': self._analyze_social_presence(competitor)
157
+ }
 
 
 
 
 
 
 
 
 
 
158
 
159
  return {
160
+ 'المنافسون الرئيسيون': comparison,
161
+ 'تحليل مقارن': self._generate_competitive_analysis(comparison)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  }
163
  except Exception as e:
164
+ return {"error": f"خطأ في تحليل المنافسين: {str(e)}"}
165
 
166
+ def _analyze_content_quality(self, soup):
167
+ """تحليل جودة المحتوى"""
168
+ text_content = soup.get_text()
 
 
 
169
 
170
+ # تحليل لغوي
171
+ blob = TextBlob(text_content)
172
+
173
+ # تحليل القراءة
174
+ readability = self._calculate_readability(text_content)
175
+
176
+ # تحليل الكلمات المفتاحية
177
+ keywords = self._extract_keywords(text_content)
178
 
 
 
 
 
 
 
 
 
 
 
 
179
  return {
180
+ "تحليل المحتوى": {
181
+ "مستوى القراءة": readability,
182
+ "تنوع المفردات": self._calculate_lexical_diversity(text_content),
183
+ "الكلمات المفتاحية الرئيسية": keywords[:10],
184
+ "العاطفة": {
185
+ "إيجابية": blob.sentiment.polarity,
186
+ "موضوعية": blob.sentiment.subjectivity
187
+ }
188
+ }
189
  }
190
 
191
+ def _analyze_backlinks(self, url):
192
+ """تحليل الروابط الخلفية"""
193
  try:
194
+ backlinks = self._fetch_backlinks(url)
 
 
 
195
 
196
+ # تحليل جودة الروابط
197
+ quality_metrics = self._analyze_backlink_quality(backlinks)
198
 
199
+ # تحليل تنوع المصادر
200
+ diversity = self._analyze_source_diversity(backlinks)
 
 
 
201
 
202
  return {
203
+ "تحليل الروابط الخلفية": {
204
+ "العدد الإجمالي": len(backlinks),
205
+ "جودة الروابط": quality_metrics,
206
+ "تنوع المصادر": diversity,
207
+ "أهم المصادر": self._get_top_referring_domains(backlinks)
 
208
  }
209
  }
210
  except Exception as e:
211
+ return {"error": f"خطأ في تحليل الروابط الخلفية: {str(e)}"}
212
 
213
+ def _analyze_social_signals(self, url):
214
+ """تحليل الإشارات الاجتماعية"""
215
  try:
216
+ social_metrics = {
217
+ 'facebook': self._get_facebook_shares(url),
218
+ 'twitter': self._get_twitter_shares(url),
219
+ 'linkedin': self._get_linkedin_shares(url),
220
+ 'pinterest': self._get_pinterest_shares(url)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  }
 
 
 
 
 
222
 
223
+ engagement_analysis = self._analyze_social_engagement(social_metrics)
 
 
 
 
 
 
224
 
225
+ return {
226
+ "التواجد الاجتماعي": {
227
+ "إحصائيات المشاركة": social_metrics,
228
+ "تحليل التفاعل": engagement_analysis,
229
+ "توصيات": self._generate_social_recommendations(engagement_analysis)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  except Exception as e:
233
+ return {"error": f"خطأ في تحليل الإشارات الاجتماعية: {str(e)}"}
 
 
 
 
 
 
 
 
 
 
234
 
235
+ def _generate_comprehensive_report(self, url):
236
+ """توليد تقرير شامل"""
237
  try:
238
+ report = {
239
+ "تحليل الأداء": self.analyze_performance(url),
240
+ "تحليل SEO": self.analyze_seo(url),
241
+ "تحليل الأمان": self.analyze_security(url),
242
+ "تحليل المنافسين": self._analyze_competitors(url),
243
+ "تحليل المحتوى": self._analyze_content_quality(BeautifulSoup(requests.get(url).text, 'html.parser')),
244
+ "تحليل الروابط": self._analyze_backlinks(url),
245
+ "التواجد الاجتماعي": self._analyze_social_signals(url),
246
+ "التوصيات": self._generate_recommendations()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
+
249
+ return report
250
  except Exception as e:
251
+ return {"error": f"خطأ في توليد التقرير الشامل: {str(e)}"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
+ def _generate_recommendations(self):
254
+ """توليد توصيات مخصصة"""
255
+ recommendations = {
256
+ "تحسينات عاجلة": [],
257
+ "تحسينات متوسطة الأولوية": [],
258
+ "تحسينات طويلة المدى": []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  }
260
 
261
+ # إضافة التوصيات بناءً على نتائج التحليل
262
+ return recommendations
263
+
264
+ # مثال للاستخدام
265
+ async def main():
266
+ analyzer = WebsiteAnalyzer()
267
+ url = "https://example.com"
268
+ report = await analyzer._generate_comprehensive_report(url)
269
+ print(json.dumps(report, ensure_ascii=False, indent=2))
270
+
271
+ if __name__ == "__main__":
272
+ asyncio.run(main())