Mazenbs commited on
Commit
37ff93a
·
verified ·
1 Parent(s): 61469f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -151
app.py CHANGED
@@ -1,168 +1,98 @@
1
- import requests
 
2
  from bs4 import BeautifulSoup
3
- from fastapi import FastAPI, HTTPException
4
- from fastapi.responses import JSONResponse
5
- from pydantic import BaseModel, HttpUrl
6
- from typing import List, Dict, Optional
7
- import urllib.parse
8
- from fastapi.middleware.cors import CORSMiddleware
9
 
 
10
  app = FastAPI(
11
- title="Web Text Extractor API",
12
- description="API لاستخراج النصوص من صفحات الويب",
13
  version="1.0.0"
14
  )
15
 
16
- # إضافة CORS للسماح بالوصول من أي مصدر
17
- app.add_middleware(
18
- CORSMiddleware,
19
- allow_origins=["*"],
20
- allow_credentials=True,
21
- allow_methods=["*"],
22
- allow_headers=["*"],
23
- )
24
 
25
- # نموذج بيانات للإدخال
26
- class URLRequest(BaseModel):
27
- url: HttpUrl
28
- include_empty_text: Optional[bool] = False
29
- specific_tags: Optional[List[str]] = None
30
- exclude_tags: Optional[List[str]] = None
 
31
 
32
- def is_valid_url(url: str) -> bool:
33
- """التحقق من صحة الرابط"""
34
  try:
35
- result = urllib.parse.urlparse(url)
36
- return all([result.scheme, result.netloc])
37
- except:
38
- return False
 
 
 
39
 
40
- def fetch_webpage(url: str) -> str:
41
- """جلب محتوى صفحة الويب"""
42
- headers = {
43
- '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'
44
- }
45
- try:
46
- response = requests.get(url, headers=headers, timeout=10)
47
- response.raise_for_status()
48
- return response.text
49
- except requests.exceptions.RequestException as e:
50
- raise HTTPException(status_code=400, detail=f"فشل في جلب الصفحة: {str(e)}")
51
 
52
- def extract_text_from_tags(html_content: str, include_empty: bool = False,
53
- specific_tags: List[str] = None, exclude_tags: List[str] = None) -> List[Dict[str, str]]:
54
- """استخراج النصوص من جميع العلامات"""
55
- soup = BeautifulSoup(html_content, 'html.parser')
56
-
57
- # تحديد العلامات المراد استخراج النصوص منها
58
- if specific_tags:
59
- tags = soup.find_all(specific_tags)
60
- else:
61
- # استخراج جميع العلامات التي يمكن أن تحتوي على نص
62
- tags = soup.find_all(True)
63
-
64
- # فلترة العلامات المستبعدة
65
- if exclude_tags:
66
- tags = [tag for tag in tags if tag.name not in exclude_tags]
67
-
68
- result = []
69
-
70
- for tag in tags:
71
- # الحصول على النص مع تنظيفه
72
- text = tag.get_text(strip=True, separator=' ')
73
-
74
- # إذا كان النص فارغاً ولا نريد تضمينه
75
- if not text and not include_empty:
76
- continue
77
-
78
- # إضافة النتيجة إلى القائمة
79
- result.append({
80
- 'text': text if text else '',
81
- 'tag': tag.name,
82
- 'attrs': dict(tag.attrs) if tag.attrs else {}
83
- })
84
-
85
- return result
86
 
87
- @app.get("/")
88
- async def root():
89
- """الصفحة الرئيسية"""
90
- return {
91
- "message": "مرحباً بك في API استخراج نصوص صفحات الويب",
92
- "endpoints": {
93
- "POST /extract": "استخراج النصوص من رابط URL",
94
- "GET /health": "فحص حالة الخدمة"
95
- },
96
- "usage": {
97
- "example_request": {
98
- "url": "https://example.com",
99
- "include_empty_text": False,
100
- "specific_tags": ["p", "h1", "h2", "h3"],
101
- "exclude_tags": ["script", "style"]
102
- }
103
- }
104
- }
105
 
