import gradio as gr import logging import requests from datetime import datetime from typing import Dict, List, Optional, Tuple from enhanced_legal_scraper import EnhancedLegalScraper, LegalDocument, IRANIAN_LEGAL_SOURCES # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('/app/logs/legal_scraper.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) class LegalScraperInterface: def __init__(self): self.scraper = EnhancedLegalScraper(delay=1.5) self.is_scraping = False self.api_base_url = os.getenv("API_BASE_URL", "http://localhost:8000") def scrape_real_sources(self, urls_text: str, max_docs: int, doc_type: str) -> Tuple[str, str, str]: if self.is_scraping: return "❌ اسکراپینگ در حال انجام است", "", "" try: self.is_scraping = True urls = [url.strip() for url in urls_text.split('\n') if url.strip()] or IRANIAN_LEGAL_SOURCES status_msg = f"🔄 شروع اسکراپینگ {len(urls)} منبع..." max_docs = min(max_docs, 20) response = requests.post( f"{self.api_base_url}/api/scrape", json={"max_docs": max_docs} ) response.raise_for_status() result = response.json() documents = [LegalDocument(**doc) for doc in result["documents"]] status = f"✅ {len(documents)} سند با موفقیت جمع‌آوری شد" summary_lines = [ "📊 **خلاصه نتایج:**", f"- تعداد اسناد: {len(documents)}", f"- منابع پردازش شده: {len(urls)}", f"- زمان: {datetime.now().strftime('%H:%M:%S')}", "" ] if documents: doc_types = {} for doc in documents: doc_types[doc.document_type] = doc_types.get(doc.document_type, 0) + 1 summary_lines.append("📈 **توزیع بر اساس نوع:**") type_names = { 'law': 'قوانین', 'news': 'اخبار', 'ruling': 'آرا', 'regulation': 'آیین‌نامه', 'general': 'عمومی' } for doc_type, count in doc_types.items(): summary_lines.append(f"- {type_names.get(doc_type, doc_type)}: {count}") top_docs = sorted(documents, key=lambda x: x.importance_score, reverse=True)[:3] summary_lines.extend(["", "⭐ **مهم‌ترین اسناد:**"]) for doc in top_docs: summary_lines.append(f"- {doc.title[:50]}... (امتیاز: {doc.importance_score:.2f})") summary = "\n".join(summary_lines) preview_lines = [] for i, doc in enumerate(documents[:3], 1): preview_lines.extend([ f"**{i}. {doc.title}**", f"🏷️ نوع: {type_names.get(doc.document_type, doc.document_type)} | ⭐ امتیاز: {doc.importance_score:.2f}", f"🔗 منبع: {doc.source_url}", f"📝 خلاصه: {doc.summary or 'خلاصه در دسترس نیست'}", f"📌 کلیدواژه‌ها: {', '.join(doc.keywords) if doc.keywords else 'ندارد'}", "" ]) preview = "\n".join(preview_lines) return status, summary, preview except Exception as e: logger.error(f"Scrape failed: {e}") return f"❌ خطا در اسکراپینگ: {str(e)}", "", "" finally: self.is_scraping = False def search_documents(self, query: str, search_type: str, doc_filter: str) -> str: try: if not query.strip(): return "❌ لطفاً عبارت جستجو را وارد کنید" filter_map = { 'همه': None, 'قوانین': 'law', 'اخبار': 'news', 'آرا': 'ruling', 'آیین‌نامه': 'regulation', 'عمومی': 'general' } response = requests.post( f"{self.api_base_url}/api/search", json={"query": query, "search_type": search_type, "doc_filter": doc_filter} ) response.raise_for_status() results = response.json() if not results: return "📭 هیچ نتیجه‌ای یافت نشد" output_lines = ["📋 **نتایج جستجو:**"] for i, result in enumerate(results[:10], 1): output_lines.extend([ f"**{i}. {result['title']}**", f"🏷️ نوع: {filter_map.get(result['document_type'], result['document_type'])}", f"⭐ امتیاز اهمیت: {result['importance_score']:.2f}", f"🔍 امتیاز شباهت: {result.get('similarity_score', 0.0):.2f}", f"🔗 منبع: {result['source_url']}", f"📝 خلاصه: {result['summary'] or 'خلاصه در دسترس نیست'}", f"📄 محتوا: {result['content'][:200]}...", "" ]) return "\n".join(output_lines) except Exception as e: logger.error(f"Search failed: {e}") return f"❌ خطا در جستجو: {str(e)}" def get_analytics(self) -> Tuple[str, str]: try: response = requests.get(f"{self.api_base_url}/api/statistics") response.raise_for_status() stats = response.json() text_lines = [ "📊 **تحلیل آماری اسناد حقوقی:**", f"- تعداد کل اسناد: {stats['total_documents']}", "", "🏷️ **توزیع بر اساس نوع:**" ] type_names = { 'law': 'قوانین', 'news': 'اخبار', 'ruling': 'آرا', 'regulation': 'آیین‌نامه', 'general': 'عمومی' } for doc_type, count in stats['by_type'].items(): text_lines.append(f"- {type_names.get(doc_type, doc_type)}: {count}") text_lines.extend(["", "📈 **توزيع بر اساس دسته‌بندی:**"]) for category, count in stats['by_category'].items(): text_lines.append(f"- {category}: {count}") text_lines.extend([ "", "⭐ **توزيع اهمیت:**", f"- بالا (>0.7): {stats['importance_distribution']['high']}", f"- متوسط (0.3-0.7): {stats['importance_distribution']['medium']}", f"- پایین (<0.3): {stats['importance_distribution']['low']}" ]) text_lines.extend(["", "🔑 **کلیدواژه‌های برتر:**"]) for kw, count in list(stats['top_keywords'].items())[:10]: text_lines.append(f"- {kw}: {count}") text_lines.extend(["", "📅 **فعالیت اخیر (هفت روز گذشته):**"]) for day, count in stats['recent_activity'].items(): text_lines.append(f"- {day}: {count} سند") text_summary = "\n".join(text_lines) import plotly.express as px import pandas as pd type_df = pd.DataFrame([ {'نوع': type_names.get(k, k), 'تعداد': v} for k, v in stats['by_type'].items() ]) type_fig = px.pie( type_df, names='نوع', values='تعداد', title='توزيع اسناد بر اساس نوع', color_discrete_sequence=px.colors.qualitative.Plotly ) type_fig.update_layout( title_x=0.5, margin=dict(l=20, r=20, t=50, b=20), height=400 ) importance_df = pd.DataFrame([ {'سطح': 'بالا (>0.7)', 'تعداد': stats['importance_distribution']['high']}, {'سطح': 'متوسط (0.3-0.7)', 'تعداد': stats['importance_distribution']['medium']}, {'سطح': 'پایین (<0.3)', 'تعداد': stats['importance_distribution']['low']} ]) importance_fig = px.bar( importance_df, x='سطح', y='تعداد', title='توزيع اسناد بر اساس اهمیت', color='سطح', color_discrete_sequence=px.colors.qualitative.Plotly ) importance_fig.update_layout( title_x=0.5, margin=dict(l=20, r=20, t=50, b=20), height=400 ) viz_html = f"""
{type_fig.to_html(full_html=False, include_plotlyjs='cdn')}
{importance_fig.to_html(full_html=False, include_plotlyjs='cdn')}
""" return text_summary, viz_html except Exception as e: logger.error(f"Analytics failed: {e}") return f"❌ خطا در تحلیل: {str(e)}", "

خطا در تولید نمودارها

" def export_data(self, export_format: str) -> Tuple[str, Optional[gr.File]]: try: timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') filename = f"legal_documents_{timestamp}.{export_format.lower()}" if export_format == "CSV": result = self.scraper.export_to_csv(filename) if result: return f"✅ فایل CSV با موفقیت تولید شد: {filename}", gr.File(value=filename, visible=True) else: return "❌ خطا در تولید فایل CSV", None else: import sqlite3 import pandas as pd import json conn = sqlite3.connect(self.scraper.db_path) query = ''' SELECT * FROM legal_documents ORDER BY importance_score DESC, date_scraped DESC ''' df = pd.read_sql_query(query, conn) conn.close() for col in ['tags', 'keywords', 'legal_entities', 'embedding']: if col in df.columns: df[col] = df[col].apply(lambda x: json.loads(x) if x else []) df.to_json(filename, orient='records', lines=True, force_ascii=False) return f"✅ فایل JSON با موفقیت تولید شد: {filename}", gr.File(value=filename, visible=True) except Exception as e: logger.error(f"Export failed: {e}") return f"❌ خطا در تولید فایل: {str(e)}", None def process_ocr(self, file: Optional[gr.File]) -> str: if not file: return "❌ لطفاً یک فایل PDF یا تصویر آپلود کنید" try: file_extension = os.path.splitext(file.name)[1].lower() endpoint = "/api/ocr/extract-pdf" if file_extension == '.pdf' else "/api/ocr/extract-image" with open(file.name, "rb") as f: files = {"file": (os.path.basename(file.name), f, "multipart/form-data")} response = requests.post(f"{self.api_base_url}{endpoint}", files=files) response.raise_for_status() result = response.json() if result["success"]: return f""" ✅ **نتایج OCR:** - **روش**: {result['method']} - **متن استخراج شده**: {result['text'][:500]}{'...' if len(result['text']) > 500 else ''} - **متادیتا**: {json.dumps(result['metadata'], ensure_ascii=False, indent=2)} """ else: return f"❌ خطا در OCR: {result['metadata']['error']}" except Exception as e: logger.error(f"OCR failed: {e}") return f"❌ خطا در پردازش OCR: {str(e)}" def create_interface(): interface = LegalScraperInterface() css = """ .tab-content { padding: 20px; background: #f8f9fa; border-radius: 10px; } .gradio-container { direction: rtl; font-family: 'Vazir', Arial, sans-serif; } h1, h2, h3 { text-align: center; color: #2c3e50; } .btn-primary { background-color: #3498db !important; } .btn-secondary { background-color: #7f8c8d !important; } """ with gr.Blocks(css=css, theme=gr.themes.Soft()) as app: gr.Markdown(""" # 🕷️ سامانه هوشمند جمع‌آوری و تحلیل اسناد حقوقی ایران **نسخه پیشرفته با هوش مصنوعی، OCR و پردازش زبان پارسی** | 🚀 مناسب برای حقوقدانان و پژوهشگران """) with gr.Tabs(): with gr.Tab("📥 جمع‌آوری اسناد"): gr.HTML('
') gr.Markdown("### 🌐 جمع‌آوری هوشمند اسناد حقوقی") with gr.Row(): urls_input = gr.Textbox( label="🔗 آدرس‌های منابع (اختیاری)", placeholder="هر URL در یک خط\nمثال:\nhttps://www.irna.ir\nhttps://www.tasnimnews.com", lines=5 ) max_docs = gr.Slider( label="📚 حداکثر تعداد اسناد", minimum=1, maximum=20, value=5, step=1, info="برای عملکرد بهتر، حداکثر ۲۰ سند" ) doc_type = gr.Dropdown( label="🏷️ نوع محتوا", choices=["همه", "قوانین", "اخبار", "آرا", "آیین‌نامه", "عمومی"], value="همه" ) scrape_btn = gr.Button("🚀 شروع جمع‌آوری واقعی", variant="primary") with gr.Row(): status_output = gr.Textbox( label="📡 وضعیت لحظه‌ای", placeholder="آماده برای شروع جمع‌آوری...", lines=5, interactive=False ) with gr.Row(): summary_output = gr.Textbox( label="📋 خلاصه تفصیلی نتایج", lines=15, interactive=False, show_copy_button=True ) preview_output = gr.Textbox( label="👁️ پیش‌نمایش اسناد پردازش شده", lines=15, interactive=False, show_copy_button=True ) scrape_btn.click( fn=interface.scrape_real_sources, inputs=[urls_input, max_docs, doc_type], outputs=[status_output, summary_output, preview_output] ) gr.HTML('
') with gr.Tab("🔍 جستجوی پیشرفته"): gr.HTML('
') gr.Markdown("### 🧠 جستجوی هوشمند با پردازش زبان طبیعی") with gr.Row(): search_input = gr.Textbox( label="🔍 عبارت جستجو", placeholder="مثال: قانون اساسی، حقوق شهروندی، آیین دادرسی مدنی، مقررات کار", scale=3, info="جستجوی هوشمند معنا و مفهوم را درنظر می‌گیرد" ) search_type = gr.Dropdown( label="🎯 نوع جستجو", choices=["هوشمند", "متنی"], value="هوشمند", info="هوشمند: بر اساس معنا | متنی: تطبیق کلمات", scale=1 ) doc_filter = gr.Dropdown( label="🏷️ فیلتر نوع سند", choices=["همه", "قوانین", "اخبار", "آرا", "آیین‌نامه", "عمومی"], value="همه", scale=1 ) search_btn = gr.Button("🔍 جستجوی پیشرفته", variant="primary") search_results = gr.Textbox( label="📋 نتایج جستجوی هوشمند", lines=20, interactive=False, show_copy_button=True, placeholder="نتایج جستجو با امتیاز شباهت و اهمیت اینجا نمایش داده می‌شود..." ) search_btn.click( fn=interface.search_documents, inputs=[search_input, search_type, doc_filter], outputs=[search_results] ) gr.HTML('
') with gr.Tab("📊 آمار و تحلیل هوشمند"): gr.HTML('
') gr.Markdown("### 📈 تحلیل جامع داده‌های جمع‌آوری شده") analytics_btn = gr.Button("🔄 بروزرسانی تحلیل‌های هوشمند", variant="secondary") with gr.Row(): stats_text = gr.Textbox( label="📈 گزارش آماری تفصیلی", lines=25, interactive=False, show_copy_button=True ) stats_viz = gr.HTML( label="📊 نمودارهای تعاملی پیشرفته", value="

📊 برای مشاهده نمودارهای تعاملی، دکمه بروزرسانی را بزنید

" ) analytics_btn.click( fn=interface.get_analytics, outputs=[stats_text, stats_viz] ) app.load( fn=interface.get_analytics, outputs=[stats_text, stats_viz] ) gr.HTML('
') with gr.Tab("💾 خروجی داده‌ها"): gr.HTML('
') gr.Markdown("### 📁 دانلود و خروجی داده‌های پردازش شده") with gr.Row(): export_format = gr.Dropdown( label="📋 فرمت خروجی", choices=["CSV", "JSON"], value="JSON", info="JSON شامل اطلاعات کامل NLP است" ) export_btn = gr.Button("💾 تولید فایل خروجی", variant="primary") export_status = gr.Textbox( label="📄 وضعیت خروجی", placeholder="وضعیت تولید فایل اینجا نمایش داده می‌شود...", interactive=False, lines=3 ) export_file = gr.File( label="📁 دانلود فایل", visible=True ) export_btn.click( fn=interface.export_data, inputs=[export_format], outputs=[export_status, export_file] ) gr.HTML('
') with gr.Tab("📄 پردازش OCR"): gr.HTML('
') gr.Markdown("### 📄 استخراج متن از PDF و تصاویر") file_input = gr.File( label="📤 آپلود فایل (PDF یا تصویر)", file_types=[".pdf", ".jpg", ".jpeg", ".png", ".bmp", ".tiff"] ) ocr_btn = gr.Button("🚀 پردازش فایل", variant="primary") ocr_output = gr.Textbox( label="📋 نتایج OCR", lines=15, interactive=False, show_copy_button=True ) ocr_btn.click( fn=interface.process_ocr, inputs=[file_input], outputs=[ocr_output] ) gr.HTML('
') with gr.Tab("📚 راهنما و اطلاعات"): gr.HTML('
') gr.Markdown(""" # 🕷️ راهنمای کاربری سیستم هوشمند اسناد حقوقی ایران ## 🌟 ویژگی‌های منحصر به فرد ### 🤖 هوش مصنوعی پیشرفته - **مدل ParsBERT**: پردازش حرفه‌ای متن‌های فارسی - **امتیازدهی هوشمند**: ارزیابی خودکار اهمیت اسناد (0-1) - **دسته‌بندی خودکار**: تشخیص نوع سند (قانون، رای، اخبار، آیین‌نامه) - **استخراج کلیدواژه**: شناسایی مفاهیم و اصطلاحات کلیدی - **تولید خلاصه**: خلاصه‌سازی هوشمند محتوا - **تحلیل نهادهای حقوقی**: شناسایی قوانین، مواد، دادگاه‌ها - **OCR پیشرفته**: استخراج متن از PDF و تصاویر - **رعایت robots.txt**: اسکراپینگ اخلاقی و قانونی ### 🌐 منابع واقعی ایرانی - **مرکز پژوهش‌های مجلس شورای اسلامی** (rc.majlis.ir) - **پورتال ملی دولت الکترونیک** (dolat.ir) - **خبرگزاری صدا و سیما** (iribnews.ir) - **خبرگزاری جمهوری اسلامی** (irna.ir) - **خبرگزاری تسنیم** (tasnimnews.com) - **خبرگزاری مهر** (mehrnews.com) - **خبرگزاری فارس** (farsnews.ir) ### 🔍 جستجوی معنایی - **درک معنا**: جستجو بر اساس مفهوم، نه فقط کلمات - **امتیاز شباهت**: نمایش میزان مرتبط بودن نتایج (0-1) - **رتبه‌بندی هوشمند**: ترکیب امتیاز شباهت و اهمیت - **فیلترهای پیشرفته**: جستجو بر اساس نوع، تاریخ، منبع ## 🚀 نحوه استفاده ### مرحله ۱: جمع‌آوری اسناد 📥 1. **انتخاب منابع**: منابع سفارشی وارد کنید یا از پیش‌فرض استفاده کنید 2. **تنظیم پارامترها**: نوع محتوا و تعداد مطلوب را مشخص کنید 3. **شروع فرآیند**: دکمه "شروع جمع‌آوری واقعی" را بزنید 4. **پیگیری پیشرفت**: وضعیت لحظه‌ای را دنبال کنید ### مرحله ۲: جستجوی هوشمند 🔍 1. **وارد کردن کلیدواژه**: عبارت مورد نظر را تایپ کنید 2. **انتخاب نوع جستجو**: هوشمند (پیشنهادی) یا متنی 3. **اعمال فیلتر**: نوع سند مورد نظر را انتخاب کنید 4. **بررسی نتایج**: امتیازها و خلاصه‌ها را مطالعه کنید ### مرحله ۳: تحلیل داده‌ها 📊 1. **مشاهده آمار**: اطلاعات کلی مجموعه داده‌ها 2. **بررسی نمودارها**: توزیع‌ها و روندهای تعاملی 3. **تحلیل کلیدواژه‌ها**: مفاهیم پرتکرار و مهم 4. **پیگیری فعالیت**: روند جمع‌آوری در زمان ### مرحله ۴: خروجی داده‌ها 💾 1. **انتخاب فرمت**: CSV (ساده) یا JSON (کامل با NLP) 2. **تولید فایل**: شامل تمام اطلاعات پردازش شده 3. **دانلود**: فایل آماده برای استفاده خارجی ### مرحله ۵: پردازش OCR 📄 1. **آپلود فایل**: PDF یا تصویر (JPG, PNG, BMP, TIFF) 2. **پردازش**: دکمه "پردازش فایل" را بزنید 3. **مشاهده نتایج**: متن استخراج شده و متادیتا ## 📊 شاخص‌های کیفی ### امتیاز اهمیت (Importance Score) - **0.8-1.0**: 🔥 اهمیت بسیار بالا (قوانین اساسی، آرای مهم) - **0.6-0.8**: ⭐ اهمیت بالا (قوانین عادی، اخبار مهم) - **0.4-0.6**: 🟡 اهمیت متوسط (آیین‌نامه‌ها، اخبار عادی) - **0.2-0.4**: ⚪ اهمیت پایین (مطالب عمومی) - **0.0-0.2**: 🔸 اهمیت خیلی پایین ### امتیاز شباهت (Similarity Score) - **0.9-1.0**: تطابق کامل با جستجو - **0.7-0.9**: شباهت بسیار بالا - **0.5-0.7**: شباهت قابل قبول - **0.3-0.5**: شباهت ضعیف - **0.0-0.3**: عدم مرتبط بودن ## ⚙️ تنظیمات پیشرفته ### بهینه‌سازی عملکرد - **حداکثر ۲۰ سند**: برای عملکرد بهتر در هاگینگ فیس - **تأخیر ۱.۵ ثانیه**: بین درخواست‌ها برای احترام به سرورها - **رعایت robots.txt**: جلوگیری از نقض قوانین سایت‌ها - **CPU Mode**: پردازش بر روی CPU برای سازگاری - **Memory Management**: مدیریت بهینه حافظه ### امنیت و اخلاق - **Rate Limiting**: محدودیت سرعت درخواست‌ها - **منابع عمومی**: فقط از محتوای عمومی استفاده - **حفظ حریم خصوصی**: عدم ذخیره اطلاعات شخصی ## 🔧 عیب‌یابی ### مشکلات رایج **❌ "سیستم آماده نیست"** - صفحه را بروزرسانی کنید - چند دقیقه صبر کنید (بارگذاری مدل) **❌ "هیچ سندی یافت نشد"** - منابع دیگری امتحان کنید - کلیدواژه‌های ساده‌تر استفاده کنید - نوع محتوای مختلف انتخاب کنید **⚠️ "خطا در اسکراپینگ یا OCR"** - اتصال اینترنت را بررسی کنید - منابع معتبر وارد کنید - تعداد کمتری درخواست کنید **🐌 "عملکرد آهسته"** - طبیعی است (پردازش هوش مصنوعی) - تعداد اسناد را کم کنید - از جستجوی متنی استفاده کنید ## 🆘 پشتیبانی ### راه‌های ارتباط - **GitHub Issues**: گزارش باگ و درخواست ویژگی - **Documentation**: راهنمای کامل فنی - **Community**: انجمن کاربران ### منابع یادگیری - **Tutorial Videos**: آموزش تصویری - **Best Practices**: بهترین روش‌های استفاده - **API Documentation**: راهنمای برنامه‌نویسی --- ## 📜 اطلاعات قانونی **⚖️ تذکر مهم**: این ابزار صرفاً برای مقاصد آموزشی، پژوهشی و اطلاع‌رسانی طراحی شده است. **📋 مسئولیت**: کاربران مسئول رعایت قوانین کپی‌رایت و حریم خصوصی هستند. **🔒 حریم خصوصی**: هیچ اطلاعات شخصی ذخیره یا منتقل نمی‌شود. --- **💡 نسخه**: 2.1 Enhanced | **📅 تاریخ**: مهر ۱۴۰۴ | **🏛️ Made for Iranian Legal Community** """) gr.HTML('
') return app if __name__ == "__main__": print("🚀 Starting Enhanced Iranian Legal Scraper...") print("🌟 Features: Persian NLP, Real Web Scraping, OCR, Smart Analytics") app = create_interface() app.launch( server_name="0.0.0.0", server_port=7860, share=True, show_error=True, show_tips=True, enable_queue=True )