import gradio as gr
import logging
import os
import requests
import time
from datetime import datetime
from typing import Dict, List, Tuple, Optional
from bs4 import BeautifulSoup
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
# Iranian legal sources
IRANIAN_LEGAL_SOURCES = [
"https://rc.majlis.ir",
"https://dolat.ir",
"https://iribnews.ir"
]
class SimpleLegalDocument:
"""Simple document structure for demo"""
def __init__(self, title: str, content: str, source_url: str,
document_type: str = "web_page", importance_score: float = 0.5):
self.title = title
self.content = content
self.source_url = source_url
self.document_type = document_type
self.importance_score = importance_score
self.summary = content[:100] + "..." if len(content) > 100 else content
self.keywords = self._extract_keywords(content)
self.date_scraped = datetime.now().isoformat()
def _extract_keywords(self, text: str) -> List[str]:
"""Simple keyword extraction for demo"""
words = text.split()
word_freq = {}
for word in words:
if len(word) > 3 and word.isalpha():
word_freq[word] = word_freq.get(word, 0) + 1
return sorted(word_freq.keys(), key=lambda x: word_freq[x], reverse=True)[:5]
class SimpleLegalScraper:
"""Simple scraper for demo without heavy dependencies"""
def __init__(self, delay: float = 2.0):
self.delay = delay
self.last_request_time = 0
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'LegalScraperDemo/1.0 (Educational Research)',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'fa,en;q=0.9'
})
def _respect_delay(self):
"""Respect delay between requests"""
current_time = time.time()
time_since_last = current_time - self.last_request_time
if time_since_last < self.delay:
time.sleep(self.delay - time_since_last)
self.last_request_time = time.time()
def fetch_page(self, url: str) -> Optional[BeautifulSoup]:
"""Fetch and parse a web page"""
try:
self._respect_delay()
logger.info(f"Fetching: {url}")
response = self.session.get(url, timeout=15)
response.raise_for_status()
response.encoding = response.apparent_encoding or 'utf-8'
return BeautifulSoup(response.text, 'html.parser')
except Exception as e:
logger.warning(f"Failed to fetch {url}: {e}")
return None
def scrape_demo_content(self, urls: List[str], max_docs: int = 5) -> List[SimpleLegalDocument]:
"""Scrape demo content from websites"""
documents = []
for url in urls[:3]: # Limit to 3 URLs for demo
try:
soup = self.fetch_page(url)
if not soup:
continue
# Extract title
title = soup.find('title')
title_text = title.get_text(strip=True) if title else "صفحه وب"
# Extract content
content_elem = soup.find('body') or soup
content = content_elem.get_text(strip=True)
content = content[:500] # Limit content length
# Determine document type based on URL
doc_type = "web_page"
if 'majlis' in url:
doc_type = "law"
elif 'news' in url:
doc_type = "news"
# Create document
doc = SimpleLegalDocument(
title=title_text,
content=content,
source_url=url,
document_type=doc_type,
importance_score=0.7 if doc_type == "law" else 0.5
)
documents.append(doc)
logger.info(f"Scraped: {title_text[:50]}...")
if len(documents) >= max_docs:
break
except Exception as e:
logger.error(f"Error scraping {url}: {e}")
continue
return documents
class LegalScraperInterface:
def __init__(self):
self.scraper = SimpleLegalScraper(delay=2.0)
self.is_scraping = False
def scrape_real_sources(self, urls_text: str, max_docs: int) -> Tuple[str, str, str]:
"""Scrape websites from provided URLs"""
if self.is_scraping:
return "❌ اسکراپینگ در حال انجام است", "", ""
try:
self.is_scraping = True
urls = [url.strip() for url in urls_text.split('\n') if url.strip()]
if not urls:
urls = IRANIAN_LEGAL_SOURCES
documents = self.scraper.scrape_demo_content(urls, max_docs)
status = f"✅ اسکراپینگ کامل شد - {len(documents)} سند جمعآوری شد"
# Create summary
summary_lines = [
f"📊 **خلاصه نتایج:**",
f"- تعداد اسناد: {len(documents)}",
f"- منابع پردازش شده: {len(urls)}",
f"- زمان: {datetime.now().strftime('%H:%M:%S')}",
"",
"📋 **اسناد جمعآوری شده:**"
]
for i, doc in enumerate(documents, 1):
summary_lines.append(f"{i}. {doc.title[:50]}...")
summary = "\n".join(summary_lines)
# Create preview
preview_lines = []
for doc in documents[:3]:
preview_lines.extend([
f"**{doc.title}**",
f"نوع: {doc.document_type}",
f"منبع: {doc.source_url}",
f"امتیاز اهمیت: {doc.importance_score:.2f}",
f"خلاصه: {doc.summary}",
f"کلیدواژهها: {', '.join(doc.keywords[:3])}",
"---"
])
preview = "\n".join(preview_lines) if preview_lines else "هیچ سندی یافت نشد"
return status, summary, preview
except Exception as e:
error_msg = f"❌ خطا در اسکراپینگ: {str(e)}"
logger.error(error_msg)
return error_msg, "", ""
finally:
self.is_scraping = False
def get_database_stats(self) -> Tuple[str, str]:
"""Get simulated statistics"""
try:
stats_lines = [
"📊 **آمار پایگاه داده:**",
f"- کل اسناد: 15",
"",
"📈 **بر اساس نوع:**",
"- قوانین: 6",
"- اخبار: 5",
"- صفحات وب: 4",
"",
"⭐ **توزيع اهمیت:**",
"- بالا (>0.7): 8",
"- متوسط (0.4-0.7): 5",
"- پایین (<0.4): 2",
"",
"🔑 **کلیدواژههای برتر:**",
"- قانون: 12",
"- حقوق: 9",
"- ایران: 7",
"- مجلس: 6",
"- اسلامی: 5"
]
stats_text = "\n".join(stats_lines)
# Simple HTML visualization
viz_html = """
📊 نمودارهای تحلیلی
انواع اسناد
توزیع بر اساس نوع
سطح اهمیت
امتیازبندی اسناد
"""
return stats_text, viz_html
except Exception as e:
error_msg = f"خطا در تولید آمار: {str(e)}"
return error_msg, ""
def search_documents(self, query: str) -> str:
"""Simulate search functionality"""
if not query.strip():
return "لطفاً کلیدواژهای برای جستجو وارد کنید"
try:
# Sample search results
sample_results = [
{
"title": "قانون اساسی جمهوری اسلامی ایران",
"document_type": "law",
"similarity": 0.92,
"source": "https://rc.majlis.ir/fa/law/show/1",
"summary": "قانون اساسی جمهوری اسلامی ایران مصوب 1358 با اصلاحات بعدی"
},
{
"title": "آییننامه داخلی مجلس شورای اسلامی",
"document_type": "regulation",
"similarity": 0.85,
"source": "https://rc.majlis.ir/fa/regulation/show/1",
"summary": "مقررات مربوط به نحوه تشکیل و اداره جلسات مجلس شورای اسلامی"
}
]
result_lines = [f"🔍 **نتایج جستجو برای '{query}':**\n"]
for i, result in enumerate(sample_results, 1):
result_lines.extend([
f"**{i}. {result['title']}**",
f" نوع: {result['document_type']}",
f" امتیاز شباهت: {result['similarity']:.2f}",
f" منبع: {result['source']}",
f" خلاصه: {result['summary']}",
"---"
])
return "\n".join(result_lines)
except Exception as e:
error_msg = f"خطا در جستجو: {str(e)}"
return error_msg
def export_data(self) -> Tuple[str, str]:
"""Create sample export file"""
try:
import csv
# Create data directory if not exists
os.makedirs('/app/data', exist_ok=True)
filename = f"/app/data/legal_documents_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
# Sample data
sample_data = [
['title', 'type', 'score', 'source', 'keywords'],
['قانون اساسی جمهوری اسلامی ایران', 'law', '0.95', 'https://rc.majlis.ir/fa/law/show/1', 'قانون,اساسی,جمهوری'],
['آییننامه داخلی مجلس', 'regulation', '0.85', 'https://rc.majlis.ir/fa/regulation/show/1', 'آییننامه,مجلس,داخلی'],
['اخبار حقوقی روز', 'news', '0.65', 'https://iribnews.ir/news/456', 'اخبار,حقوقی,روز'],
['تحلیل قراردادهای بینالمللی', 'analysis', '0.75', 'https://dolat.ir/analysis/789', 'تحلیل,قرارداد,بینالمللی']
]
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(sample_data)
return f"✅ فایل نمونه با موفقیت تولید شد: {filename}", filename
except Exception as e:
error_msg = f"❌ خطا در تولید فایل: {str(e)}"
return error_msg, ""
def create_interface():
interface = LegalScraperInterface()
css = """
.gradio-container {
max-width: 1200px !important;
margin: auto;
font-family: 'Tahoma', sans-serif;
}
.header {
background: linear-gradient(135deg, #2c3e50, #3498db);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
margin-bottom: 20px;
}
.tab-content {
padding: 20px;
background: #f8f9fa;
border-radius: 10px;
margin-bottom: 20px;
}
"""
with gr.Blocks(css=css, title="اسکراپر اسناد حقوقی", theme=gr.themes.Soft()) as app:
gr.HTML("""
""")
with gr.Tab("🕷️ اسکراپینگ"):
gr.Markdown("## جمعآوری اسناد از منابع وب")
with gr.Row():
with gr.Column(scale=2):
urls_input = gr.Textbox(
label="📝 URL های منابع",
placeholder="هر URL را در یک خط وارد کنید:",
lines=5,
value="https://rc.majlis.ir\nhttps://dolat.ir\nhttps://iribnews.ir"
)
max_docs = gr.Slider(
label="حداکثر اسناد",
minimum=1,
maximum=10,
value=5,
step=1
)
scrape_btn = gr.Button("🚀 شروع اسکراپینگ", variant="primary")
with gr.Column(scale=1):
status_output = gr.Textbox(
label="⚡ وضعیت",
interactive=False,
lines=3
)
with gr.Row():
summary_output = gr.Textbox(
label="📊 خلاصه نتایج",
interactive=False,
lines=8
)
preview_output = gr.Textbox(
label="👁️ پیشنمایش اسناد",
interactive=False,
lines=8
)
scrape_btn.click(
fn=interface.scrape_real_sources,
inputs=[urls_input, max_docs],
outputs=[status_output, summary_output, preview_output]
)
with gr.Tab("🔍 جستجو"):
gr.Markdown("## جستجو در اسناد جمعآوری شده")
search_input = gr.Textbox(
label="🔍 کلیدواژه جستجو",
placeholder="مثال: قانون اساسی, آییننامه, حقوق"
)
search_btn = gr.Button("🔍 جستجو", variant="primary")
search_results = gr.Textbox(
label="📋 نتایج جستجو",
interactive=False,
lines=15
)
search_btn.click(
fn=interface.search_documents,
inputs=[search_input],
outputs=[search_results]
)
with gr.Tab("📊 آمار و تحلیل"):
gr.Markdown("## آمار و تحلیل دادهها")
stats_btn = gr.Button("📊 بروزرسانی آمار", variant="secondary")
with gr.Row():
stats_text = gr.Textbox(
label="📈 آمار متنی",
interactive=False,
lines=15
)
stats_plot = gr.HTML(
label="📊 نمودارهای تحلیلی"
)
stats_btn.click(
fn=interface.get_database_stats,
outputs=[stats_text, stats_plot]
)
with gr.Tab("💾 خروجی دادهها"):
gr.Markdown("## ذخیرهسازی و خروجی")
export_btn = gr.Button("💾 تولید فایل خروجی", variant="primary")
export_status = gr.Textbox(
label="📝 وضعیت",
interactive=False,
lines=2
)
export_file = gr.File(
label="📁 دانلود فایل",
visible=False
)
export_btn.click(
fn=interface.export_data,
outputs=[export_status, export_file]
)
with gr.Tab("📚 راهنما"):
gr.Markdown("""
# 🕷️ راهنمای اسکراپر اسناد حقوقی
## ویژگیها
- جمعآوری اسناد از منابع وب
- پردازش و تحلیل محتوا
- جستجوی پیشرفته
- آمار و گزارشهای تحلیلی
- خروجی در قالب CSV
## نحوه استفاده
1. در تب "اسکراپینگ" URL منابع را وارد کنید
2. دکمه "شروع اسکراپینگ" را بزنید
3. از تب "جستجو" برای یافتن اسناد استفاده کنید
4. آمار و تحلیلها را مشاهده کنید
5. فایلهای خروجی را دانلود کنید
## منابع پیشنهادی
- مجلس شورای اسلامی (rc.majlis.ir)
- پورتال دولت (dolat.ir)
- خبرگزاریهای معتبر
⚠️ **تذکر**: این ابزار برای مقاصد آموزشی و پژوهشی ارائه شده است.
""")
return app
def main():
"""Main entry point"""
print("🚀 راه اندازی اسکراپر اسناد حقوقی...")
# Create required directories
os.makedirs("/app/data", exist_ok=True)
# Create and launch interface
interface = create_interface()
interface.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
debug=False
)
if __name__ == "__main__":
main()