:基于MCP的智能数据爬取与分析Agent)
一、理论部分1. MCP 的核心价值把“工具能力”从 Agent 中解耦出来在传统 Agent 实现中“工具”通常以内置函数的形式直接写在 Agent 代码里。这样做虽然简单但会带来三个工程问题耦合度高Agent 与工具强绑定工具升级、迁移或复用成本高。部署受限工具只能跟随 Agent 同进程、同语言运行跨语言/跨进程复用困难。能力难共享同一套工具能力难以被 IDE、其他应用或其他 Agent 体系复用。MCPModel Context Protocol的思路是把工具能力封装成标准化的MCP Server由 Agent 作为MCP Client去调用。工具成为独立进程甚至可远程部署的服务Agent 只关心“调用接口”和“拿到结果”。2. MCP 的运行形态Client/Server Transport一个 MCP 体系最关键的结构是Server提供工具Tools。每个工具有名字、参数与返回值。Client发起工具调用请求并消费返回结果。Transport通信通道。本项目使用最易本地调试的stdio标准输入/输出作为进程间通信方式Client 启动 Server 子进程通过 stdin/stdout 传递协议消息。stdio 模式的优势是易调试、零网络配置不足是跨机器调用需要改为 SSE/HTTP 等远程传输方式。3. 将 MCP 工具接入 Agent 编排用 LangGraph 管理“调用-再推理”的循环在“工具型 Agent”里典型模式是模型决定是否要调用工具并给出工具名与参数程序执行工具并拿到结果把工具结果反馈给模型继续推理直到完成任务本项目使用 LangGraph 组织这个循环agent节点负责决策tools节点负责执行工具当最后一条消息包含tool_calls时进入 tools否则结束。4. 数据分析工具的关键工程点受控执行 可视化落盘“让模型写分析代码再执行”属于高风险能力本项目做了两个关键约束以保证可运行性与可复现性非交互式绘图后端强制matplotlib使用Agg避免在无 GUI / 非主线程环境调用图形后端导致报错。统一保存接口提供save_plot(filename)要求模型生成图表时只保存不调用plt.show()。同时执行环境预置pd/plt/sns并自动注入中文字体兼容配置保证中文标题与标签可正常显示。二、实战部分1. 项目目标实现一个“智能数据爬取与分析 Agent”具备通过 MCP 工具爬取 GitHub Trending指定语言让模型生成分析代码整理数据、绘制图表柱状图执行分析代码并将图表保存到output/目录默认示例任务为分析 GitHub Trending 上 Python 热门项目并绘制 Star 数最高的 Top-5 柱状图。2. 运行准备请确保本地已安装依赖并配置环境变量不要把真实密钥写进代码或公开仓库依赖安装示例pipinstall-rrequirements.txt环境变量示例DEEPSEEK_API_KEYDEEPSEEK_BASE_URL说明本项目依赖mcp、requests、beautifulsoup4、pandas、matplotlib、seaborn等。3. 项目代码核心实现本项目由三部分组成MCP Server暴露工具、工具实现爬虫与分析执行器、MCP Client/AgentLangGraph 编排。3.1 MCP Server用 FastMCP 暴露两个工具Server 负责把“爬取”和“分析执行”封装成标准工具接口importosimportsys sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),../..)))frommcp.server.fastmcpimportFastMCPfromstage_06_advanced.project_10_data_analysis.tools.crawlerimportcrawl_github_trendingfromstage_06_advanced.project_10_data_analysis.tools.analysisimportexecute_analysis_code mcpFastMCP(DataAnalysisServer)mcp.tool()defget_github_trending(language:strpython)-str:resultscrawl_github_trending(language)returnstr(results)mcp.tool()defanalyze_data(code:str)-str:returnexecute_analysis_code(code)if__name____main__:mcp.run()工具接口设计要点get_github_trending(language)返回热门仓库列表名称、stars、描述analyze_data(code)执行模型生成的 Python 分析代码输出执行日志与保存路径3.2 爬虫工具抓取 GitHub Trending 并结构化为列表爬虫侧输出的是 Python 列表每个元素是字典便于后续转换为 DataFrameimportrequestsfrombs4importBeautifulSoupfromtypingimportList,Dictdefcrawl_github_trending(language:strpython)-List[Dict[str,str]]:urlfhttps://github.com/trending/{language}?sincedailyheaders{User-Agent:Mozilla/5.0}responserequests.get(url,headersheaders,timeout10)response.raise_for_status()soupBeautifulSoup(response.text,html.parser)articlessoup.find_all(article,class_Box-row)repos[]forarticleinarticles:title_tagarticle.find(h2,class_h3 lh-condensed)repo_nametitle_tag.get_text().strip().replace(\n,).replace( ,)iftitle_tagelseUnknowndesc_tagarticle.find(p,class_col-9 color-fg-muted my-1 pr-4)descriptiondesc_tag.get_text().strip()ifdesc_tagelseNo descriptionfooterarticle.find(div,class_f6 color-fg-muted mt-2)stars0iffooter:forlinkinfooter.find_all(a):ifstargazersinlink.get(href,):star_textlink.get_text().strip().replace(,,)starsint(star_text)ifstar_text.isdigit()else0breakrepos.append({name:repo_name,stars:stars,description:description})returnrepos3.3 分析执行器受控执行 save_plot 落盘分析执行器的目标是让模型可以写 pandas/matplotlib/seaborn 代码并在受控环境中执行且图表可保存到项目目录importsysimportioimportosimportmatplotlib matplotlib.use(Agg)importpandasaspdimportmatplotlib.pyplotaspltimportseabornassnsimporttracebackdefexecute_analysis_code(code:str)-str:old_stdoutsys.stdout redirected_outputio.StringIO()sys.stdoutredirected_output output_diros.path.join(os.path.dirname(os.path.dirname(__file__)),output)os.makedirs(output_dir,exist_okTrue)defsave_plot(filenameanalysis_result.png):filepathos.path.join(output_dir,filename)try:plt.savefig(filepath)print(f图表已保存至:{filepath})finally:plt.close()local_scope{pd:pd,plt:plt,sns:sns,save_plot:save_plot}font_fix import platform system_name platform.system() if system_name Windows: plt.rcParams[font.sans-serif] [Microsoft YaHei, SimHei, SimSun, Malgun Gothic] elif system_name Darwin: plt.rcParams[font.sans-serif] [Arial Unicode MS, PingFang SC, Heiti TC] else: plt.rcParams[font.sans-serif] [WenQuanYi Micro Hei, Noto Sans CJK SC, SimHei] plt.rcParams[axes.unicode_minus] False try:exec(font_fix\ncode,local_scope)resultredirected_output.getvalue()returnresultifresultelse代码执行成功 (无输出)。exceptException:returnf执行代码出错:\n{traceback.format_exc()}finally:sys.stdoutold_stdout3.4 MCP Client LangGraph把工具调用接入 Agent 决策循环Client 侧使用 stdio 启动 Server并把 MCP 工具包装成 LangChain 工具供 LangGraph 调用importosimportasynciofromdotenvimportload_dotenvfromlangchain_openaiimportChatOpenAIfromlangchain_core.messagesimportHumanMessagefromlangchain_core.toolsimportStructuredToolfromlanggraph.graphimportStateGraph,ENDfromlanggraph.graph.messageimportadd_messagesfromlanggraph.prebuiltimportToolNodefromtypingimportTypedDict,Annotated,Listfromlangchain_core.messagesimportBaseMessagefrommcpimportClientSession,StdioServerParametersfrommcp.client.stdioimportstdio_client load_dotenv()server_paramsStdioServerParameters(commandpython,args[os.path.join(os.path.dirname(__file__),server.py)],envos.environ.copy(),)asyncdef_call_mcp_tool(tool_name:str,arguments:dict):asyncwithstdio_client(server_params)as(read,write):asyncwithClientSession(read,write)assession:awaitsession.initialize()resultawaitsession.call_tool(tool_name,argumentsarguments)texts[c.textforcinresult.contentifc.typetext]return\n.join(texts)defcall_get_github_trending(language:strpython):returnasyncio.run(_call_mcp_tool(get_github_trending,{language:language}))defcall_analyze_data(code:str):returnasyncio.run(_call_mcp_tool(analyze_data,{code:code}))get_github_trending_toolStructuredTool.from_function(funccall_get_github_trending,nameget_github_trending,description获取 GitHub Trending 数据。,)analyze_data_toolStructuredTool.from_function(funccall_analyze_data,nameanalyze_data,description执行 Python 分析代码并保存图表。,)tools[get_github_trending_tool,analyze_data_tool]classAgentState(TypedDict):messages:Annotated[List[BaseMessage],add_messages]llmChatOpenAI(modeldeepseek-chat,temperature0,openai_api_basehttps://api.deepseek.com,openai_api_keyos.getenv(DEEPSEEK_API_KEY),)llm_with_toolsllm.bind_tools(tools)defagent_node(state:AgentState):responsellm_with_tools.invoke(state[messages])return{messages:[response]}defshould_continue(state:AgentState):last_messagestate[messages][-1]returntoolsiflast_message.tool_callselseEND workflowStateGraph(AgentState)workflow.add_node(agent,agent_node)workflow.add_node(tools,ToolNode(tools))workflow.set_entry_point(agent)workflow.add_conditional_edges(agent,should_continue,{tools:tools,END:END})workflow.add_edge(tools,agent)appworkflow.compile()4. 如何运行在项目根目录执行python stage_06_advanced/project_10_data_analysis/main.py程序会自动以 stdio 启动 MCP Server并执行默认示例任务。图表输出将保存到stage_06_advanced/project_10_data_analysis/output/5. 运行结果示例运行过程会输出 MCP 连接信息与 LangGraph 节点流转信息示例 启动 MCP 架构数据分析 Agent... 连接到 MCP Server: python [.../server.py] 用户: 请帮我分析一下 GitHub 上 Python 的热门项目画一个柱状图展示 Star 数最高的 5 个项目并保存图表。 ---- 节点: agent ---- ---- 节点: tools ---- ---- 节点: agent ---- ---- 节点: tools ---- ---- 节点: agent ---- ✅ 任务完成。请检查 stage_06_advanced/project_10_data_analysis/output 目录查看结果。在output/目录下你会看到生成的图表文件并在工具返回日志中看到保存路径例如图表已保存至: .../stage_06_advanced/project_10_data_analysis/output/python_top5_stars.png6. 项目总结MCP 把“工具能力”封装为标准化 Server使 Agent 与工具解耦便于跨进程、跨语言、跨应用复用。stdio 是最适合本地演示的传输方式零配置、易调试若要远程调用可扩展为 SSE/HTTP。对“代码执行型工具”必须做工程约束非交互式后端、统一保存接口、受控命名空间与中文字体处理保证可运行性与可复现性。将 MCP 工具接入 LangGraph 后Agent 可以以“可控循环”的方式完成检索数据 → 生成分析代码 → 执行 → 输出图表的完整闭环。