Update app.py
Browse files
app.py
CHANGED
@@ -1,212 +1,250 @@
|
|
1 |
-
import
|
|
|
|
|
2 |
import requests
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
from bs4 import BeautifulSoup
|
5 |
-
import
|
6 |
-
import
|
7 |
-
|
8 |
-
|
9 |
-
import
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
Returns:
|
20 |
-
dict: IP and location details or error information
|
21 |
-
"""
|
22 |
-
try:
|
23 |
-
domain = urlparse(url).netloc
|
24 |
-
ip = socket.gethostbyname(domain)
|
25 |
-
with geoip2.database.Reader('GeoLite2-City.mmdb') as reader:
|
26 |
-
response = reader.city(ip)
|
27 |
-
return {
|
28 |
-
"ip": ip,
|
29 |
-
"city": response.city.name or "Unknown",
|
30 |
-
"region": response.subdivisions.most_specific.name or "Unknown",
|
31 |
-
"country": response.country.name or "Unknown",
|
32 |
-
"latitude": response.location.latitude or "Unknown",
|
33 |
-
"longitude": response.location.longitude or "Unknown",
|
34 |
-
}
|
35 |
-
except Exception as e:
|
36 |
-
return {"error": str(e)}
|
37 |
-
|
38 |
-
def analyze_uptime_free(url):
|
39 |
-
"""
|
40 |
-
Check website availability and response status
|
41 |
-
|
42 |
-
Args:
|
43 |
-
url (str): Website URL to check
|
44 |
-
|
45 |
-
Returns:
|
46 |
-
dict: Uptime status and status code
|
47 |
-
"""
|
48 |
-
try:
|
49 |
-
response = requests.get(url, timeout=5)
|
50 |
-
return {
|
51 |
-
"status": "Up" if response.status_code == 200 else "Down",
|
52 |
-
"status_code": response.status_code,
|
53 |
-
}
|
54 |
-
except requests.exceptions.RequestException as e:
|
55 |
-
return {"status": "Down", "error": str(e)}
|
56 |
-
|
57 |
-
def analyze_seo_free(url):
|
58 |
-
"""
|
59 |
-
Extract basic SEO information from the website
|
60 |
-
|
61 |
-
Args:
|
62 |
-
url (str): Website URL to analyze
|
63 |
-
|
64 |
-
Returns:
|
65 |
-
dict: SEO-related metadata
|
66 |
-
"""
|
67 |
-
try:
|
68 |
-
response = requests.get(url)
|
69 |
-
soup = BeautifulSoup(response.text, 'html.parser')
|
70 |
-
title = soup.title.string if soup.title else "No Title"
|
71 |
-
meta_description = soup.find("meta", attrs={"name": "description"})
|
72 |
-
keywords = soup.find("meta", attrs={"name": "keywords"})
|
73 |
-
|
74 |
-
return {
|
75 |
-
"title": title,
|
76 |
-
"meta_description": meta_description["content"] if meta_description else "No Description",
|
77 |
-
"keywords": keywords["content"] if keywords else "No Keywords",
|
78 |
-
}
|
79 |
-
except Exception as e:
|
80 |
-
return {"error": str(e)}
|
81 |
-
|
82 |
-
def analyze_carbon_free(url):
|
83 |
-
"""
|
84 |
-
Estimate website's carbon footprint based on page size
|
85 |
-
|
86 |
-
Args:
|
87 |
-
url (str): Website URL to analyze
|
88 |
-
|
89 |
-
Returns:
|
90 |
-
dict: Page size and estimated CO2 emissions
|
91 |
-
"""
|
92 |
-
try:
|
93 |
-
response = requests.get(url)
|
94 |
-
page_size = len(response.content) / 1024 # in kilobytes
|
95 |
-
co2_estimation = page_size * 0.02 # rough CO2 emission estimate
|
96 |
-
return {
|
97 |
-
"page_size_kb": round(page_size, 2),
|
98 |
-
"estimated_co2_g": round(co2_estimation, 2),
|
99 |
-
}
|
100 |
-
except Exception as e:
|
101 |
-
return {"error": str(e)}
|
102 |
-
|
103 |
-
def draw_bar_chart(data, title, xlabel, ylabel):
|
104 |
-
"""
|
105 |
-
Create a bar chart visualization
|
106 |
-
|
107 |
-
Args:
|
108 |
-
data (dict): Data to visualize
|
109 |
-
title (str): Chart title
|
110 |
-
xlabel (str): X-axis label
|
111 |
-
ylabel (str): Y-axis label
|
112 |
-
"""
|
113 |
-
keys, values = list(data.keys()), list(data.values())
|
114 |
-
plt.figure(figsize=(8, 5))
|
115 |
-
plt.bar(keys, values, color='skyblue')
|
116 |
-
plt.title(title)
|
117 |
-
plt.xlabel(xlabel)
|
118 |
-
plt.ylabel(ylabel)
|
119 |
-
plt.tight_layout()
|
120 |
-
plt.savefig('chart.png')
|
121 |
-
plt.show()
|
122 |
|
123 |
-
|
124 |
-
|
125 |
-
Export analysis results to a PDF report
|
126 |
-
|
127 |
-
Args:
|
128 |
-
results (dict): Analysis results
|
129 |
-
file_path (str): Path to save PDF
|
130 |
-
"""
|
131 |
-
c = canvas.Canvas(file_path, pagesize=letter)
|
132 |
-
c.drawString(30, 750, "Website Analysis Report")
|
133 |
-
c.drawString(30, 730, "=" * 50)
|
134 |
-
y = 700
|
135 |
-
for section, content in results.items():
|
136 |
-
c.drawString(30, y, f"{section}:")
|
137 |
-
y -= 20
|
138 |
-
for key, value in content.items():
|
139 |
-
c.drawString(50, y, f"- {key}: {value}")
|
140 |
-
y -= 20
|
141 |
-
y -= 20
|
142 |
-
c.save()
|
143 |
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
|
151 |
-
|
152 |
-
url = st.text_input("أدخل رابط الموقع:", "https://example.com")
|
153 |
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
else:
|
169 |
-
st.json(uptime_data)
|
170 |
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
}
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
"
|
201 |
-
"
|
202 |
-
|
203 |
-
|
|
|
|
|
204 |
}
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
|
211 |
if __name__ == "__main__":
|
212 |
-
main()
|
|
|
|
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
|
6 |
+
import plotly.express as px
|
7 |
+
import plotly.graph_objects as go
|
8 |
+
from datetime import datetime
|
9 |
+
import httpx
|
10 |
+
import asyncio
|
11 |
+
import aiohttp
|
12 |
from bs4 import BeautifulSoup
|
13 |
+
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
|
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 |
+
r = requests.get(url)
|
33 |
+
if r.status_code != 200:
|
34 |
+
return None
|
35 |
+
return r.json()
|
36 |
|
37 |
+
lottie_analyzing = load_lottieurl("https://assets5.lottiefiles.com/packages/lf20_qpwbqki6.json")
|
|
|
38 |
|
39 |
+
# تصميم CSS مخصص
|
40 |
+
st.markdown("""
|
41 |
+
<style>
|
42 |
+
.main {
|
43 |
+
background-color: #f0f2f6;
|
44 |
+
}
|
45 |
+
.stButton>button {
|
46 |
+
color: white;
|
47 |
+
background-color: #ff4b4b;
|
48 |
+
border-radius: 10px;
|
49 |
+
padding: 15px 25px;
|
50 |
+
border: none;
|
51 |
+
}
|
52 |
+
.stButton>button:hover {
|
53 |
+
background-color: #ff6b6b;
|
54 |
+
border: none;
|
55 |
+
}
|
56 |
+
.metric-card {
|
57 |
+
background-color: white;
|
58 |
+
border-radius: 10px;
|
59 |
+
padding: 20px;
|
60 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
61 |
+
}
|
62 |
+
</style>
|
63 |
+
""", unsafe_allow_html=True)
|
64 |
|
65 |
+
class WebsiteAnalyzer:
|
66 |
+
def __init__(self):
|
67 |
+
self.headers = {
|
68 |
+
'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'
|
69 |
+
}
|
|
|
|
|
70 |
|
71 |
+
async def analyze_performance(self, url):
|
72 |
+
try:
|
73 |
+
start_time = time.time()
|
74 |
+
async with httpx.AsyncClient() as client:
|
75 |
+
response = await client.get(url)
|
76 |
+
load_time = time.time() - start_time
|
77 |
+
page_size = len(response.content) / 1024 # KB
|
78 |
+
|
79 |
+
return {
|
80 |
+
"load_time": round(load_time, 2),
|
81 |
+
"page_size": round(page_size, 2),
|
82 |
+
"status_code": response.status_code
|
83 |
+
}
|
84 |
+
except Exception as e:
|
85 |
+
return {"error": str(e)}
|
86 |
|
87 |
+
async def analyze_seo(self, url):
|
88 |
+
try:
|
89 |
+
async with httpx.AsyncClient() as client:
|
90 |
+
response = await client.get(url)
|
91 |
+
soup = BeautifulSoup(response.text, 'html.parser')
|
92 |
+
|
93 |
+
# تحليل العناوين
|
94 |
+
headings = {
|
95 |
+
'h1': len(soup.find_all('h1')),
|
96 |
+
'h2': len(soup.find_all('h2')),
|
97 |
+
'h3': len(soup.find_all('h3'))
|
98 |
+
}
|
99 |
+
|
100 |
+
# تحليل الروابط
|
101 |
+
links = soup.find_all('a')
|
102 |
+
internal_links = len([link for link in links if urlparse(link.get('href', '')).netloc == urlparse(url).netloc])
|
103 |
+
external_links = len(links) - internal_links
|
104 |
+
|
105 |
+
# تحليل الصور
|
106 |
+
images = soup.find_all('img')
|
107 |
+
images_without_alt = len([img for img in images if not img.get('alt')])
|
108 |
+
|
109 |
+
return {
|
110 |
+
"title": soup.title.string if soup.title else "لا يوجد عنوان",
|
111 |
+
"meta_description": soup.find("meta", {"name": "description"}).get("content") if soup.find("meta", {"name": "description"}) else "لا يوجد وصف",
|
112 |
+
"headings": headings,
|
113 |
+
"internal_links": internal_links,
|
114 |
+
"external_links": external_links,
|
115 |
+
"images_without_alt": images_without_alt
|
116 |
+
}
|
117 |
+
except Exception as e:
|
118 |
+
return {"error": str(e)}
|
119 |
|
120 |
+
def analyze_security(self, url):
|
121 |
+
try:
|
122 |
+
domain = urlparse(url).netloc
|
123 |
+
|
124 |
+
# فحص SSL
|
125 |
+
ctx = ssl.create_default_context()
|
126 |
+
with ctx.wrap_socket(socket.socket(), server_hostname=domain) as s:
|
127 |
+
s.connect((domain, 443))
|
128 |
+
cert = s.getpeercert()
|
129 |
+
|
130 |
+
# فحص DNS
|
131 |
+
dns_records = {
|
132 |
+
'A': [str(r) for r in dns.resolver.resolve(domain, 'A')],
|
133 |
+
'MX': [str(r) for r in dns.resolver.resolve(domain, 'MX')],
|
134 |
+
'TXT': [str(r) for r in dns.resolver.resolve(domain, 'TXT')]
|
135 |
}
|
136 |
+
|
137 |
+
# معلومات WHOIS
|
138 |
+
domain_info = whois.whois(domain)
|
139 |
+
|
140 |
+
return {
|
141 |
+
"ssl_valid": True,
|
142 |
+
"ssl_expiry": cert['notAfter'],
|
143 |
+
"dns_records": dns_records,
|
144 |
+
"domain_info": {
|
145 |
+
"registrar": domain_info.registrar,
|
146 |
+
"creation_date": domain_info.creation_date,
|
147 |
+
"expiration_date": domain_info.expiration_date
|
148 |
+
}
|
149 |
}
|
150 |
+
except Exception as e:
|
151 |
+
return {"error": str(e)}
|
152 |
+
|
153 |
+
async def take_screenshot(self, url):
|
154 |
+
try:
|
155 |
+
chrome_options = Options()
|
156 |
+
chrome_options.add_argument('--headless')
|
157 |
+
chrome_options.add_argument('--no-sandbox')
|
158 |
+
chrome_options.add_argument('--disable-dev-shm-usage')
|
159 |
+
|
160 |
+
driver = webdriver.Chrome(ChromeDriverManager().install(), options=chrome_options)
|
161 |
+
driver.get(url)
|
162 |
+
driver.set_window_size(1920, 1080)
|
163 |
+
|
164 |
+
screenshot = driver.get_screenshot_as_png()
|
165 |
+
driver.quit()
|
166 |
+
|
167 |
+
return Image.open(io.BytesIO(screenshot))
|
168 |
+
except Exception as e:
|
169 |
+
return None
|
170 |
+
|
171 |
+
def main():
|
172 |
+
st.title("🔍 محلل المواقع المتقدم")
|
173 |
+
|
174 |
+
# إضافة قائمة جانبية
|
175 |
+
with st.sidebar:
|
176 |
+
selected = option_menu(
|
177 |
+
menu_title="القائمة الرئيسية",
|
178 |
+
options=["تحليل جديد", "التقارير السابقة", "الإعدادات"],
|
179 |
+
icons=["search", "file-text", "gear"],
|
180 |
+
menu_icon="cast",
|
181 |
+
default_index=0,
|
182 |
+
)
|
183 |
+
|
184 |
+
if selected == "تحليل جديد":
|
185 |
+
col1, col2 = st.columns([2, 1])
|
186 |
+
|
187 |
+
with col1:
|
188 |
+
url = st.text_input("أدخل رابط الموقع", "https://example.com")
|
189 |
+
if st.button("بدء التحليل"):
|
190 |
+
with st.spinner("جاري التحليل..."):
|
191 |
+
st_lottie(lottie_analyzing, height=200)
|
192 |
+
|
193 |
+
analyzer = WebsiteAnalyzer()
|
194 |
+
|
195 |
+
# تحليل متزامن
|
196 |
+
loop = asyncio.new_event_loop()
|
197 |
+
asyncio.set_event_loop(loop)
|
198 |
+
performance_data = loop.run_until_complete(analyzer.analyze_performance(url))
|
199 |
+
seo_data = loop.run_until_complete(analyzer.analyze_seo(url))
|
200 |
+
security_data = analyzer.analyze_security(url)
|
201 |
+
screenshot = loop.run_until_complete(analyzer.take_screenshot(url))
|
202 |
+
|
203 |
+
# عرض النتائج
|
204 |
+
st.success("تم اكتمال التحليل!")
|
205 |
+
|
206 |
+
# عرض البطاقات الإحصائية
|
207 |
+
cols = st.columns(3)
|
208 |
+
with cols[0]:
|
209 |
+
st.metric("زمن التحميل", f"{performance_data['load_time']}s")
|
210 |
+
with cols[1]:
|
211 |
+
st.metric("حجم الصفحة", f"{performance_data['page_size']} KB")
|
212 |
+
with cols[2]:
|
213 |
+
st.metric("الروابط الداخلية", seo_data['internal_links'])
|
214 |
+
|
215 |
+
# عرض تحليل SEO
|
216 |
+
with st.expander("تحليل SEO", expanded=True):
|
217 |
+
st.json(seo_data)
|
218 |
+
|
219 |
+
# رسم بياني للعناوين
|
220 |
+
fig = px.bar(
|
221 |
+
x=list(seo_data['headings'].keys()),
|
222 |
+
y=list(seo_data['headings'].values()),
|
223 |
+
title="توزيع العناوين",
|
224 |
+
labels={'x': 'نوع العنوان', 'y': 'العدد'}
|
225 |
+
)
|
226 |
+
st.plotly_chart(fig)
|
227 |
+
|
228 |
+
# عرض تحليل الأمان
|
229 |
+
with st.expander("تحليل الأمان", expanded=True):
|
230 |
+
st.json(security_data)
|
231 |
+
|
232 |
+
# عرض لقطة الشاشة
|
233 |
+
if screenshot:
|
234 |
+
st.image(screenshot, caption="لقطة شاشة للموقع", use_column_width=True)
|
235 |
+
|
236 |
+
with col2:
|
237 |
+
st.subheader("آخر التحليلات")
|
238 |
+
# هنا يمكن إضافة قائمة بآخر المواقع التي تم تحليلها
|
239 |
+
|
240 |
+
elif selected == "التقارير السابقة":
|
241 |
+
st.subheader("التقارير السابقة")
|
242 |
+
# هنا يمكن إضافة عرض للتقارير السابقة
|
243 |
+
|
244 |
+
elif selected == "الإعدادات":
|
245 |
+
st.subheader("إعدادات التحليل")
|
246 |
+
# هنا يمكن إضافة إعدادات التحليل
|
247 |
|
248 |
if __name__ == "__main__":
|
249 |
+
main()
|
250 |
+
|