news 2026/5/7 9:43:39

自建搜索代理实践:基于Nginx与FastAPI构建聚合搜索系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
自建搜索代理实践:基于Nginx与FastAPI构建聚合搜索系统

1. 项目概述:一个自建搜索代理的实践

最近在折腾一个挺有意思的东西,我把它叫做“MySearch-Proxy”。这个名字听起来可能有点技术范儿,但说白了,它的核心目标很简单:在现有的网络环境下,为自己搭建一个更干净、更可控、更聚焦的搜索体验层。这玩意儿不是什么复杂的商业产品,而是一个可以自己部署、自己定制的代理服务,专门用来处理和优化你的搜索请求。

你可能会问,直接用浏览器搜不就行了?确实,但用久了你会发现一些问题。比如,搜索结果里夹杂着大量你不关心的推广信息;不同搜索引擎的排序逻辑和内容侧重天差地别,想做个横向对比得开好几个标签页;有时候,一些学术或技术类的小众网站内容,在主流搜索引擎里反而被埋得很深。MySearch-Proxy 就是想解决这些“痒点”。它像一个智能的搜索调度中心,你发出一条搜索指令,它帮你同时向多个后端搜索引擎(比如你可以配置谷歌、必应、DuckDuckGo,甚至是某些垂直领域的站内搜索)发起请求,然后对返回的结果进行去重、过滤、重新排序,最后以一个整洁统一的界面呈现给你。所有过程都在你自己的服务器上完成,搜索历史和请求模式完全由你自己掌控。

这个项目适合谁呢?首先是那些对信息质量有要求的研发人员、技术写作者或学生,他们需要高效、准确地获取技术文档、论文或解决方案。其次,是注重隐私、不希望搜索行为被单一商业实体过度分析的用户。最后,它也适合任何喜欢折腾、希望将常用网络服务“内化”为自己数字工具箱一部分的极客。实现它,你需要一点服务器运维的基础(比如会用 Linux 命令行),了解基本的网络代理原理,以及一些简单的脚本编写能力。接下来,我就把自己搭建和优化这套系统的全过程,包括设计思路、技术选型、踩过的坑和提升效率的技巧,毫无保留地分享出来。

2. 核心架构与设计思路拆解

2.1 为什么是“代理”模式?

在构思之初,我考虑过几种方案:浏览器插件、本地桌面应用、或者是这种服务端代理。最终选择代理模式,主要基于以下几个考量:

1. 全平台无感接入:代理服务部署在服务器上,任何能设置网络代理的设备(电脑、手机、平板)都可以立即使用,无需在每个设备上安装单独的客户端。你只需要在系统的网络设置或浏览器的代理配置中,指向你的服务器地址和端口即可。这种透明性带来了极大的便利。

2. 计算与资源分离:并发向多个搜索引擎发起请求、对大量返回的 HTML 页面进行解析和去重,这些操作都是计算和 I/O 密集型的。如果放在浏览器插件或本地应用里,会消耗用户本地设备的资源,可能造成卡顿,尤其是结果页数较多时。而服务器通常有更充沛的 CPU 和内存,网络带宽也更好,处理起来更从容,用户体验更流畅。

3. 规则集中化管理:所有过滤规则(比如屏蔽特定域名、关键词高亮、结果排序权重)都可以在服务端统一配置和更新。一旦我需要调整“屏蔽所有内容农场类网站”的规则,只需要在服务器上改一次,所有连接的设备立即生效。如果每个客户端都维护一套规则,同步和更新会是个噩梦。

4. 隐私控制边界清晰:代理服务器作为中间层,可以直接剥离用户请求中的个人标识信息(如浏览器指纹、部分 Cookie)后再转发给后端搜索引擎。同时,所有搜索日志(如果需要保留)也只存在于你自己掌控的服务器上,不会泄露到第三方。这个清晰的隐私边界是本地应用难以提供的。

2.2 技术栈选型与权衡

确定了代理模式,接下来就是技术选型。一个典型的搜索代理包含几个核心模块:代理服务器本身、请求调度器、HTML 解析器、结果去重与排序算法、以及前端展示界面。

