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()