import gradio as gr
import json
from typing import Dict, List, Any
import requests
class SearchUI:
def __init__(self):
self.api_key = "8af097ae8b587bb0569425058e03e5ef33b4c7b8b1a505053764b62e7e4ab9d6"
def parse_google_results(self, results: Dict) -> List[Dict]:
"""解析Google搜索结果"""
parsed = []
if 'organic_results' in results:
for result in results['organic_results']:
parsed.append({
'title': result.get('title', ''),
'link': result.get('link', ''),
'snippet': result.get('snippet', ''),
'position': result.get('position', 0)
})
if 'answer_box' in results:
answer = results['answer_box']
parsed.append({
'title': '直接回答:',
'link': answer.get('link', ''),
'snippet': answer.get('answer', ''),
'position': 0
})
return parsed
def parse_bing_results(self, results: Dict) -> List[Dict]:
"""解析Bing搜索结果"""
parsed = []
if 'organic_results' in results:
for result in results['organic_results']:
parsed.append({
'title': result.get('title', ''),
'link': result.get('link', ''),
'snippet': result.get('snippet', ''),
'position': result.get('position', 0)
})
return parsed
def parse_baidu_results(self, results: Dict) -> List[Dict]:
"""解析百度搜索结果"""
parsed = []
if 'organic_results' in results:
for result in results['organic_results']:
parsed.append({
'title': result.get('title', ''),
'link': result.get('link', ''),
'snippet': result.get('snippet', ''),
'position': result.get('position', 0)
})
if 'answer_box' in results:
for answer in results['answer_box']:
parsed.append({
'title': '直接回答:',
'link': answer.get('link', ''),
'snippet': f"{answer.get('answer', '')}\n来源: {answer.get('source', '')}",
'position': 0
})
return parsed
def format_results(self, results: List[Dict], engine: str) -> str:
"""将解析后的结果格式化为HTML卡片"""
formatted = ""
for result in sorted(results, key=lambda x: x['position']):
formatted += f"""
{result['title']}
{result['link']}
{result['snippet']}
"""
return f'{formatted}
'
def search_all_engines(self, query: str) -> tuple:
"""在所有搜索引擎中执行搜索并返回格式化结果"""
try:
results = {}
for engine in ["google", "bing", "baidu"]:
params = {
"api_key": self.api_key,
"engine": engine,
"q": query
}
if engine == "google":
params.update({"gl": "cn", "hl": "zh-cn"})
response = requests.get('https://serpapi.com/search', params=params)
if response.status_code == 200:
results[engine] = response.json()
else:
return f"{engine}搜索出错: HTTP {response.status_code}", "", ""
google_parsed = self.parse_google_results(results['google'])
google_text = self.format_results(google_parsed, "google")
bing_parsed = self.parse_bing_results(results['bing'])
bing_text = self.format_results(bing_parsed, "bing")
baidu_parsed = self.parse_baidu_results(results['baidu'])
baidu_text = self.format_results(baidu_parsed, "baidu")
return google_text, bing_text, baidu_text
except Exception as e:
return f"搜索出错: {str(e)}", f"搜索出错: {str(e)}", f"搜索出错: {str(e)}"
def create_ui():
search_ui = SearchUI()
custom_css = """
/* 全局样式 */
.container { max-width: 1400px; margin: 0 auto; }
/* 搜索引擎区域样式 */
.google-container, .bing-container, .baidu-container {
background: white;
border-radius: 15px;
padding: 15px;
height: calc(100vh - 200px);
overflow-y: auto;
scrollbar-width: thin;
}
.google-container { border: 2px solid rgba(66, 133, 244, 0.3); }
.bing-container { border: 2px solid rgba(0, 169, 157, 0.3); }
.baidu-container { border: 2px solid rgba(45, 97, 255, 0.3); }
/* 结果卡片样式 */
.result-card {
background: white;
border-radius: 10px;
padding: 15px;
margin: 10px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: all 0.3s ease;
cursor: pointer;
overflow: hidden;
word-wrap: break-word;
}
.result-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.card-title {
font-size: 1.1em;
font-weight: bold;
color: #1a0dab;
margin-bottom: 5px;
line-height: 1.4;
}
.card-link {
color: #006621;
font-size: 0.9em;
margin-bottom: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.card-snippet {
color: #545454;
font-size: 0.95em;
line-height: 1.5;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
}
/* 滚动条样式 */
.google-container::-webkit-scrollbar,
.bing-container::-webkit-scrollbar,
.baidu-container::-webkit-scrollbar {
width: 6px;
}
.google-container::-webkit-scrollbar-thumb { background: rgba(66, 133, 244, 0.3); }
.bing-container::-webkit-scrollbar-thumb { background: rgba(0, 169, 157, 0.3); }
.baidu-container::-webkit-scrollbar-thumb { background: rgba(45, 97, 255, 0.3); }
.google-container::-webkit-scrollbar-track,
.bing-container::-webkit-scrollbar-track,
.baidu-container::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
}
"""
with gr.Blocks(title="九鑫多搜索引擎聚合搜索", theme=gr.themes.Soft(), css=custom_css) as demo:
gr.Markdown("## 🔍 九鑫多搜索引擎聚合搜索")
with gr.Row():
query_input = gr.Textbox(
label="搜索关键词",
placeholder="请输入要搜索的内容...",
lines=1
)
search_button = gr.Button("搜索", variant="primary", scale=0.2)
loading_indicator = gr.HTML(
"""
正在搜索中...
""",
visible=False
)
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 🔵 Google 搜索结果")
google_output = gr.HTML(container=False)
with gr.Column(scale=1):
gr.Markdown("### 🟢 Bing 搜索结果")
bing_output = gr.HTML(container=False)
with gr.Column(scale=1):
gr.Markdown("### 🔷 百度搜索结果")
baidu_output = gr.HTML(container=False)
def on_search(query):
return [True, "", "", ""]
def after_search(query):
google, bing, baidu = search_ui.search_all_engines(query)
return [False, google, bing, baidu]
search_button.click(
fn=on_search,
inputs=[query_input],
outputs=[loading_indicator, google_output, bing_output, baidu_output],
).then(
fn=after_search,
inputs=[query_input],
outputs=[loading_indicator, google_output, bing_output, baidu_output],
show_progress="minimal"
)
return demo
if __name__ == "__main__":
demo = create_ui()
demo.launch(share=True, server_name="0.0.0.0", server_port=10083)