1. 代理服务器基础:我选择了Nginx作为反向代理的核心。原因很简单:它性能极高、稳定性久经考验、配置灵活。通过 Nginx 的proxy_pass指令,我可以轻松地将请求转发到不同的后端搜索引擎。同时,Nginx 的sub_filter模块可以用来在返回的 HTML 内容中注入我们自己的 CSS 和 JavaScript,以实现界面的美化与功能增强,这是一种非侵入式的修改方式。

2. 请求调度与逻辑处理:纯 Nginx 配置虽然能转发请求,但复杂的逻辑(如并发请求、结果聚合)处理起来很吃力。因此,我引入了一个Python(FastAPI)编写的中间层应用。FastAPI 异步特性好,适合处理高并发的网络 I/O。这个中间层负责接收用户的搜索查询,然后使用asyncioaiohttp库并发地向预配置的多个搜索引擎 URL 发起请求。这里有个关键点:模拟真实的浏览器请求头(User-Agent、Accept-Language等),以避免被搜索引擎识别为机器人而返回验证码或简化版页面。

3. HTML 解析:从搜索引擎拿回来的是完整的 HTML 页面,我们需要从中精准地提取出标题、链接、摘要这些结构化信息。BeautifulSoup4lxml是这个领域的黄金组合。BeautifulSoup 提供了友好的 Pythonic API,而 lxml 作为解析后端,速度非常快。我们需要为每个目标搜索引擎编写一个专门的“解析器”,因为它们的 HTML 结构差异很大。例如,谷歌结果通常包裹在<div class=”g”>的标签里,而必应的结构则完全不同。

4. 去重与排序:这是体现代理“智能”的地方。去重主要基于结果的URL标题的语义相似度。简单的 URL 匹配可以去掉完全相同的链接。对于标题相似度,我使用了TF-IDF 向量化结合余弦相似度计算的方法。虽然不如大型 NLP 模型精准,但对于识别“同一事件的不同报道”或“同一软件的不同下载站”这类重复结果,效果已经足够好,且计算开销小。 排序则是一个综合考量的过程。基础的相关性分数由原始搜索引擎的排名决定(例如,谷歌第一页的结果通常比第五页的相关性更高)。在此基础上,我会加入自定义的权重:

  • 域名权重:我可以手动给stackoverflow.comdeveloper.mozilla.org等技术站点更高的权重,让它们排名靠前。
  • 关键词惩罚:如果结果链接或摘要中包含“下载站”、“破解”、“广告密集”等特征词,会被降低权重。
  • 时效性加分:对新闻类搜索,发布日期较新的结果会获得加分。

5. 前端展示:为了极致轻量和控制力,我没有用 React/Vue 等重型框架,而是用了简单的HTML + JavaScript (原生) + Tailwind CSS。前端页面通过 Fetch API 与后端的 FastAPI 应用通信。Tailwind CSS 让我能快速构建出一个干净、响应式的界面,而不需要写大量自定义的 CSS。结果以卡片形式呈现,清晰展示来源搜索引擎、标题、摘要和链接。

注意:技术选型没有银弹。这里选择 Python 和 Nginx 是基于快速开发和部署的考虑。如果你的场景对并发性能要求极高(每秒数千请求),或许可以考虑 Go 语言重写核心代理逻辑。但对于个人或小团队使用,当前栈完全够用,且生态丰富,出了问题也容易排查。

3. 核心模块实现细节与配置

3.1 Nginx 反向代理配置精讲

Nginx 在这里扮演着流量入口和初级路由的角色。它的配置直接影响了服务的稳定性和性能。

