joermd commited on
Commit
48814e4
·
verified ·
1 Parent(s): c8f549a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +733 -174
app.py CHANGED
@@ -21,6 +21,13 @@ import io
21
  import time
22
  import matplotlib.pyplot as plt
23
  import seaborn as sns
 
 
 
 
 
 
 
24
 
25
  # تحسين مظهر الصفحة
26
  st.set_page_config(
@@ -30,9 +37,15 @@ st.set_page_config(
30
  initial_sidebar_state="expanded"
31
  )
32
 
33
- # تحسين التصميم باستخدام CSS
34
  st.markdown("""
35
  <style>
 
 
 
 
 
 
36
  .main {
37
  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
38
  padding: 20px;
@@ -42,52 +55,116 @@ st.markdown("""
42
  background: white;
43
  border-radius: 15px;
44
  padding: 20px;
45
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
46
- transition: transform 0.3s ease;
 
47
  }
 
48
  .metric-card:hover {
49
  transform: translateY(-5px);
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
 
52
  .stButton>button {
53
  background: linear-gradient(45deg, #2196F3, #21CBF3);
54
  color: white;
55
  border-radius: 25px;
56
- padding: 15px 25px;
57
  border: none;
58
- box-shadow: 0 4px 15px rgba(0,0,0,0.2);
59
  transition: all 0.3s ease;
 
 
 
60
  }
 
61
  .stButton>button:hover {
62
  transform: translateY(-2px);
63
- box-shadow: 0 6px 20px rgba(0,0,0,0.25);
64
  }
65
 
66
  h1, h2, h3 {
67
  color: #1E3D59;
68
- font-family: 'Tajawal', sans-serif;
69
  }
70
 
71
  .stTextInput>div>div>input {
72
  border-radius: 10px;
73
  border: 2px solid #E0E0E0;
74
- padding: 10px;
 
 
 
 
 
 
 
75
  }
76
 
77
  .streamlit-expanderHeader {
78
  background-color: white;
79
  border-radius: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
81
  </style>
82
- <link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@400;700&display=swap" rel="stylesheet">
83
  """, unsafe_allow_html=True)
84
 
85
- class WebsiteAnalyzer:
86
  def __init__(self):
87
  self.headers = {
88
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
89
  }
90
-
 
 
 
 
 
 
 
 
 
 
 
91
  async def analyze_performance(self, url):
92
  try:
93
  start_time = time.time()
@@ -96,33 +173,111 @@ class WebsiteAnalyzer:
96
  load_time = time.time() - start_time
97
  page_size = len(response.content) / 1024
98
 
 
 
 
 
 
 
99
  performance_metrics = {
100
- "load_time": round(load_time, 2),
101
- "page_size": round(page_size, 2),
102
- "status_code": response.status_code,
103
- "تقييم الأ��اء": self._calculate_performance_score(load_time, page_size),
104
- "توصيات تحسين الأداء": self._get_performance_recommendations(load_time, page_size)
 
 
 
105
  }
106
 
 
 
 
 
107
  return performance_metrics
108
  except Exception as e:
109
  return {"error": f"خطأ في تحليل الأداء: {str(e)}"}
110
 
111
- def _calculate_performance_score(self, load_time, page_size):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  score = 100
 
 
113
  if load_time > 2:
114
- score -= (load_time - 2) * 10
 
 
115
  if page_size > 1000:
116
- score -= (page_size - 1000) / 100
117
- return max(0, min(100, round(score)))
 
 
 
 
 
 
 
 
 
118
 
119
- def _get_performance_recommendations(self, load_time, page_size):
120
  recommendations = []
 
121
  if load_time > 2:
122
- recommendations.append("تحسين سرعة تحميل الصفحة")
 
 
 
 
 
123
  if page_size > 1000:
124
- recommendations.append("تقليل حجم الصفحة")
125
- return recommendations if recommendations else ["أداء الموقع جيد!"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
  async def analyze_seo(self, url):
128
  try:
@@ -130,183 +285,587 @@ class WebsiteAnalyzer:
130
  response = await client.get(url)
131
  soup = BeautifulSoup(response.text, 'html.parser')
132
 
 
 
 
 
 
 
 
 
 
133
  seo_analysis = {
134
- "العنوان": self._analyze_title(soup),
135
- "الوصف": self._analyze_description(soup),
136
- "الكلمات المفتاحية": self._extract_keywords(soup),
137
- "هيكل العناوين": self._analyze_headings(soup),
138
- "الروابط": self._analyze_links(soup),
139
- "تقييم SEO": self._calculate_seo_score(soup)
 
 
140
  }
141
 
142
  return seo_analysis
143
  except Exception as e:
144
  return {"error": f"خطأ في تحليل SEO: {str(e)}"}
145
 
146
- def _analyze_title(self, soup):
147
- title = soup.title.string if soup.title else ""
 
 
 
 
 
 
 
 
 
 
 
148
  return {
149
- "النص": title,
150
- "الطول": len(title) if title else 0,
151
- "التقييم": "ممتاز" if title and 30 <= len(title) <= 60 else "يحتاج تحسين"
 
152
  }
153
 
154
- def _analyze_description(self, soup):
155
- meta_desc = soup.find('meta', {'name': 'description'})
156
- desc = meta_desc['content'] if meta_desc else ""
157
- return {
158
- "النص": desc,
159
- "الطول": len(desc) if desc else 0,
160
- "التقييم": "ممتاز" if desc and 120 <= len(desc) <= 160 else "يحتاج تحسين"
161
- }
162
 
163
- def _extract_keywords(self, soup):
164
- keywords = []
165
- meta_keywords = soup.find('meta', {'name': 'keywords'})
166
- if meta_keywords:
167
- keywords = [k.strip() for k in meta_keywords['content'].split(',')]
168
- return {
169
- "الكلمات": keywords,
170
- "العدد": len(keywords),
171
- "التقييم": "ممتاز" if keywords else "يحتاج تحسين"
172
- }
173
 
174
- def _analyze_headings(self, soup):
175
- headings = {f'h{i}': len(soup.find_all(f'h{i}')) for i in range(1, 7)}
176
- return {
177
- "العدد": headings,
178
- "التقييم": "ممتاز" if headings.get('h1', 0) == 1 else "يح��اج تحسين"
179
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
- def _analyze_links(self, soup):
182
- links = soup.find_all('a')
183
- return {
184
- "العدد الإجمالي": len(links),
185
- "الروابط الداخلية": len([l for l in links if l.get('href', '').startswith('/')]),
186
- "الروابط الخارجية": len([l for l in links if l.get('href', '').startswith('http')])
187
- }
188
 
189
- def _calculate_seo_score(self, soup):
190
- score = 100
191
- # حساب النتيجة بناءً على العوامل المختلفة
192
- if not soup.title:
193
- score -= 20
194
- if not soup.find('meta', {'name': 'description'}):
195
- score -= 15
196
- if not soup.find('meta', {'name': 'keywords'}):
197
- score -= 10
198
- if len(soup.find_all('h1')) != 1:
199
- score -= 10
200
- return max(0, score)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
- def analyze_security(self, url):
 
 
 
 
 
 
 
203
  try:
204
- domain = urlparse(url).netloc
205
- security_analysis = {
206
- "شهادة SSL": self._check_ssl(url),
207
- "سجلات DNS": self._check_dns(domain),
208
- "تقييم الأمان": self._calculate_security_score(url)
209
- }
210
- return security_analysis
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  except Exception as e:
212
- return {"error": f"خطأ في تحليل الأمان: {str(e)}"}
213
 
214
- def _check_ssl(self, url):
215
  try:
216
- context = ssl.create_default_context()
217
- with socket.create_connection((urlparse(url).netloc, 443)) as sock:
218
- with context.wrap_socket(sock, server_hostname=urlparse(url).netloc) as ssock:
219
- cert = ssock.getpeercert()
220
- return {
221
- "الحالة": "آمن ✅",
222
- "تاريخ الانتهاء": cert['notAfter']
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  }
224
- except:
225
- return {"الحالة": "غير آمن ❌"}
226
-
227
- def _check_dns(self, domain):
228
- try:
229
- records = dns.resolver.resolve(domain, 'A')
230
- return {
231
- "عدد السجلات": len(records),
232
- "السجلات": [str(r) for r in records]
233
- }
234
- except:
235
- return {"error": "لا يمكن الوصول إلى سجلات DNS"}
236
 
237
- def _calculate_security_score(self, url):
238
  score = 100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  try:
240
- if not url.startswith('https'):
241
- score -= 40
242
- # يمكن إضافة المزيد من الفحوصات الأمنية هنا
243
- except:
244
- score = 0
245
- return score
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
- def main():
248
- st.title("🔍 محلل المواقع المتقدم")
249
-
250
- with st.sidebar:
251
- selected = option_menu(
252
- menu_title="القائمة الرئيسية",
253
- options=["تحليل جديد", "التقارير السابقة", "الإحصائيات"],
254
- icons=["search", "file-text", "graph-up"],
255
- menu_icon="cast",
256
- styles={
257
- "container": {"padding": "15px"},
258
- "icon": {"color": "orange", "font-size": "25px"},
259
- "nav-link": {"font-size": "16px", "text-align": "right", "margin": "0px"}
260
- }
261
- )
262
-
263
- if selected == "تحليل جديد":
264
- col1, col2 = st.columns([2, 1])
265
 
266
- with col1:
267
- url = st.text_input("🌐 أدخل رابط الموقع", "https://example.com")
268
- analyze_button = st.button("⚡ بدء التحليل")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
- if analyze_button:
271
- with st.spinner("جاري التحليل... ⏳"):
272
- analyzer = WebsiteAnalyzer()
273
-
274
- # تحليل متزامن
275
- loop = asyncio.new_event_loop()
276
- asyncio.set_event_loop(loop)
277
-
278
- performance_data = loop.run_until_complete(analyzer.analyze_performance(url))
279
- seo_data = loop.run_until_complete(analyzer.analyze_seo(url))
280
- security_data = analyzer.analyze_security(url)
281
-
282
- st.success("✨ تم اكتمال التحليل بنجاح!")
283
-
284
- # عرض النتائج
285
- tabs = st.tabs(["📊 الأداء", "🎯 SEO", "🔒 الأمان"])
286
-
287
- with tabs[0]:
288
- st.subheader("تحليل الأداء")
289
- st.json(performance_data)
290
-
291
- with tabs[1]:
292
- st.subheader("تحليل SEO")
293
- st.json(seo_data)
294
-
295
- with tabs[2]:
296
- st.subheader("تحليل الأمان")
297
- st.json(security_data)
298
 
299
- with col2:
300
- st.subheader("📊 آخر التحليلات")
301
- st.info("سيتم عرض آخر المواقع التي تم تحليلها هنا")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
 
303
- elif selected == "التقارير السابقة":
304
- st.subheader("📊 التقارير السابقة")
305
- st.info("قريباً...")
 
 
 
 
 
306
 
307
- elif selected == "الإحصائيات":
308
- st.subheader("📈 إحصائيات التحليل")
309
- st.info("قريباً...")
 
 
310
 
311
- if __name__ == "__main__":
312
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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(
 
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;
 
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()
 
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:
 
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
+ import streamlit as st
352
+ from streamlit_option_menu import option_menu
353
+ import requests
354
+ import pandas as pd
355
+ import plotly.express as px
356
+ import plotly.graph_objects as go
357
+ from datetime import datetime
358
+ import httpx
359
+ import asyncio
360
+ import aiohttp
361
+ from bs4 import BeautifulSoup
362
+ import whois
363
+ import ssl
364
+ import socket
365
+ import dns.resolver
366
+ from urllib.parse import urlparse
367
+ import json
368
+ import numpy as np
369
+ from PIL import Image
370
+ import io
371
+ import time
372
+ import matplotlib.pyplot as plt
373
+ import seaborn as sns
374
+ from datetime import timedelta
375
+ import tldextract
376
+ from concurrent.futures import ThreadPoolExecutor
377
+ import re
378
+ from collections import Counter
379
+ from wordcloud import WordCloud
380
+ import advertools as adv
381
 
382
+ # تحسين مظهر الصفحة
383
+ st.set_page_config(
384
+ layout="wide",
385
+ page_title="محلل المواقع المتقدم | Website Analyzer Pro",
386
+ page_icon="🔍",
387
+ initial_sidebar_state="expanded"
388
+ )
389
 
390
+ # تحسين التصميم باستخدام CSS المحسن
391
+ st.markdown("""
392
+ <style>
393
+ @import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;700&display=swap');
394
+
395
+ * {
396
+ font-family: 'Tajawal', sans-serif;
397
+ }
398
+
399
+ .main {
400
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
401
+ padding: 20px;
402
+ }
403
+
404
+ .metric-card {
405
+ background: white;
406
+ border-radius: 15px;
407
+ padding: 20px;
408
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
409
+ transition: all 0.3s ease;
410
+ margin-bottom: 20px;
411
+ }
412
+
413
+ .metric-card:hover {
414
+ transform: translateY(-5px);
415
+ box-shadow: 0 8px 25px rgba(0,0,0,0.15);
416
+ }
417
+
418
+ .metric-value {
419
+ font-size: 2em;
420
+ font-weight: bold;
421
+ color: #2196F3;
422
+ }
423
+
424
+ .metric-label {
425
+ color: #666;
426
+ font-size: 1.1em;
427
+ }
428
+
429
+ .stButton>button {
430
+ background: linear-gradient(45deg, #2196F3, #21CBF3);
431
+ color: white;
432
+ border-radius: 25px;
433
+ padding: 15px 30px;
434
+ border: none;
435
+ box-shadow: 0 4px 15px rgba(33,150,243,0.3);
436
+ transition: all 0.3s ease;
437
+ font-size: 1.1em;
438
+ font-weight: 500;
439
+ width: 100%;
440
+ }
441
+
442
+ .stButton>button:hover {
443
+ transform: translateY(-2px);
444
+ box-shadow: 0 6px 20px rgba(33,150,243,0.4);
445
+ }
446
+
447
+ h1, h2, h3 {
448
+ color: #1E3D59;
449
+ font-weight: 700;
450
+ }
451
+
452
+ .stTextInput>div>div>input {
453
+ border-radius: 10px;
454
+ border: 2px solid #E0E0E0;
455
+ padding: 12px;
456
+ font-size: 1.1em;
457
+ transition: all 0.3s ease;
458
+ }
459
+
460
+ .stTextInput>div>div>input:focus {
461
+ border-color: #2196F3;
462
+ box-shadow: 0 0 0 2px rgba(33,150,243,0.2);
463
+ }
464
+
465
+ .streamlit-expanderHeader {
466
+ background-color: white;
467
+ border-radius: 10px;
468
+ padding: 10px;
469
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
470
+ }
471
+
472
+ .stProgress > div > div > div {
473
+ background-color: #2196F3;
474
+ }
475
+
476
+ .tab-content {
477
+ padding: 20px;
478
+ background: white;
479
+ border-radius: 15px;
480
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
481
+ }
482
+
483
+ .insight-card {
484
+ background: #f8f9fa;
485
+ border-right: 4px solid #2196F3;
486
+ padding: 15px;
487
+ margin: 10px 0;
488
+ border-radius: 8px;
489
+ }
490
+
491
+ .chart-container {
492
+ background: white;
493
+ padding: 20px;
494
+ border-radius: 15px;
495
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
496
+ margin: 20px 0;
497
+ }
498
+ </style>
499
+ """, unsafe_allow_html=True)
500
 
501
+ class AdvancedWebsiteAnalyzer:
502
+ def __init__(self):
503
+ self.headers = {
504
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
505
+ }
506
+ self.history = self.load_history()
507
+
508
+ def load_history(self):
509
  try:
510
+ return pd.read_csv('analysis_history.csv')
511
+ except:
512
+ return pd.DataFrame(columns=['url', 'timestamp', 'performance_score', 'seo_score', 'security_score'])
513
+
514
+ def save_history(self, data):
515
+ self.history = self.history.append(data, ignore_index=True)
516
+ self.history.to_csv('analysis_history.csv', index=False)
517
+
518
+ async def analyze_performance(self, url):
519
+ try:
520
+ start_time = time.time()
521
+ async with httpx.AsyncClient() as client:
522
+ response = await client.get(url)
523
+ load_time = time.time() - start_time
524
+ page_size = len(response.content) / 1024
525
+
526
+ # تحليل الصور والموارد
527
+ soup = BeautifulSoup(response.text, 'html.parser')
528
+ images = soup.find_all('img')
529
+ scripts = soup.find_all('script')
530
+ css_files = soup.find_all('link', {'rel': 'stylesheet'})
531
+
532
+ performance_metrics = {
533
+ "زمن التحميل": round(load_time, 2),
534
+ "حجم الصفحة": round(page_size, 2),
535
+ "حالة الاستجابة": response.status_code,
536
+ "عدد الصور": len(images),
537
+ "عدد ملفات JavaScript": len(scripts),
538
+ "عدد ملفات CSS": len(css_files),
539
+ "تقييم الأداء": self._calculate_performance_score(load_time, page_size, len(images), len(scripts)),
540
+ "توصيات التحسين": self._get_performance_recommendations(load_time, page_size, len(images), len(scripts))
541
+ }
542
+
543
+ # إضافة تحليل الموارد
544
+ resources_analysis = await self._analyze_resources(url)
545
+ performance_metrics.update(resources_analysis)
546
+
547
+ return performance_metrics
548
  except Exception as e:
549
+ return {"error": f"خطأ في تحليل الأداء: {str(e)}"}
550
 
551
+ async def _analyze_resources(self, url):
552
  try:
553
+ async with httpx.AsyncClient() as client:
554
+ response = await client.get(url)
555
+ soup = BeautifulSoup(response.text, 'html.parser')
556
+
557
+ # تحليل الصور
558
+ images = soup.find_all('img')
559
+ image_sizes = []
560
+ for img in images[:5]: # تحليل أول 5 صور فقط لتجنب البطء
561
+ if img.get('src'):
562
+ try:
563
+ img_response = await client.get(img['src'])
564
+ image_sizes.append(len(img_response.content) / 1024)
565
+ except:
566
+ continue
567
+
568
+ return {
569
+ "تحليل الموارد": {
570
+ "متوسط حجم الصور": round(np.mean(image_sizes), 2) if image_sizes else 0,
571
+ "عدد الموارد الخارجية": len(soup.find_all(['script', 'link', 'img'])),
572
+ "توصيات تحسين الموارد": self._get_resource_recommendations(image_sizes)
573
  }
574
+ }
575
+ except Exception as e:
576
+ return {"error": f"خطأ في تحليل الموارد: {str(e)}"}
 
 
 
 
 
 
 
 
 
577
 
578
+ def _calculate_performance_score(self, load_time, page_size, image_count, script_count):
579
  score = 100
580
+
581
+ # تأثير زمن التحميل
582
+ if load_time > 2:
583
+ score -= min(30, (load_time - 2) * 10)
584
+
585
+ # تأثير حجم الصفحة
586
+ if page_size > 1000:
587
+ score -= min(20, (page_size - 1000) / 100)
588
+
589
+ # تأثير عدد الصور
590
+ if image_count > 10:
591
+ score -= min(15, (image_count - 10) * 1.5)
592
+
593
+ # تأثير عدد ملفات JavaScript
594
+ if script_count > 5:
595
+ score -= min(15, (script_count - 5) * 2)
596
+
597
+ return max(0, round(score))
598
+
599
+ def _get_performance_recommendations(self, load_time, page_size, image_count, script_count):
600
+ recommendations = []
601
+
602
+ if load_time > 2:
603
+ recommendations.append({
604
+ "المشكلة": "بطء زمن التحميل",
605
+ "الحل": "تحسين سرعة الخادم وتفعيل التخزين المؤقت",
606
+ "الأولوية": "عالية"
607
+ })
608
+
609
+ if page_size > 1000:
610
+ recommendations.append({
611
+ "المشكلة": "حجم الصفحة كبير",
612
+ "الحل": "ضغط الملفات وتحسين الكود",
613
+ "الأولوية": "متوسطة"
614
+ })
615
+
616
+ if image_count > 10:
617
+ recommendations.append({
618
+ "المشكلة": "عدد كبير من الصور",
619
+ "الحل": "تحسين حجم الصور واستخدام التحميل الكسول",
620
+ "الأولوية": "متوسطة"
621
+ })
622
+
623
+ if script_count > 5:
624
+ recommendations.append({
625
+ "المشكلة": "عدد كبير من ملفات JavaScript",
626
+ "الحل": "دمج وضغط ملفات JavaScript",
627
+ "الأولوية": "عالية"
628
+ })
629
+
630
+ return recommendations if recommendations else [{"المشكلة": "لا توجد مشاكل", "الحل": "الأداء جيد!", "الأولوية": "منخفضة"}]
631
+
632
+ async def analyze_seo(self, url):
633
  try:
634
+ async with httpx.AsyncClient() as client:
635
+ response = await client.get(url)
636
+ soup = BeautifulSoup(response.text, 'html.parser')
637
+
638
+ # تحليل المحتوى
639
+ content_analysis = self._analyze_content(soup)
640
+
641
+ # تحليل الروابط
642
+ links_analysis = self._analyze_links(soup)
643
+
644
+ # تحليل الكلمات المفتاحية
645
+ keywords_analysis = self._extract_keywords(soup)
646
+
647
+ seo_analysis = {
648
+ "تحليل العنوان": self._analyze_title(soup),
649
+ "تحليل الوصف": self._analyze_description(soup),
650
+ "تحليل الكلمات المفتاحية": keywords_analysis,
651
+ "تحليل العناوين": self._analyze_headings(soup),
652
+ "تحليل الروابط": links_analysis,
653
+ "تحليل المحتوى": content_analysis,
654
+ "تقييم SEO": self._calculate_seo_score(soup),
655
+ "توصيات تحسين SEO": self._get_seo_recommendations(soup)
656
+ }
657
+
658
+ return seo_analysis
659
+ except Exception as e:
660
+ return {"error": f"خطأ في تحليل SEO: {str(e)}"}
661
 
662
+ def _analyze_content(self, soup):
663
+ # استخراج النص
664
+ text_content = ' '.join([p.text for p in soup.find_all('p')])
665
+
666
+ # تحليل طول المحتوى
667
+ word_count = len(text_content.split())
 
 
 
 
 
 
 
 
 
 
 
 
668
 
669
+ # تحليل قراءة المحتوى
670
+ readability_score = self._calculate_readability(text_content)
671
+
672
+ # تحليل كثافة الكلمات المفتاحية
673
+ keyword_density = self._calculate_keyword_density(text_content)
674
+
675
+ return {
676
+ "عدد الكلمات": word_count,
677
+ "مستوى القراءة": readability_score,
678
+ "كثافة الكلمات المفتاحية": keyword_density,
679
+ "��لتقييم": "ممتاز" if word_count > 300 and readability_score > 60 else "يحتاج تحسين"
680
+ }
681
+
682
+ def _calculate_readability(self, text):
683
+ # حساب مؤشر بسيط لسهولة القراءة
684
+ sentences = len(re.split(r'[.!?]+', text))
685
+ words = len(text.split())
686
+ if sentences == 0:
687
+ return 0
688
+ return min(100, round((words / sentences) * 10))
689
+
690
+ def _calculate_keyword_density(self, text):
691
+ words = text.lower().split()
692
+ word_freq = Counter(words)
693
+ total_words = len(words)
694
+
695
+ if total_words == 0:
696
+ return {}
697
 
698
+ return {word: round((count / total_words) * 100, 2)
699
+ for word, count in word_freq.most_common(5)}
700
+
701
+ def analyze_security(self, url):
702
+ try:
703
+ domain = urlparse(url).netloc
704
+ whois_info = self._get_whois_info(domain)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
 
706
+ security_analysis = {
707
+ "تحليل SSL": self._check_ssl(url),
708
+ "تحليل DNS": self._check_dns(domain),
709
+ "تحليل Headers": self._check_security_headers(url),
710
+ "فحص المخاطر": self._check_security_risks(url),
711
+ "معلومات Whois": whois_info,
712
+ "تقييم الأمان": self._calculate_security_score(url),
713
+ "توصيات الأمان": self._get_security_recommendations(url)
714
+ }
715
+ return security_analysis
716
+ except Exception as e:
717
+ return {"error": f"خطأ في تحليل الأمان: {str(e)}"}
718
+
719
+ def _get_whois_info(self, domain):
720
+ try:
721
+ w = whois.whois(domain)
722
+ return {
723
+ "اسم النطاق": domain,
724
+ "تاريخ التسجيل": str(w.creation_date),
725
+ "تاريخ الانتهاء": str(w.expiration_date),
726
+ "المسجل": w.registrar,
727
+ "الحالة": w.status
728
+ }
729
+ except:
730
+ return {"error": "لا يمكن الحصول على معلومات Whois"}
731
+
732
+ def _check_ssl(self, url):
733
+ try:
734
+ context = ssl.create_default_context()
735
+ with socket.create_connection((urlparse(url).netloc, 443)) as sock:
736
+ with context.wrap_socket(sock, server_hostname=urlparse(url).netloc) as ssock:
737
+ cert = ssock.getpeercert()
738
+ return {
739
+ "الحالة": "آمن ✅",
740
+ "نوع الشهادة": cert.get('subject', {}).get('commonName', 'Unknown'),
741
+ "تاريخ الإصدار": cert.get('notBefore', 'Unknown'),
742
+ "تاريخ الانتهاء": cert.get('notAfter', 'Unknown'),
743
+ "الخوارزمية": ssock.cipher()[0],
744
+ "قوة التشفير": f"{ssock.cipher()[2]} bits"
745
+ }
746
+ except:
747
+ return {
748
+ "الحالة": "غير آمن ❌",
749
+ "السبب": "لا يوجد شهادة SSL صالحة"
750
+ }
751
+
752
+ def _check_security_headers(self, url):
753
+ try:
754
+ response = requests.get(url)
755
+ headers = response.headers
756
+ security_headers = {
757
+ 'Strict-Transport-Security': 'HSTS',
758
+ 'Content-Security-Policy': 'CSP',
759
+ 'X-Frame-Options': 'X-Frame',
760
+ 'X-Content-Type-Options': 'X-Content-Type',
761
+ 'X-XSS-Protection': 'XSS Protection'
762
+ }
763
+
764
+ results = {}
765
+ for header, name in security_headers.items():
766
+ results[name] = {
767
+ "موجود": header in headers,
768
+ "القيمة": headers.get(header, "غير موجود")
769
+ }
770
+ return results
771
+ except:
772
+ return {"error": "فشل فحص headers الأمان"}
773
+
774
+ def _check_security_risks(self, url):
775
+ risks = []
776
+
777
+ # فحص بروتوكول HTTP
778
+ if not url.startswith('https'):
779
+ risks.append({
780
+ "المستوى": "عالي",
781
+ "النوع": "بروتوكول غير آمن",
782
+ "الوصف": "الموقع يستخدم HTTP بدلاً من HTTPS"
783
+ })
784
+
785
+ # فحص تحديث شهادة SSL
786
+ ssl_info = self._check_ssl(url)
787
+ if ssl_info.get("الحالة") == "غير آمن ❌":
788
+ risks.append({
789
+ "المستوى": "عالي",
790
+ "النوع": "شهادة SSL",
791
+ "الوصف": "شهادة SSL غير صالحة أو منتهية"
792
+ })
793
 
794
+ # فحص headers الأمان
795
+ headers = self._check_security_headers(url)
796
+ if isinstance(headers, dict) and not headers.get("HSTS", {}).get("موجود"):
797
+ risks.append({
798
+ "المستوى": "متوسط",
799
+ "النوع": "HSTS غير مفعل",
800
+ "الوصف": "عدم وجود حماية النقل الآمن الصارم"
801
+ })
802
 
803
+ return {
804
+ "المخاطر المكتشفة": risks,
805
+ "عدد المخاطر": len(risks),
806
+ "مستوى الخطورة": "عالي" if any(r["المستوى"] == "عالي" for r in risks) else "متوسط" if risks else "منخفض"
807
+ }
808
 
809
+ def _calculate_security_score(self, url):
810
+ score = 100
811
+
812
+ # فحص HTTPS
813
+ if not url.startswith('https'):
814
+ score -= 30
815
+
816
+ # فحص SSL
817
+ ssl_info = self._check_ssl(url)
818
+ if ssl_info.get("الحالة") == "غير آمن ❌":
819
+ score -= 25
820
+
821
+ # فحص Headers
822
+ headers = self._check_security_headers(url)
823
+ if isinstance(headers, dict):
824
+ for header_info in headers.values():
825
+ if not header_info.get("موجود"):
826
+ score -= 5
827
+
828
+ # فحص مخاطر الأمان
829
+ risks = self._check_security_risks(url)
830
+ score -= (risks.get("عدد المخاطر", 0) * 10)
831
+
832
+ return max(0, score)
833
+
834
+ def _get_security_recommendations(self, url):
835
+ recommendations = []
836
+
837
+ # فحص HTTPS
838
+ if not url.startswith('https'):
839
+ recommendations.append({
840
+ "المشكلة": "عدم استخدام HTTPS",
841
+ "الحل": "قم بتفعيل HTTPS وتثبيت شهادة SSL",
842
+ "الأولوية": "عالية"
843
+ })
844
+
845
+ # فحص SSL
846
+ ssl_info = self._check_ssl(url)
847
+ if ssl_info.get("الحالة") == "غير آمن ❌":
848
+ recommendations.append({
849
+ "المشكلة": "شهادة SSL غير صالحة",
850
+ "الحل": "قم بتجديد أو تثبيت شهادة SSL جديدة",
851
+ "الأولوية": "عالية"
852
+ })
853
+
854
+ # فحص Headers
855
+ headers = self._check_security_headers(url)
856
+ if isinstance(headers, dict):
857
+ for name, info in headers.items():
858
+ if not info.get("موجود"):
859
+ recommendations.append({
860
+ "المشكلة": f"عدم وجود {name}",
861
+ "الحل": f"قم بإضافة header الأمان {name}",
862
+ "الأولوية": "متوسطة"
863
+ })
864
+
865
+ return recommendations if recommendations else [
866
+ {
867
+ "المشكلة": "لا توجد مشاكل أمنية واضحة",
868
+ "الحل": "استمر في مراقبة وتحديث إعدادات الأمان",
869
+ "الأولوية": "منخفضة"
870
+ }
871
+ ]