106
- @app.post("/parse", response_model=List[Dict[str, str]])
107
- async def extract_text(request: URLRequest):
108
- """
109
- استخراج النصوص من صفحة ويب
110
-
111
- - **url**: رابط الصفحة المراد تحليلها
112
- - **include_empty_text**: تضمين العلامات الفارغة (افتراضي: False)
113
- - **specific_tags**: قائمة بعلامات محددة لاستخراج النصوص منها (افتراضي: جميع العلامات)
114
- - **exclude_tags**: قائمة بعلامات لتجاهلها (مثل: script, style)
115
- """
116
-
117
- # جلب محتوى الصفحة
118
- html_content = fetch_webpage(str(request.url))
119
-
120
- # استخراج النصوص
121
- extracted_texts = extract_text_from_tags(
122
- html_content,
123
- include_empty=request.include_empty_text,
124
- specific_tags=request.specific_tags,
125
- exclude_tags=request.exclude_tags or ["script", "style", "meta", "link", "noscript"]
126
- )
127
-
128
- return extracted_texts
129
 
130
- @app.get("/parse")
131
- async def extract_text_get(url: str, include_empty: bool = False):
132
- """نسخة GET من استخراج النصوص (للسهولة)"""
133
- if not is_valid_url(url):
134
- raise HTTPException(status_code=400, detail="رابط غير صالح")
135
-
136
- html_content = fetch_webpage(url)
137
- extracted_texts = extract_text_from_tags(html_content, include_empty)
138
-
139
- return extracted_texts
140
 
141
- @app.get("/health")
142
- async def health_check():
143
- """فحص حالة الخدمة"""
144
- return {"status": "healthy", "service": "web-text-extractor"}
 
 
 
 
 
 
 
 
 
 
 
145
 
146
- @app.get("/tags-info")
147
- async def tags_info():
148
- """معلومات عن العلامات الشائعة التي تحتوي على نص"""
149
- common_tags = {
150
- "headings": ["h1", "h2", "h3", "h4", "h5", "h6"],
151
- "paragraphs": ["p"],
152
- "lists": ["li", "dt", "dd"],
153
- "text_formatting": ["span", "strong", "em", "b", "i", "mark", "small"],
154
- "links": ["a"],
155
- "tables": ["td", "th", "caption"],
156
- "quotes": ["blockquote", "q"],
157
- "other": ["div", "section", "article", "header", "footer", "nav", "main", "aside"]
158
- }
159
-
160
- return {
161
- "info": "العلامات الشائ��ة التي تحتوي على نص في صفحات الويب",
162
- "common_tags": common_tags,
163
- "excluded_by_default": ["script", "style", "meta", "link", "noscript", "img", "br", "hr"]
164
- }
165
 
166
- if __name__ == "__main__":
167
- import uvicorn
168
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
1
+ from fastapi import FastAPI, Query, HTTPException
2
+ import httpx
3
  from bs4 import BeautifulSoup
4
+ from typing import List, Dict, Any
 
 
 
 
 
5
 
6
+ # تهيئة تطبيق FastAPI
7
  app = FastAPI(
8
+ title="محلل محتوى الصفحة (Page Content Analyzer)",
9
+ description="تطبيق لاستخراج النصوص من صفحة ويب معينة بناءً على رابط URL.",
10
  version="1.0.0"
11
  )
12
 
13
+ # قائمة بالوسوم (Tags) التي يجب تجاهل محتواها عادةً (مثل النصوص البرمجية والأنماط)
14
+ # Ignore these common noise-generating tags
15
+ IGNORE_TAGS = {
16
+ 'script', 'style', 'noscript', 'head', 'meta', 'link', 'img', 'svg',
17
+ 'button', 'input', 'select', 'textarea', 'nav', 'footer', 'header',
18
+ 'form', 'iframe'
19
+ }
 
20
 