# /etc/nginx/sites-available/mysearch-proxy server { listen 80; server_name search.your-domain.com; # 替换为你的域名 # 强烈建议启用 HTTPS,使用 Let‘s Encrypt 免费证书 # listen 443 ssl; # ssl_certificate /path/to/fullchain.pem; # ssl_certificate_key /path/to/privkey.pem; location / { # 将根路径的请求转发给后端的 FastAPI 应用 proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location ~ ^/proxy/(?<engine>google|bing|duckduckgo)/ { # 这是一个关键技巧:通过路径来动态选择后端搜索引擎 # 用户请求 /proxy/google/search?q=hello,会被转发到谷歌 internal; # 标记为内部位置,只允许后端应用访问,防止用户直接调用 resolver 8.8.8.8; # 配置DNS解析器 set $target_host ""; set $target_url ""; # 根据 $engine 变量设置不同的代理目标 if ($engine = google) { set $target_host www.google.com; set $target_url https://www.google.com/search?$query_string; } if ($engine = bing) { set $target_host www.bing.com; set $target_url https://www.bing.com/search?$query_string; } if ($engine = duckduckgo) { set $target_host duckduckgo.com; set $target_url https://duckduckgo.com/html/?$query_string; } proxy_pass $target_url; proxy_set_header Host $target_host; # 关键:设置一个真实的 User-Agent,模仿 Chrome 浏览器 proxy_set_header 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"; proxy_set_header Accept-Language "en-US,en;q=0.9"; proxy_hide_header Set-Cookie; # 隐藏后端返回的 Cookie,避免污染 proxy_hide_header Cache-Control; add_header X-Search-Proxy-Engine $engine; # 自定义头,便于调试 } }

配置要点解析:

  1. 内部位置 (internal):/proxy/这个路径下的请求被标记为internal,意味着它只能由 Nginx 内部的其他请求(比如我们后端 FastAPI 发出的子请求)访问,而不能由用户直接从浏览器访问。这增加了安全性,防止接口被滥用。
  2. 动态代理目标: 利用 Nginx 的location正则捕获和set指令,我们实现了根据 URL 路径动态切换代理后端。这种设计比写死多个location块更清晰,也更容易扩展新的搜索引擎。
  3. 请求头伪装:proxy_set_header User-Agent是重中之重。使用一个常见的桌面浏览器 User-Agent,能极大降低被搜索引擎识别为爬虫的风险。Accept-Language头也会影响搜索引擎返回的语言版本。
  4. 隐藏不必要头部:proxy_hide_header用于移除后端返回的某些 HTTP 头,比如Set-Cookie。我们不希望搜索引擎的 Cookie 被设置到用户的浏览器中,这既是为了隐私,也是为了避免潜在的冲突。

3.2 FastAPI 后端:异步调度与聚合引擎

后端应用是大脑,它协调所有搜索任务。我使用 FastAPI 因为它自动生成的交互式 API 文档(Swagger UI)对调试非常友好。

