|
import streamlit as st |
|
import requests |
|
import whois |
|
import socket |
|
import ssl |
|
from bs4 import BeautifulSoup |
|
from datetime import datetime |
|
import matplotlib.pyplot as plt |
|
from wordcloud import WordCloud |
|
from langdetect import detect |
|
from textblob import TextBlob |
|
import nltk |
|
|
|
|
|
def download_nltk_data(): |
|
import os |
|
nltk_data_path = os.path.join(os.getcwd(), 'nltk_data') |
|
if not os.path.exists(nltk_data_path): |
|
os.makedirs(nltk_data_path) |
|
nltk.data.path.append(nltk_data_path) |
|
required_packages = ['wordnet', 'punkt', 'averaged_perceptron_tagger', 'brown'] |
|
for package in required_packages: |
|
try: |
|
nltk.data.find(f'corpora/{package}') |
|
except LookupError: |
|
nltk.download(package, download_dir=nltk_data_path) |
|
|
|
download_nltk_data() |
|
|
|
|
|
st.set_page_config(page_title="أداة تحليل المواقع", layout="wide") |
|
|
|
def get_ssl_expiry_date(hostname): |
|
try: |
|
context = ssl.create_default_context() |
|
with socket.create_connection((hostname, 443), timeout=3) as sock: |
|
with context.wrap_socket(sock, server_hostname=hostname) as ssock: |
|
ssl_info = ssock.getpeercert() |
|
expire_date = datetime.strptime(ssl_info['notAfter'], '%b %d %H:%M:%S %Y %Z') |
|
days_left = (expire_date - datetime.utcnow()).days |
|
return days_left |
|
except Exception: |
|
return None |
|
|
|
def main(): |
|
st.title("🕵️♂️ أداة تحليل المواقع الشاملة") |
|
|
|
url = st.text_input("أدخل رابط الموقع (مثال: https://www.example.com)") |
|
|
|
if st.button("تحليل") and url: |
|
if not url.startswith("http"): |
|
url = "http://" + url |
|
|
|
try: |
|
response = requests.get(url, timeout=5) |
|
status_code = response.status_code |
|
|
|
if status_code != 200: |
|
st.error(f"الموقع غير متاح. رمز الحالة: {status_code}") |
|
return |
|
|
|
|
|
soup = BeautifulSoup(response.content, 'html.parser') |
|
title = soup.title.string.strip() if soup.title else "لم يتم العثور على وسم العنوان" |
|
meta_desc = soup.find('meta', attrs={'name': 'description'}) |
|
meta_desc_content = meta_desc['content'].strip() if meta_desc else "لم يتم العثور على ميتا الوصف" |
|
|
|
|
|
texts = soup.get_text() |
|
blob = TextBlob(texts) |
|
keywords = blob.word_counts.items() |
|
sorted_keywords = sorted(keywords, key=lambda x: x[1], reverse=True)[:10] |
|
|
|
|
|
wordcloud = WordCloud(width=800, height=400, background_color='white').generate(texts) |
|
fig_wc, ax_wc = plt.subplots(figsize=(12, 6)) |
|
ax_wc.imshow(wordcloud, interpolation='bilinear') |
|
ax_wc.axis('off') |
|
|
|
|
|
domain = url.replace("http://", "").replace("https://", "").split('/')[0] |
|
domain_info = whois.whois(domain) |
|
|
|
|
|
ssl_days_left = get_ssl_expiry_date(domain) |
|
if ssl_days_left is not None: |
|
ssl_status = f"شهادة SSL تنتهي صلاحيتها خلال {ssl_days_left} يومًا" |
|
else: |
|
ssl_status = "لم يتم العثور على شهادة SSL" |
|
|
|
|
|
security_score = 0 |
|
headers = response.headers |
|
|
|
security_headers = { |
|
'X-Frame-Options': 'حماية من النقر', |
|
'X-Content-Type-Options': 'منع نوع المحتوى', |
|
'Content-Security-Policy': 'سياسة أمان المحتوى', |
|
'Strict-Transport-Security': 'أمان النقل الصارم', |
|
'Referrer-Policy': 'سياسة المرجع' |
|
} |
|
|
|
for header in security_headers: |
|
if header in headers: |
|
security_score += 10 |
|
|
|
|
|
total_score = security_score |
|
if title != "لم يتم العثور على وسم العنوان": |
|
total_score += 15 |
|
if meta_desc_content != "لم يتم العثور على ميتا الوصف": |
|
total_score += 15 |
|
if ssl_days_left is not None: |
|
total_score += 20 |
|
|
|
|
|
if total_score > 100: |
|
total_score = 100 |
|
|
|
|
|
st.header("📊 نتائج التحليل") |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
st.subheader("تحليل SEO") |
|
st.write(f"**وسم العنوان:** {title}") |
|
st.write(f"**ميتا الوصف:** {meta_desc_content}") |
|
|
|
st.subheader("أهم الكلمات تكرارًا") |
|
for word, count in sorted_keywords: |
|
st.write(f"- {word}: {count} مرة") |
|
|
|
st.pyplot(fig_wc) |
|
|
|
with col2: |
|
st.subheader("تحليل الأمان") |
|
st.write(f"**حالة SSL:** {ssl_status}") |
|
st.write("**عناوين الأمان:**") |
|
for header, desc in security_headers.items(): |
|
if header in headers: |
|
st.write(f"- {header}: متوفر ({desc})") |
|
else: |
|
st.write(f"- {header}: غير متوفر ({desc})") |
|
|
|
st.subheader("معلومات WHOIS") |
|
st.write(f"**اسم النطاق:** {domain_info.domain_name}") |
|
st.write(f"**المسجل:** {domain_info.registrar}") |
|
st.write(f"**تاريخ الإنشاء:** {domain_info.creation_date}") |
|
st.write(f"**تاريخ الانتهاء:** {domain_info.expiration_date}") |
|
|
|
st.header("🏆 التقييم الكلي") |
|
st.write(f"**التقييم الإجمالي:** {total_score} / 100") |
|
|
|
|
|
labels = ['SEO', 'الأمان', 'SSL', 'أخرى'] |
|
sizes = [30 if title != "لم يتم العثور على وسم العنوان" else 0, |
|
security_score, |
|
20 if ssl_days_left is not None else 0, |
|
100 - (30 + security_score + (20 if ssl_days_left is not None else 0))] |
|
fig_score, ax_score = plt.subplots() |
|
ax_score.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=140) |
|
ax_score.axis('equal') |
|
st.pyplot(fig_score) |
|
|
|
st.header("💡 اقتراحات للتحسين") |
|
suggestions = [] |
|
if title == "لم يتم العثور على وسم العنوان": |
|
suggestions.append("- أضف وسم العنوان إلى صفحتك الرئيسية.") |
|
if meta_desc_content == "لم يتم العثور على ميتا الوصف": |
|
suggestions.append("- أضف ميتا الوصف إلى صفحتك لتحسين SEO.") |
|
if ssl_days_left is None: |
|
suggestions.append("- قم بتثبيت شهادة SSL لتأمين موقعك باستخدام HTTPS.") |
|
for header in security_headers: |
|
if header not in headers: |
|
suggestions.append(f"- أضف عنوان {header} ({security_headers[header]}) لتعزيز الأمان.") |
|
|
|
if suggestions: |
|
for suggestion in suggestions: |
|
st.write(suggestion) |
|
else: |
|
st.write("لا توجد اقتراحات. الموقع يبدو ممتازًا!") |
|
|
|
except Exception as e: |
|
st.error(f"حدث خطأ: {e}") |
|
|
|
if __name__ == "__main__": |
|
main() |