21
+ # دالة مساعدة لجلب وتحليل الرابط
22
+ async def fetch_and_analyze(url: str) -> List[Dict[str, str]]:
23
+ """
24
+ تقوم بجلب محتوى الرابط، وتحليله، واستخراج النصوص الموجودة داخل وسوم HTML.
25
+ """
26
+ # تعيين مهلة قصوى للطلب (بالثواني)
27
+ TIMEOUT = 15
28
 
 
 
29
  try:
30
+ # 1. جلب المحتوى باستخدام عميل غير متزامن
31
+ # Fetch content using an async client
32
+ async with httpx.AsyncClient(timeout=TIMEOUT, follow_redirects=True) as client:
33
+ response = await client.get(url)
34
+ # إطلاق استثناء في حال وجود رمز حالة خطأ (4xx أو 5xx)
35
+ response.raise_for_status()
36
+ html_content = response.text
37
 
38
+ # 2. تحليل المحتوى باستخدام BeautifulSoup
39
+ # Parse content using BeautifulSoup
40
+ soup = BeautifulSoup(html_content, 'html.parser')
 
 
 
 
 
 
 
 
41
 
42
+ results: List[Dict[str, str]] = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
+ # Find all tags that might contain text
45
+ # البحث عن جميع الوسوم التي قد تحتوي على نص
46
+ for element in soup.find_all(True):
47
+ # التحقق مما إذا كان الوسم خارج قائمة التجاهل وليس جزءاً من وسم آخر تم استخلاصه
48
+ # Check if the tag is not in the ignore list
49
+ if element.name not in IGNORE_TAGS:
50
+ # الحصول على النص النظيف والمجرد من المسافات البيضاء الزائدة
51
+ # Get the clean, stripped text
52
+ text = element.get_text(strip=True, separator=' ')
53
+
54
+ # التحقق من أن النص ليس فارغاً
55
+ # Check if the text is meaningful
56
+ if text:
57
+ # إضافة النص إلى قائمة النتائج بالتنسيق المطلوب: {'text': '...'}
58
+ results.append({'text': text})
 
 
 
59
 
60
+ return results
61
+
62
+ except httpx.InvalidURL:
63
+ raise HTTPException(status_code=400, detail="الرابط غير صالح. يرجى التأكد من أن الرابط صحيح ويتضمن http:// أو https://.")
64
+ except httpx.ConnectTimeout:
65
+ raise HTTPException(status_code=504, detail="انتهت مهلة الاتصال بالرابط.")
66
+ except httpx.HTTPStatusError as e:
67
+ # Handling HTTP error status codes
68
+ raise HTTPException(status_code=e.response.status_code, detail=f"فشل في جلب الرابط: {e.response.status_code} - {e.response.reason_phrase}")
69
+ except Exception as e:
70
+ # Catch other unexpected errors
71
+ raise HTTPException(status_code=500, detail=f"حدث خطأ غير متوقع أثناء التحليل: {type(e).__name__} - {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
72
 
 
 
 
 
 
 
 
 
 
 
73
 
74
+ @app.get(
75
+ "/analyze_url",
76
+ summary="تحليل محتوى الصفحة واستخراج النصوص",
77
+ description="يقوم بتحليل صفحة الويب المحددة واستخراج جميع النصوص الموجودة داخل وسوم HTML.",
78
+ response_model=List[Dict[str, str]]
79
+ )
80
+ async def get_page_content(
81
+ url: str = Query(
82
+ ...,
83
+ description="أدخل الرابط كاملاً (على سبيل المثال: https://www.google.com)",
84
+ example="https://www.google.com"
85
+ )
86
+ ):
87
+ """
88
+ المسار الرئيسي لاستقبال رابط URL وتنفيذ عملية التحليل.
89
 
90
+ - **url**: الرابط المراد تحليله.
91
+ - **النتيجة**: قائمة من القواميس، حيث يحتوي كل قاموس على نص تم استخلاصه من وسم: `{'text': 'النص المستخرج'}`.
92
+ """
93
+ # استدعاء دالة الجلب والتحليل
94
+ return await fetch_and_analyze(url)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ # معلومات تشغيل التطبيق (للتذكير)
97
+ # To run this app, you would use: uvicorn app:app --reload
98
+ # This instruction is for user reference only.