# main.py 核心部分 from fastapi import FastAPI, Query, HTTPException from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles import aiohttp import asyncio from bs4 import BeautifulSoup from urllib.parse import quote_plus import re from typing import List, Dict import hashlib app = FastAPI(title=”MySearch-Proxy API”) # 挂载前端静态文件 app.mount(“/static”, StaticFiles(directory=”static”), name=”static”) # 搜索引擎配置 SEARCH_ENGINES = { “google”: {“url”: “http://localhost/proxy/google/search”, “name”: “Google”}, “bing”: {“url”: “http://localhost/proxy/bing/search”, “name”: “Bing”}, “duckduckgo”: {“url”: “http://localhost/proxy/duckduckgo/”, “name”: “DuckDuckGo”}, } async def fetch_search_results(session: aiohttp.ClientSession, engine: str, query: str) -> List[Dict]: “””并发获取单个搜索引擎的结果””” config = SEARCH_ENGINES[engine] try: async with session.get(config[“url”], params={“q”: query}, timeout=10) as resp: if resp.status == 200: html = await resp.text() return parse_results(engine, html) # 调用对应的解析函数 else: print(f”Engine {engine} returned status {resp.status}”) return [] except asyncio.TimeoutError: print(f”Engine {engine} timeout”) return [] except Exception as e: print(f”Error fetching from {engine}: {e}”) return [] def parse_results(engine: str, html: str) -> List[Dict]: “””解析HTML,提取结构化结果。这里以谷歌为例””” soup = BeautifulSoup(html, ‘lxml’) results = [] if engine == “google”: # 谷歌的搜索结果通常包含在 <div class=‘g’> 中 for item in soup.select(‘div.g’): link_elem = item.select_one(‘a[href^=”http”]’) title_elem = item.select_one(‘h3’) snippet_elem = item.select_one(‘div.VwiC3b’) if link_elem and title_elem: url = link_elem[‘href’] # 过滤掉非http/https或谷歌自家的链接(如图片、视频搜索) if not re.match(r’^https?://’, url) or ‘google.com/’ in url: continue title = title_elem.get_text(strip=True) snippet = snippet_elem.get_text(strip=True) if snippet_elem else ” results.append({ “title”: title, “url”: url, “snippet”: snippet, “engine”: “google”, “source”: “Google”, }) # … 此处需要为 bing, duckduckgo 编写类似的解析逻辑 return results @app.get(“/api/search”) async def search_all(q: str = Query(…, min_length=1)): “””主搜索接口,并发查询所有引擎””” if not q: raise HTTPException(status_code=400, detail=”Query cannot be empty”) async with aiohttp.ClientSession() as session: tasks = [] for engine in SEARCH_ENGINES.keys(): task = fetch_search_results(session, engine, q) tasks.append(task) # 并发执行所有搜索任务 results_lists = await asyncio.gather(*tasks, return_exceptions=True) # 扁平化并聚合结果 all_results = [] for lst in results_lists: if isinstance(lst, list): all_results.extend(lst) # 去重和排序 deduplicated_results = deduplicate_by_url_and_title(all_results) sorted_results = custom_ranking(deduplicated_results, q) return {“query”: q, “results”: sorted_results} def deduplicate_by_url_and_title(results: List[Dict]) -> List[Dict]: “””基于URL和标题相似度去重””” seen_urls = set() seen_title_hashes = set() unique_results = [] for result in results: url = result[“url”] # 1. 精确URL去重 if url in seen_urls: continue seen_urls.add(url) # 2. 简单标题去重(取前50字符的MD5,可优化为语义相似度) title_key = hashlib.md5(result[“title”][:50].encode()).hexdigest() if title_key in seen_title_hashes: continue seen_title_hashes.add(title_key) unique_results.append(result) return unique_results def custom_ranking(results: List[Dict], query: str) -> List[Dict]: “””自定义排序算法””” # 这里实现一个简单的加权评分系统 for result in results: score = 0 # 基础分:模拟原始排名(这里简化处理,实际可以从HTML中解析排名信息) score += 100 # 域名权重加成 if ‘stackoverflow.com’ in result[“url”]: score += 50 if ‘github.com’ in result[“url”]: score += 30 if ‘developer.mozilla.org’ in result[“url”]: score += 40 # 关键词惩罚 penalty_keywords = [‘download’, ‘crack’, ‘serial’, ‘注册码’] for kw in penalty_keywords: if kw in result[“title”].lower() or kw in result[“snippet”].lower(): score -= 80 break # 查询词在标题中的出现位置(越靠前越好) try: title_lower = result[“title”].lower() idx = title_lower.find(query.lower()) if idx != -1: score += max(0, 30 - idx) # 位置越靠前,加分越多 except: pass result[“_score”] = score # 添加一个临时评分字段 # 按评分降序排序 results.sort(key=lambda x: x.get(“_score”, 0), reverse=True) # 移除临时字段 for r in results: r.pop(“_score”, None) return results @app.get(“/”, response_class=HTMLResponse) async def read_index(): “””返回前端主页面””” with open(“static/index.html”, “r”) as f: return HTMLResponse(content=f.read())

代码逻辑解析:

  1. 异步并发 (asyncio.gather): 这是性能关键。asyncio.gather允许我们同时发起所有搜索引擎的请求,总耗时约等于最慢的那个引擎的响应时间,而不是它们的总和。
  2. 结构化解析: 每个搜索引擎都需要一个独立的parse_results函数。这是因为它们的 HTML 结构差异巨大。编写这个函数需要仔细分析目标网站的页面源码,通常需要用到 Chrome 开发者工具的“检查”功能。这个过程可能比较繁琐,且需要定期维护,因为搜索引擎可能会改版。
  3. 去重策略: 示例中使用了 URL 精确匹配和标题简单哈希去重。在实际生产中,你可能需要更智能的语义去重,比如使用jieba(中文)或nltk(英文)进行分词,然后计算 TF-IDF 向量和余弦相似度,当相似度超过一个阈值(如0.8)时视为重复。
  4. 评分排序:custom_ranking函数展示了如何将业务逻辑融入排序。你可以根据自己的偏好无限扩展这个评分规则。例如,给.edu.gov域名加分,给某些广告特征明显的摘要减分等。

