Ddgoai / app.py
THEZYZSTUDIO's picture
Update app.py
59ac583 verified
Raw
History Blame Contribute Delete
5.19 kB
"""
تطبيق Gradio يبحث في DuckDuckGo مباشرة (سحب HTML) بدون أي مفتاح API
مصمم ليعمل كواجهة + API في نفس الوقت على Hugging Face Spaces
"""
import requests
from urllib.parse import unquote, urlparse, parse_qs
from bs4 import BeautifulSoup
import gradio as gr
DDG_URL = "https://html.duckduckgo.com/html/"
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "ar,en-US;q=0.9,en;q=0.8",
"Referer": "https://html.duckduckgo.com/",
"Origin": "https://html.duckduckgo.com",
"Content-Type": "application/x-www-form-urlencoded",
}
def clean_ddg_link(raw_href: str) -> str:
"""يطلع الرابط الحقيقي من رابط DuckDuckGo الوسيط"""
if not raw_href:
return ""
if raw_href.startswith("//"):
raw_href = "https:" + raw_href
parsed = urlparse(raw_href)
qs = parse_qs(parsed.query)
if "uddg" in qs:
return unquote(qs["uddg"][0])
return raw_href
def search_duckduckgo(query: str):
"""
يبحث في DuckDuckGo ويرجع قائمة نتائج كـ list of dicts
تُستخدم من الواجهة ومن الـ API معاً
"""
if not query or not query.strip():
return {"query": query, "results": [], "error": "كلمة البحث فارغة"}
last_error = None
response = None
for attempt in range(3):
try:
response = requests.post(
DDG_URL,
data={"q": query},
headers=HEADERS,
timeout=12,
)
response.raise_for_status()
last_error = None
break
except requests.exceptions.RequestException as e:
last_error = e
continue
if last_error is not None or response is None:
return {
"query": query,
"results": [],
"error": f"فشل الاتصال بمحرك البحث بعد عدة محاولات: {str(last_error)}",
}
soup = BeautifulSoup(response.text, "html.parser")
results = []
for result_div in soup.select(".result"):
title_tag = result_div.select_one(".result__title a") or result_div.select_one("a.result__a")
snippet_tag = result_div.select_one(".result__snippet")
if not title_tag:
continue
title = title_tag.get_text(strip=True)
raw_link = title_tag.get("href", "")
link = clean_ddg_link(raw_link)
snippet = snippet_tag.get_text(strip=True) if snippet_tag else ""
if not link or not title:
continue
domain = urlparse(link).netloc.replace("www.", "")
results.append({
"title": title,
"link": link,
"snippet": snippet,
"displayed_link": domain,
})
return {"query": query, "results": results, "error": None}
def format_results_html(query: str):
"""يبني HTML منسق لعرض النتائج داخل واجهة Gradio نفسها (للاختبار المباشر)"""
data = search_duckduckgo(query)
if data["error"]:
return f"<div style='color:#ff6b6b;padding:16px'>⚠️ {data['error']}</div>"
if not data["results"]:
return "<div style='padding:16px;color:#888'>ما لقينا نتائج لهالبحث.</div>"
html = "<div style='display:flex;flex-direction:column;gap:14px;padding:8px'>"
for r in data["results"]:
html += f"""
<div style='border-bottom:1px solid #333;padding-bottom:12px'>
<div style='font-size:12px;color:#28e0c2;direction:ltr;text-align:right'>{r['displayed_link']}</div>
<a href="{r['link']}" target="_blank" style='font-size:16px;font-weight:700;color:#eef3f5;text-decoration:none'>{r['title']}</a>
<div style='font-size:13px;color:#999;margin-top:4px'>{r['snippet']}</div>
</div>
"""
html += "</div>"
return html
# ============ واجهة Gradio (للعرض المباشر داخل الـ Space) ============
with gr.Blocks(title="غوّاص - بحث DuckDuckGo") as demo:
gr.Markdown("## 🤿 غوّاص — بحث مباشر عبر DuckDuckGo")
with gr.Row():
query_input = gr.Textbox(label="ابحث عن أي شيء", placeholder="اكتب كلمة البحث هنا...")
search_btn = gr.Button("بحث", variant="primary")
output_html = gr.HTML()
search_btn.click(fn=format_results_html, inputs=query_input, outputs=output_html)
query_input.submit(fn=format_results_html, inputs=query_input, outputs=output_html)
# ============ API endpoint يستخدمه index.html من الخارج ============
api_output = gr.JSON(visible=False)
api_trigger = gr.Textbox(visible=False)
api_trigger.change(fn=search_duckduckgo, inputs=api_trigger, outputs=api_output, api_name="search")
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)