实操心得:在编写解析器时,最头疼的是搜索引擎的反爬机制。除了伪装 User-Agent,有时还需要处理 JavaScript 渲染的内容(谷歌的部分结果可能是动态加载的)。对于简单情况,可以尝试在请求头中添加Accept: text/html并确保 Referer 头设置合理。如果遇到严重封锁,可能需要引入更复杂的策略,如使用playwrightselenium进行无头浏览器渲染,但这会大幅增加资源消耗和延迟,仅作为最后手段。

4. 前端界面与交互实现

前端的目标是简洁、快速、信息清晰。我们使用原生 JavaScript 来保持轻量。

<!– static/index.html –> <!DOCTYPE html> <html lang=”en”> <head> <meta charset=”UTF-8”> <meta name=”viewport” content=”width=device-width, initial-scale=1.0”> <title>MySearch-Proxy</title> <script src=”https://cdn.tailwindcss.com”></script> <style> .result-card:hover { background-color: #f8fafc; } .loader { border-top-color: #3b82f6; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style> </head> <body class=”bg-gray-50 min-h-screen”> <div class=”container mx-auto px-4 py-8”> <header class=”text-center mb-10”> <h1 class=”text-4xl font-bold text-gray-800 mb-2”>MySearch-Proxy</h1> <p class=”text-gray-600”>一个干净、聚合、可定制的搜索界面</p> </header> <main> <div class=”max-w-3xl mx-auto”> <div class=”relative”> <input type=”text” id=”searchInput” placeholder=”输入关键词开始搜索 (例如: Python FastAPI 教程)…” class=”w-full px-6 py-4 text-lg border border-gray-300 rounded-full shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent” autocomplete=”off”> <button onclick=”performSearch()” class=”absolute right-3 top-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-6 rounded-full transition duration-200”> 搜索 </button> </div> <div class=”mt-4 flex flex-wrap gap-2 justify-center” id=”engineBadges”> <!– 搜索引擎标签,动态激活 –> <span class=”engine-badge px-3 py-1 rounded-full text-sm border border-blue-500 text-blue-500 bg-blue-50”># /etc/systemd/system/mysearch-proxy.service [Unit] Description=MySearch-Proxy Backend Service After=network.target [Service] User=www-data # 使用一个非root用户运行,更安全 Group=www-data WorkingDirectory=/opt/mysearch-proxy # 你的项目目录 Environment=”PATH=/opt/mysearch-proxy/venv/bin” # 如果你用了虚拟环境 ExecStart=/opt/mysearch-proxy/venv/bin/uvicorn main:app –host 0.0.0.0 –port 8000 –proxy-headers Restart=always # 进程崩溃后自动重启 RestartSec=3 [Install] WantedBy=multi-user.target

部署步骤:

  1. 将项目代码上传至服务器/opt/mysearch-proxy
  2. 安装 Python 依赖:pip install -r requirements.txt(需包含 fastapi, uvicorn, aiohttp, beautifulsoup4, lxml 等)。
  3. 配置好 Nginx 配置文件并启用:sudo ln -s /etc/nginx/sites-available/mysearch-proxy /etc/nginx/sites-enabled/,然后sudo nginx -t测试配置,sudo systemctl reload nginx重载。
  4. 启用并启动后端服务:sudo systemctl enable –now mysearch-proxy.service
  5. 使用sudo systemctl status mysearch-proxysudo journalctl -u mysearch-proxy -f来检查状态和跟踪日志。

5.2 性能优化策略

随着使用量增加,性能问题会浮现。以下是几个关键的优化方向:

1. 请求缓存:对相同的搜索查询,没必要每次都去骚扰搜索引擎。可以在 FastAPI 应用层加入缓存。对于个人使用,一个简单的内存缓存(如cachetools库的TTLCache)就够用,设置一个合理的 TTL(例如 5-10 分钟)。

from cachetools import TTLCache search_cache = TTLCache(maxsize=1000, ttl=600) # 缓存1000个查询,有效期10分钟 @app.get(“/api/search”) async def search_all(q: str = Query(…)): cache_key = f”{active_engine}:{q}” if cache_key in search_cache: return search_cache[cache_key] # … 正常搜索逻辑 … result = {“query”: q, “results”: sorted_results} search_cache[cache_key] = result return result

2. 异步解析优化:HTML 解析(BeautifulSoup)是 CPU 密集型操作,如果在主事件循环中同步进行,可能会阻塞其他异步任务。可以考虑将解析任务丢到线程池中执行,避免阻塞。

import concurrent.futures parse_executor = concurrent.futures.ThreadPoolExecutor(max_workers=4) async def fetch_and_parse(session, engine, query): html = await fetch_html(session, engine, query) # 将解析任务提交到线程池 loop = asyncio.get_event_loop() results = await loop.run_in_executor(parse_executor, parse_results, engine, html) return results

3. 限制与超时:必须为向外部的搜索引擎请求设置严格的超时(如 10 秒),并使用asyncio.gatherreturn_exceptions=True参数,确保一个引擎的失败不会导致整个搜索失败。同时,可以考虑限制用户每分钟的搜索频率,防止滥用。

5.3 安全与隐私加固

自建服务,安全不容忽视。

  1. 启用 HTTPS:使用 Let‘s Encrypt 为你的域名申请免费 SSL 证书,并在 Nginx 中强制启用 HTTPS。这能加密用户浏览器和你服务器之间的所有通信,防止搜索词被窃听。
  2. 访问控制:如果你的搜索代理仅供自己或小团队使用,最简单的办法是在 Nginx 层面配置HTTP 基本认证,或者只允许特定的 IP 地址访问。
    # Nginx 基本认证 auth_basic “Restricted Access”; auth_basic_user_file /etc/nginx/.htpasswd; # 使用 htpasswd 命令创建此文件
  3. 输入净化:对用户输入的搜索词进行基本的清理,防止 XSS 攻击。虽然我们的后端主要是转发,但净化输入是好习惯。FastAPI 的Query参数会自动处理一些基础问题。
  4. 日志管理:明确你的日志策略。如果你关心隐私,可以选择不记录搜索词,或者只记录聚合后的匿名统计信息(如每天搜索次数)。确保服务器日志文件有适当的权限和滚动策略,避免磁盘被撑满。

5.4 扩展性与自定义

这是 MySearch-Proxy 最有趣的部分——你可以无限扩展它。

  • 添加新的搜索引擎:只需在SEARCH_ENGINES字典和 Nginx 配置中添加一个新条目,并为其编写对应的parse_results函数。你可以加入WikipediaGitHub、甚至公司内部的文档系统。
  • 自定义过滤规则:custom_ranking函数中,你可以加入任何你想要的逻辑。比如,我写了一条规则,自动屏蔽所有 URL 中包含特定广告联盟模式域名的结果。
  • 结果后处理:你可以在结果返回给用户前,对摘要进行高亮显示搜索词,或者自动为某些特定网站(如 YouTube、GitHub)的链接生成一个快速预览图标。
  • 聚合其他服务:这个框架不限于搜索。你可以想象,将天气查询、词典翻译、快递跟踪等 API 调用聚合到同一个简洁的界面中,打造一个真正的个人信息门户。

6. 常见问题与故障排查实录

在实际搭建和运行过程中,你几乎一定会遇到下面这些问题。这里是我踩过坑后的经验总结。

问题一:Nginx 代理返回 502 Bad Gateway 错误。

  • 排查思路:
    1. 检查后端服务是否运行:sudo systemctl status mysearch-proxy。如果没运行,查看日志journalctl -u mysearch-proxy -n 50
    2. 检查端口和网络:确认 FastAPI 应用是否在0.0.0.0:8000监听。使用netstat -tlnp | grep :8000查看。确保防火墙(如 UFW)放行了 8000 端口。
    3. 检查 Nginx 错误日志:sudo tail -f /var/log/nginx/error.log。最常见的错误是connect() failed (111: Connection refused),这指向后端服务未启动或网络不通。
    4. 检查 Nginx 代理配置:确保proxy_pass的地址和端口正确。如果是本地服务,使用http://127.0.0.1:8000http://localhost:8000更可靠。

问题二:从某些搜索引擎获取的结果为空或解析失败。

  • 排查思路:
    1. 验证原始请求:首先,手动在服务器上用curl命令模拟请求,看看能否拿到正常的 HTML。例如:curl -A “Mozilla/5.0…” “https://www.google.com/search?q=test”。如果返回的是验证码页面或简化版页面,说明请求头伪装不够。
    2. 增强请求头:在 Nginx 的proxy_set_header或 Python 的aiohttp请求中,添加更多常见的浏览器头部,如AcceptAccept-EncodingReferer(可以设置为搜索引擎的域名)。
    3. 检查解析器:搜索引擎的页面结构可能已经改变。将获取到的 HTML 保存到文件,仔细检查你用来定位结果的 CSS 选择器是否还正确。可能需要更新parse_results函数中的选择器字符串。
    4. 处理动态内容:如果页面大量依赖 JavaScript 渲染(现代网站越来越常见),简单的 HTTP 请求获取的 HTML 是不完整的。这时需要评估是否引入无头浏览器,但务必谨慎,因为资源消耗巨大。

问题三:搜索响应速度很慢。

  • 排查步骤:
    1. 定位瓶颈:在代码中添加计时器,分别记录“网络请求总耗时”和“解析聚合耗时”。通常瓶颈在网络 I/O。
    2. 检查超时设置:确保为每个引擎的请求设置了合理的超时(如 8-10 秒)。一个引擎的卡顿会拖累整个并发请求。
    3. 启用缓存:如 5.2 节所述,引入缓存对重复查询的提升是立竿见影的。
    4. 检查服务器资源:使用tophtop命令查看服务器 CPU 和内存使用情况。如果解析任务太重,考虑使用线程池优化。
    5. 考虑地理延迟:如果你的服务器在海外,访问国内搜索引擎(如百度)可能会很慢。反之亦然。考虑为不同区域的引擎使用不同的代理策略,或者接受其延迟。

问题四:服务运行一段时间后内存占用越来越高。

  • 可能原因与解决:
    1. 内存泄漏:检查 Python 代码中是否有全局变量在无限累积数据(比如一个全局的 List 在不断 append)。缓存如果未设置大小或 TTL,也可能导致内存增长。确保使用TTLCache这类有自动淘汰机制的缓存。
    2. 解析器内存未释放:BeautifulSoup 解析大的 HTML 文档会生成复杂的 DOM 树。在处理完一个页面后,确保及时将变量引用置为None,或让它们离开作用域,以便垃圾回收。
    3. 监控与重启:对于长期运行的服务,可以配置一个简单的监控脚本,当内存超过阈值时,自动重启后端进程(systemctl restart mysearch-proxy)。这虽然粗暴,但对于个人项目往往是有效的兜底方案。

搭建这样一个 MySearch-Proxy 的过程,更像是一次对互联网信息获取方式的个性化改造。它没有改变互联网上的任何内容,只是在你和浩瀚信息海洋之间,架设了一个属于你自己的、可编程的过滤器与调度站。从最初的简单转发,到加入智能排序和过滤,再到为它优化性能和界面,每一步的调整都让这个工具更贴合我自己的使用习惯。最终,它节省的不仅仅是打开多个标签页的时间,更是一种心流不被频繁打断的专注。如果你也对默认的搜索体验感到些许不适,不妨花一个周末,从最基础的 Nginx 代理配置开始,亲手搭建一个。你会发现,掌控自己数字生活的一部分,感觉真的很好。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 9:40:31

AI Agent全栈开发工具箱run402:后端即服务与MCP集成实战

1. 项目概述&#xff1a;一个为AI Agent量身打造的全栈开发工具箱如果你正在为AI Agent&#xff08;比如Claude、Cursor里的AI助手&#xff09;寻找一个能“开箱即用”的后端服务&#xff0c;或者厌倦了手动配置数据库、部署函数、管理域名的繁琐流程&#xff0c;那么run402这个…

作者头像 李华
网站建设 2026/5/7 9:40:06

AegisAI:为AI编程助手构建命令执行审批的安全层

1. 项目概述&#xff1a;为AI助手戴上“紧箍咒”如果你和我一样&#xff0c;日常重度依赖Cursor、Windsurf这类AI编程助手&#xff0c;一边享受着它们带来的效率飞跃&#xff0c;一边又对那个“允许运行”的按钮提心吊胆&#xff0c;那么AegisAI这个项目&#xff0c;可能就是你…

作者头像 李华