当前位置: 首页 > news >正文

【人工智能-agent】--Dify中MCP工具存数据到MySQL

本文记录的工作如下:

  1. 自定义MCP工具,爬取我的钢铁网数据
  2. 爬取的数据插值处理
  3. 自定义MCP工具,把爬取到的数据(str)存入本地excel表格中
  4. 自定义MCP工具,把爬取到的数据(str)存入本地MySQL数据库中
  5. 搭建MCP-server
  6. 使用Dify调用MCP工具,实现自动化爬取和存入

目录

1.工具--爬取数据

2.工具--保存到excel

3.工具--保存到MySQL中

4.搭建MCP-server

5.Dify调用MCP工具


1.工具--爬取数据

async def fetch_website(data_type: str,Start_Time: str,End_Time: str,
)-> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.edge.service import Servicefrom selenium.webdriver.edge.options import Optionsfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.action_chains import ActionChains# 设置Edge浏览器选项# try:# except Exception as e:#     print("Edge浏览器启动失败,请检查驱动是否正确安装!")#     print(e)all_data_dist = {"综合": "GANGCAIZONGHE","长材": "CHANGCAI","扁平": "BIANPING","一次材": "YICICAI","华东": "HUADONG","华南": "HUANAN","华北": "HUABEI","中南": "ZHONGNAN","东北": "DONGBEI","西南": "XINAN","西北": "XIBEI","螺纹": "LUOWEN","线材": "XIANCAI","型材": "XINCAI","中厚": "ZHONGHOU","锅炉容器板": "GUOLURONGQIBAN","造船板": "ZAOCHUANBAN","热卷": "REJUAN","窄带": "ZAIDAI","冷板": "LENGBAN","镀锌板卷": "DUXIN","无缝管": "WUFENGGUAN","聊城无缝钢管": "WUFENG_LIAOCHENG","焊管": "HANGUAN","盘扣式钢管脚手架": "PKSJSJ"}print("开始爬取数据")print("普钢的所有数据类型:" + str(all_data_dist.keys()))# Start_Time = input("请输入开始日期(格式:2024-01-01):") or "2024-04-01"Start_Time_year = Start_Time.split("-")[0]Start_Time_month = Start_Time.split("-")[1]Start_Time_day = Start_Time.split("-")[2]# day格式转换02需要去掉前导0Start_Time_day = str(int(Start_Time_day))print(f"开始日期:{Start_Time_year}-{Start_Time_month}-{Start_Time_day}")# End_Time = input("请输入结束日期(格式:2025-04-01):") or "2025-04-01"End_Time_year = End_Time.split("-")[0]End_Time_month = End_Time.split("-")[1]End_Time_day = End_Time.split("-")[2]# day格式转换02需要去掉前导0End_Time_day = str(int(End_Time_day))print(f"结束日期:{End_Time_year}-{End_Time_month}-{End_Time_day}")# data_type = input(#     "请输入需要爬取的数据类型:普钢['综合', '长材', '扁平', '一次材', '华东', '华南', '华北', '中南', '东北', '西南', '西北', '螺纹', '线材', '型材', '中厚', '锅炉容器板', '造船板', '热卷', '窄带', '冷板', '镀锌板卷', '无缝管', '聊城无缝钢管', '焊管', '盘扣式钢管脚手架']")data_type1 = all_data_dist[data_type]# 等待页面加载3s# time.sleep(3)# # element = driver.find_element(By.CLASS_NAME, "mRightBox")# # 等待页面加载3s-# time.sleep(3)edge_options = Options()edge_options.add_argument("--headless")  # 无头模式,不显示浏览器窗口edge_options.add_argument("--disable-gpu")edge_options.add_argument("--window-size=1920,1080")edge_service = Service('D:\桌面文件\edgedriver_win64\msedgedriver.exe')  # 替换为你的Edge驱动路径driver = webdriver.Edge(service=edge_service, options=edge_options)print("开始打开浏览器")url = "https://index.mysteel.com/xpic/detail.html?tabName=pugang"driver.get(url)time.sleep(3)driver.find_element(By.CSS_SELECTOR, "img.addBtn[src*='icon.png']").click()  # 点击展开print("点击展开")time.sleep(3)# print(element.text)try:# 点击类型key1 = driver.find_element(By.ID, data_type1)key1.click()# 等待页面加载3stime.sleep(1)# //*[@id="searchTimeLiDiv"]/ul/li[1]/a按日查询key2 = driver.find_element(By.XPATH, '//*[@id="searchTimeLiDiv"]/ul/li[1]/a')key2.click()time.sleep(1)# 起始日期//*[@id="startDay"]start_date = driver.find_element(By.XPATH, '//*[@id="startDay"]')# start_date.clear()start_date.click()time.sleep(1)driver.maximize_window()# # 解析年月日# target_date = "2021-09-01"# year, month, day = target_date.split('-')# # 等待日历面板加载# WebDriverWait(driver, 10).until(#     EC.presence_of_element_located((By.CSS_SELECTOR,  ".daterangepicker.dropdown-menu"))# )from selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import Select# 选择年份(如果页面有年份下拉框)year_dropdown = driver.find_element(By.XPATH, "/html/body/div[3]/div[2]/div/table/thead/tr[1]/th[2]/select[2]")year_dropdown.click()time.sleep(1)print("选择年份下拉框")# 选择年份<option value="1975">1975</option># year_dropdown.find_element(By.XPATH,  f"//option[@value='{year}']").click()# print(f"选择年份:{year}")select = Select(year_dropdown)select.select_by_visible_text(Start_Time_year)  # 根据文本选择print(f"选择年份:{Start_Time_year}")# 输出# 选择月份  /html/body/div[3]/div[2]/div/table/thead/tr[1]/th[2]/select[1]month_dropdown = driver.find_element(By.XPATH, "/html/body/div[3]/div[2]/div/table/thead/tr[1]/th[2]/select[1]")month_dropdown.click()time.sleep(1)print("选择月份下拉框")select1 = Select(month_dropdown)select1.select_by_visible_text(Start_Time_month)  # 根据文本选择print(f"选择月份:{Start_Time_month}")# 选择日期date_cell = driver.find_element(By.XPATH, f"//td[contains(@class, 'available') and text()='{Start_Time_day}']")date_cell.click()time.sleep(1)print(f"选择日期:{Start_Time_day}")except Exception as e:print(f"执行出错: {str(e)}")driver.save_screenshot('error.png')try:# //*[@id="searchTimeLiDiv"]/ul/li[1]/a按日查询key2 = driver.find_element(By.XPATH, '//*[@id="searchTimeLiDiv"]/ul/li[1]/a')key2.click()time.sleep(1)# 终止日期//*[@id="endDay"]end_date = driver.find_element(By.XPATH, '//*[@id="endDay"]')# start_date.clear()end_date.click()time.sleep(1)driver.maximize_window()# 解析年月日target_date = "2021-09-01"year, month, day = target_date.split('-')# # 等待日历面板加载# WebDriverWait(driver, 10).until(#     EC.presence_of_element_located((By.CSS_SELECTOR,  ".daterangepicker.dropdown-menu"))# )from selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import Select# 选择年份(如果页面有年份下拉框)year_end_dropdown = driver.find_element(By.XPATH,"/html/body/div[4]/div[2]/div/table/thead/tr[1]/th[2]/select[2]")year_end_dropdown.click()print("选择年份下拉框")# 选择年份<option value="1975">1975</option># year_dropdown.find_element(By.XPATH,  f"//option[@value='{year}']").click()# print(f"选择年份:{year}")select_year_end = Select(year_end_dropdown)select_year_end.select_by_visible_text(End_Time_year)  # 根据文本选择print(f"选择年份:{End_Time_year}")# 输出# 选择月份  /html/body/div[4]/div[2]/div/table/thead/tr[1]/th[2]/select[1]month_end_dropdown = driver.find_element(By.XPATH,"/html/body/div[4]/div[2]/div/table/thead/tr[1]/th[2]/select[1]")month_end_dropdown.click()print("选择月份下拉框")select_month_end = Select(month_end_dropdown)select1_month_end = select_month_end.select_by_visible_text(End_Time_month)  # 根据文本选择print(f"选择月份:{End_Time_month}")# /html/body/div[4]/div[2]# 找到右侧日历left_calendar = driver.find_element(By.XPATH,'/html/body/div[4]/div[2]')# 选择日期# left_calendar = driver.find_element(By.CLASS_SELECTOR, "calendar single left")# driver.find_element(By.XPATH, "//td[contains(@class, 'available') and text()='2']")date_end_cell = left_calendar.find_element(By.XPATH,f'.//td[text()={End_Time_day}]')date_end_cell.click()driver.save_screenshot('C:\pythonProject\python爬虫\我的钢铁网\end_date.png')print(f"选择日期:{End_Time_day}")except Exception as e:print(f"执行出错: {str(e)}")driver.save_screenshot('error.png')# 点击搜索按钮search_btn = driver.find_element(By.XPATH, '//*[@id="dome1"]/table/tbody/tr/td[5]/img')search_btn.click()# driver.save_screenshot('C:\pythonProject\python爬虫\我的钢铁网\搜索之后.png')element = driver.find_element(By.CLASS_NAME, "mRightBox")# 等待页面加载3s-time.sleep(3)#保存截图driver.save_screenshot('C:\pythonProject\项目五(钢铁价格预测)\我的钢铁网\搜索之后.png')print(element.text)# data_str = element.text# driver.quit()return [types.TextContent(type="text", text=element.text)]# return data_str

2.工具--保存到excel

async def Save_To_Excel(data_str:str):# 把爬取的text数据保存到Excel文档中import pandas as pdfrom openpyxl import load_workbookimport osimport re# 示例数据准备(替换为你的实际数据)# 按行拆分数据并转换为列表lines = [line.split() for line in data_str.strip().split('\n')]# 工作簿名称,去掉空格sheet_name = str(lines[0]).strip()sheet_name =  sheet_namesheet_name = re.sub(r'[^\u4e00-\u9fa5]', '', sheet_name)header = lines[1]  # 假设第一行是表头rows = lines[2:]  # 数据行df = pd.DataFrame(rows, columns=header)'''数据插值,然后存入表格'''print(df.columns.tolist())  # 查看所有列名df['时间'] = pd.to_datetime(df['时间'], format='%Y/%m/%d')  # 确保日期为datetime类型# 2. 创建完整日期范围(从数据最早日期到最晚日期)date_range = pd.date_range(start=df['时间'].min(),end=df['时间'].max(),freq='D')# 3. 重新索引并保留原始数据df = df.set_index('时间').reindex(date_range).rename_axis('时间').reset_index()# 4. 定义需要插值的数值列(根据实际Excel列名调整)numeric_cols = ['本日', '昨日', '日环比', '上周', '周环比','上月度', '与上月比', '去年同期', '与去年比']# 5. 线性插值填充(取前后值的平均值)for col in numeric_cols:if col in df.columns:if col in ['日环比', '周环比', '与上月比', '与去年比']:df[col] = df[col].str.rstrip('%').astype(float) / 100# 2. 线性插值df[col] = df[col].interpolate(method='linear')# 3. 还原为百分比字符串df[col] = (df[col] * 100).round(2).astype(str) + '%'else:# 2. 线性插值,保留两位小数# df[col] = df[col].astype(float).interpolate(method='linear', limit_direction='both', limit=2)df[col] = pd.to_numeric(df[col], errors='coerce')  # 非数值转为NaNdf[col] = df[col].interpolate(method='linear').round(2)# 6. 格式化日期为YYYY/MM/DDdf['时间'] = df['时间'].dt.strftime('%Y/%m/%d')from openpyxl.styles import Alignment# 目标文件路径(注意Windows路径要用双反斜杠或原始字符串)file_path = r"C:\pythonProject\项目五(钢铁价格预测)\我的钢铁网\gyp4.xlsx"# 核心逻辑:追加或新建if os.path.exists(file_path):# 追加模式(加载现有工作簿)with pd.ExcelWriter(file_path, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:book = load_workbook(file_path)if sheet_name in book.sheetnames:# # 读取现有数据并合并# existing_df = pd.read_excel(file_path,  sheet_name=sheet_name)# combined_df = pd.concat([existing_df,  new_df], ignore_index=True)combined_df = df# 移除旧表(为了覆盖写入)book.remove(book[sheet_name])else:combined_df = df# 写入合并后的数据combined_df.to_excel(writer, index=False, sheet_name=sheet_name)# 调整列宽worksheet = writer.sheets[sheet_name]for col in worksheet.columns:max_length = max(len(str(cell.value)) for cell in col)worksheet.column_dimensions[col[0].column_letter].width = max_length + 5for cell in col:cell.alignment = Alignment(horizontal='left')  # 左对else:# 新建模式with pd.ExcelWriter(file_path, engine='openpyxl') as writer:df.to_excel(writer, index=False, sheet_name=sheet_name)# 调整列宽worksheet = writer.sheets[sheet_name]for col in worksheet.columns:max_length = max(len(str(cell.value)) for cell in col)worksheet.column_dimensions[col[0].column_letter].width = max_length + 5for cell in col:cell.alignment = Alignment(horizontal='left')  # 左对print(f"数据已保存到:{file_path}")return [types.TextContent(type="text", text=f"数据已保存到:{file_path}")]

3.工具--保存到MySQL中

async  def Save_To_MySQL(data_str:str):# 把爬取的text数据保存到MySQL数据库中import pandas as pdfrom openpyxl import load_workbookimport osimport re# 示例数据准备(替换为你的实际数据)Small_type1 = '综合'# 按行拆分数据并转换为列表lines = [line.split() for line in data_str.strip().split('\n')]# 工作簿名称,去掉空格sheet_name = str(lines[0]).strip()# sheet_name = Large_type1 + '-' + Small_type1# sheet_name = re.sub(r'[^\u4e00-\u9fa5]',  '', sheet_name)header = lines[1]  # 假设第一行是表头rows = lines[2:]  # 数据行df = pd.DataFrame(rows, columns=header)'''数据插值,然后存入表格'''print(df.columns.tolist())  # 查看所有列名df['时间'] = pd.to_datetime(df['时间'], format='%Y/%m/%d')  # 确保日期为datetime类型# 2. 创建完整日期范围(从数据最早日期到最晚日期)date_range = pd.date_range(start=df['时间'].min(),end=df['时间'].max(),freq='D')# 3. 重新索引并保留原始数据df = df.set_index('时间').reindex(date_range).rename_axis('时间').reset_index()# 4. 定义需要插值的数值列(根据实际Excel列名调整)numeric_cols = ['本日', '昨日', '日环比', '上周', '周环比','上月度', '与上月比', '去年同期', '与去年比']# 5. 线性插值填充(取前后值的平均值)for col in numeric_cols:if col in df.columns:if col in ['日环比', '周环比', '与上月比', '与去年比']:df[col] = df[col].str.rstrip('%').astype(float) / 100# 2. 线性插值df[col] = df[col].interpolate(method='linear')# 3. 还原为百分比字符串df[col] = (df[col] * 100).round(2).astype(str) + '%'else:# 2. 线性插值,保留两位小数# df[col] = df[col].astype(float).interpolate(method='linear', limit_direction='both', limit=2)df[col] = pd.to_numeric(df[col], errors='coerce')  # 非数值转为NaNdf[col] = df[col].interpolate(method='linear').round(2)# 6. 格式化日期为YYYY/MM/DDdf['时间'] = df['时间'].dt.strftime('%Y/%m/%d')print("插值成功")df.insert(0, '类型', sheet_name)print("第一列插入类型成功")import pandas as pdfrom sqlalchemy import create_engine, text, Datefrom sqlalchemy.exc import SQLAlchemyError# MySQL连接配置(替换为你的实际配置)db_config = {'host': 'localhost','user': 'root','password': 'root111111','database': 'gyp_test','port': 3306}try:# 创建SQLAlchemy引擎engine = create_engine(f"mysql+pymysql://{db_config['user']}:{db_config['password']}@{db_config['host']}:{db_config['port']}/{db_config['database']}?charset=utf8mb4")# 检查表是否存在,不存在则创建with engine.connect() as conn:table_exists = conn.execute(text(f"SHOW TABLES LIKE '原材料指数表'")).fetchone()if not table_exists:create_table_sql = """CREATE TABLE `原材料指数表` (`id` INT AUTO_INCREMENT PRIMARY KEY,`类型` VARCHAR(50) NOT NULL,`时间` DATE NOT NULL,`本日` DECIMAL(10,2),`昨日` DECIMAL(10,2),`日环比` VARCHAR(20),`上周` DECIMAL(10,2),`周环比` VARCHAR(20),`上月度` DECIMAL(10,2),`与上月比` VARCHAR(20),`去年同期` DECIMAL(10,2),`与去年比` VARCHAR(20),INDEX (`时间`),INDEX (`类型`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""conn.execute(text(create_table_sql))print("表'原材料指数表'创建成功")# 将DataFrame写入数据库df.to_sql(name='原材料指数表',con=engine,if_exists='append',  # 追加模式index=False,dtype={'时间': Date  # 确保日期格式正确})print("数据插入成功")return [types.TextContent(type="text", text="数据插入MySQL数据库成功")]except SQLAlchemyError as e:print(f"数据库操作出错: {e}")except Exception as e:print(f"发生错误: {e}")finally:engine.dispose()

4.搭建MCP-server

from math import pi
@click.command()
@click.option("--port", default=8000, help="Port to listen on for SSE")
@click.option("--transport",type=click.Choice(["stdio", "sse"]),default="stdio",help="Transport type",
)
def main(port: int, transport: str) -> int:app = Server("mcp-website-fetcher")@app.call_tool()async def call_tool(name: str, arguments: dict) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:# if name != "fetch":#     raise ValueError(f"Unknown tool: {name}")# if "url" not in arguments:#     raise ValueError("Missing required argument 'url'")if name=="Web_crawling":return await fetch_website(arguments["data_type"], arguments["Start_Time"], arguments["End_Time"])elif name=="add_gyp":A = arguments["A"]B = arguments["B"]return [types.TextContent(type="text", text=str(A+B+pi))]elif name=="save_to_excel":return await Save_To_Excel(arguments["data_str"])elif name=="save_to_mysql":return await Save_To_MySQL(arguments["data_str"])#普钢:类型为综合,2025.1.1-2025.5.6的数据@app.list_tools()async def list_tools() -> list[types.Tool]:return [types.Tool(name="Web_crawling",description="抓取网页数据,需要输入抓取钢材的类型,开始时间,结束时间三个参数",inputSchema={"type": "object","required": ["data_type", "Start_Time", "End_Time"],"properties":{"data_type": {"type": "string", "description":"普钢['综合', '长材', '扁平', '一次材', '螺纹', '线材', '型材', '中厚', '锅炉容器板', '造船板', '热卷', '窄带', '冷板', '镀锌板卷', '无缝管', '聊城无缝钢管', '焊管', '盘扣式钢管脚手架'],""特钢['综合', '特钢'],""铁矿石['综合','进口矿','国产矿'],""焦炭['综合']"},"Start_Time": {"type": "string", "format": "date", "description": "开始时间(YYYY-MM-DD)"},"End_Time": {"type": "string", "format": "date", "description": "结束时间(YYYY-MM-DD)"},},},),types.Tool(name="add_gyp",description="这是一个计算器工具",inputSchema={"type": "object","required": ["A", "B"],"properties":{"A": {"type": "number", "description": "第一个数"},"B": {"type": "number", "description": "第二个数"},},},),types.Tool(name="save_to_excel",description="保存爬取的数据到本地Excel文档当中",inputSchema={"type": "object","required": ["data_str"],"properties":{"data_str": {"type": "string", "description": "爬取的数据"},},},),types.Tool(name="save_to_mysql",description="保存爬取的数据到MySQL数据库中",inputSchema={"type": "object","required": ["data_str"],"properties":{"data_str": {"type": "string", "description": "爬取的数据"},},},)]# @app.list_resources()# async def list_resources() -> list[types.Resource]:#     return [#         types.Resource(#             uri=FileUrl(r"D:\桌面文件\test-知识库.xlsx"),#             name='student_grade',#             description="这是一个成绩单",#             mimeType="text/plain",#         )#     ]# @app.read_resource()# async def read_resource(name: str,uri:FileUrl) -> str | bytes:#     if name=="student_grade":#         with open(uri.path, "rb") as f:#             return f.read()if transport == "sse":from mcp.server.sse import SseServerTransportfrom starlette.applications import Starlettefrom starlette.responses import Responsefrom starlette.routing import Mount, Routesse = SseServerTransport("/messages/")async def handle_sse(request):async with sse.connect_sse(request.scope, request.receive, request._send) as streams:await app.run(streams[0], streams[1], app.create_initialization_options())return Response()starlette_app = Starlette(debug=True,routes=[Route("/sse", endpoint=handle_sse, methods=["GET"]),Mount("/messages/", app=sse.handle_post_message),],)import uvicornuvicorn.run(starlette_app, host="0.0.0.0", port=port)else:from mcp.server.stdio import stdio_serverasync def arun():async with stdio_server() as streams:await app.run(streams[0], streams[1], app.create_initialization_options())anyio.run(arun)return 0

运行:uv run mcp_simple_tool --transport sse --port 8000

 

5.Dify调用MCP工具

你是一个智能助手,可根据用户输入的指令,进行推理并调用工具,完成任务后返回给用户结果。其中
server_name1为地图和天气服务,其中server_name2为搜索服务,server 3是·爬虫、保存数据服务。注意:1.必须使用mcp_sse_list_tools工具列出可以调用的工具。2.必须使用mcp_sse_call_tool调用合适的工具完成用户的需求。

 1.查询:普钢:类型为综合,2025.1.1-2025.5.6的数据

2.保存到本机数据库中,MySQL

完整代码:

import anyio
import click
import httpx
import mcp.types as types
from mcp.server.lowlevel import Server
import pandas as pd
import time
from pydantic import FileUrl#启动服务端:
# muv run mcp_simple_tool --transport sse --port 8000# async def fetch_website(
#     url: str,
# ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
#     headers = {
#         "User-Agent": "MCP Test Server (github.com/modelcontextprotocol/python-sdk)"
#     }
#     async with httpx.AsyncClient(follow_redirects=True, headers=headers) as client:
#         response = await client.get(url)
#         response.raise_for_status()
#         return [types.TextContent(type="text", text=response.text)]async def fetch_website(data_type: str,Start_Time: str,End_Time: str,
)-> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.edge.service import Servicefrom selenium.webdriver.edge.options import Optionsfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.action_chains import ActionChains# 设置Edge浏览器选项# try:# except Exception as e:#     print("Edge浏览器启动失败,请检查驱动是否正确安装!")#     print(e)all_data_dist = {"综合": "GANGCAIZONGHE","长材": "CHANGCAI","扁平": "BIANPING","一次材": "YICICAI","华东": "HUADONG","华南": "HUANAN","华北": "HUABEI","中南": "ZHONGNAN","东北": "DONGBEI","西南": "XINAN","西北": "XIBEI","螺纹": "LUOWEN","线材": "XIANCAI","型材": "XINCAI","中厚": "ZHONGHOU","锅炉容器板": "GUOLURONGQIBAN","造船板": "ZAOCHUANBAN","热卷": "REJUAN","窄带": "ZAIDAI","冷板": "LENGBAN","镀锌板卷": "DUXIN","无缝管": "WUFENGGUAN","聊城无缝钢管": "WUFENG_LIAOCHENG","焊管": "HANGUAN","盘扣式钢管脚手架": "PKSJSJ"}print("开始爬取数据")print("普钢的所有数据类型:" + str(all_data_dist.keys()))# Start_Time = input("请输入开始日期(格式:2024-01-01):") or "2024-04-01"Start_Time_year = Start_Time.split("-")[0]Start_Time_month = Start_Time.split("-")[1]Start_Time_day = Start_Time.split("-")[2]# day格式转换02需要去掉前导0Start_Time_day = str(int(Start_Time_day))print(f"开始日期:{Start_Time_year}-{Start_Time_month}-{Start_Time_day}")# End_Time = input("请输入结束日期(格式:2025-04-01):") or "2025-04-01"End_Time_year = End_Time.split("-")[0]End_Time_month = End_Time.split("-")[1]End_Time_day = End_Time.split("-")[2]# day格式转换02需要去掉前导0End_Time_day = str(int(End_Time_day))print(f"结束日期:{End_Time_year}-{End_Time_month}-{End_Time_day}")# data_type = input(#     "请输入需要爬取的数据类型:普钢['综合', '长材', '扁平', '一次材', '华东', '华南', '华北', '中南', '东北', '西南', '西北', '螺纹', '线材', '型材', '中厚', '锅炉容器板', '造船板', '热卷', '窄带', '冷板', '镀锌板卷', '无缝管', '聊城无缝钢管', '焊管', '盘扣式钢管脚手架']")data_type1 = all_data_dist[data_type]# 等待页面加载3s# time.sleep(3)# # element = driver.find_element(By.CLASS_NAME, "mRightBox")# # 等待页面加载3s-# time.sleep(3)edge_options = Options()edge_options.add_argument("--headless")  # 无头模式,不显示浏览器窗口edge_options.add_argument("--disable-gpu")edge_options.add_argument("--window-size=1920,1080")edge_service = Service('D:\桌面文件\edgedriver_win64\msedgedriver.exe')  # 替换为你的Edge驱动路径driver = webdriver.Edge(service=edge_service, options=edge_options)print("开始打开浏览器")url = "https://index.mysteel.com/xpic/detail.html?tabName=pugang"driver.get(url)time.sleep(3)driver.find_element(By.CSS_SELECTOR, "img.addBtn[src*='icon.png']").click()  # 点击展开print("点击展开")time.sleep(3)# print(element.text)try:# 点击类型key1 = driver.find_element(By.ID, data_type1)key1.click()# 等待页面加载3stime.sleep(1)# //*[@id="searchTimeLiDiv"]/ul/li[1]/a按日查询key2 = driver.find_element(By.XPATH, '//*[@id="searchTimeLiDiv"]/ul/li[1]/a')key2.click()time.sleep(1)# 起始日期//*[@id="startDay"]start_date = driver.find_element(By.XPATH, '//*[@id="startDay"]')# start_date.clear()start_date.click()time.sleep(1)driver.maximize_window()# # 解析年月日# target_date = "2021-09-01"# year, month, day = target_date.split('-')# # 等待日历面板加载# WebDriverWait(driver, 10).until(#     EC.presence_of_element_located((By.CSS_SELECTOR,  ".daterangepicker.dropdown-menu"))# )from selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import Select# 选择年份(如果页面有年份下拉框)year_dropdown = driver.find_element(By.XPATH, "/html/body/div[3]/div[2]/div/table/thead/tr[1]/th[2]/select[2]")year_dropdown.click()time.sleep(1)print("选择年份下拉框")# 选择年份<option value="1975">1975</option># year_dropdown.find_element(By.XPATH,  f"//option[@value='{year}']").click()# print(f"选择年份:{year}")select = Select(year_dropdown)select.select_by_visible_text(Start_Time_year)  # 根据文本选择print(f"选择年份:{Start_Time_year}")# 输出# 选择月份  /html/body/div[3]/div[2]/div/table/thead/tr[1]/th[2]/select[1]month_dropdown = driver.find_element(By.XPATH, "/html/body/div[3]/div[2]/div/table/thead/tr[1]/th[2]/select[1]")month_dropdown.click()time.sleep(1)print("选择月份下拉框")select1 = Select(month_dropdown)select1.select_by_visible_text(Start_Time_month)  # 根据文本选择print(f"选择月份:{Start_Time_month}")# 选择日期date_cell = driver.find_element(By.XPATH, f"//td[contains(@class, 'available') and text()='{Start_Time_day}']")date_cell.click()time.sleep(1)print(f"选择日期:{Start_Time_day}")except Exception as e:print(f"执行出错: {str(e)}")driver.save_screenshot('error.png')try:# //*[@id="searchTimeLiDiv"]/ul/li[1]/a按日查询key2 = driver.find_element(By.XPATH, '//*[@id="searchTimeLiDiv"]/ul/li[1]/a')key2.click()time.sleep(1)# 终止日期//*[@id="endDay"]end_date = driver.find_element(By.XPATH, '//*[@id="endDay"]')# start_date.clear()end_date.click()time.sleep(1)driver.maximize_window()# 解析年月日target_date = "2021-09-01"year, month, day = target_date.split('-')# # 等待日历面板加载# WebDriverWait(driver, 10).until(#     EC.presence_of_element_located((By.CSS_SELECTOR,  ".daterangepicker.dropdown-menu"))# )from selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import Select# 选择年份(如果页面有年份下拉框)year_end_dropdown = driver.find_element(By.XPATH,"/html/body/div[4]/div[2]/div/table/thead/tr[1]/th[2]/select[2]")year_end_dropdown.click()print("选择年份下拉框")# 选择年份<option value="1975">1975</option># year_dropdown.find_element(By.XPATH,  f"//option[@value='{year}']").click()# print(f"选择年份:{year}")select_year_end = Select(year_end_dropdown)select_year_end.select_by_visible_text(End_Time_year)  # 根据文本选择print(f"选择年份:{End_Time_year}")# 输出# 选择月份  /html/body/div[4]/div[2]/div/table/thead/tr[1]/th[2]/select[1]month_end_dropdown = driver.find_element(By.XPATH,"/html/body/div[4]/div[2]/div/table/thead/tr[1]/th[2]/select[1]")month_end_dropdown.click()print("选择月份下拉框")select_month_end = Select(month_end_dropdown)select1_month_end = select_month_end.select_by_visible_text(End_Time_month)  # 根据文本选择print(f"选择月份:{End_Time_month}")# /html/body/div[4]/div[2]# 找到右侧日历left_calendar = driver.find_element(By.XPATH,'/html/body/div[4]/div[2]')# 选择日期# left_calendar = driver.find_element(By.CLASS_SELECTOR, "calendar single left")# driver.find_element(By.XPATH, "//td[contains(@class, 'available') and text()='2']")date_end_cell = left_calendar.find_element(By.XPATH,f'.//td[text()={End_Time_day}]')date_end_cell.click()driver.save_screenshot('C:\pythonProject\python爬虫\我的钢铁网\end_date.png')print(f"选择日期:{End_Time_day}")except Exception as e:print(f"执行出错: {str(e)}")driver.save_screenshot('error.png')# 点击搜索按钮search_btn = driver.find_element(By.XPATH, '//*[@id="dome1"]/table/tbody/tr/td[5]/img')search_btn.click()# driver.save_screenshot('C:\pythonProject\python爬虫\我的钢铁网\搜索之后.png')element = driver.find_element(By.CLASS_NAME, "mRightBox")# 等待页面加载3s-time.sleep(3)#保存截图driver.save_screenshot('C:\pythonProject\项目五(钢铁价格预测)\我的钢铁网\搜索之后.png')print(element.text)# data_str = element.text# driver.quit()return [types.TextContent(type="text", text=element.text)]# return data_strasync def Save_To_Excel(data_str:str):# 把爬取的text数据保存到Excel文档中import pandas as pdfrom openpyxl import load_workbookimport osimport re# 示例数据准备(替换为你的实际数据)# 按行拆分数据并转换为列表lines = [line.split() for line in data_str.strip().split('\n')]# 工作簿名称,去掉空格sheet_name = str(lines[0]).strip()sheet_name =  sheet_namesheet_name = re.sub(r'[^\u4e00-\u9fa5]', '', sheet_name)header = lines[1]  # 假设第一行是表头rows = lines[2:]  # 数据行df = pd.DataFrame(rows, columns=header)'''数据插值,然后存入表格'''print(df.columns.tolist())  # 查看所有列名df['时间'] = pd.to_datetime(df['时间'], format='%Y/%m/%d')  # 确保日期为datetime类型# 2. 创建完整日期范围(从数据最早日期到最晚日期)date_range = pd.date_range(start=df['时间'].min(),end=df['时间'].max(),freq='D')# 3. 重新索引并保留原始数据df = df.set_index('时间').reindex(date_range).rename_axis('时间').reset_index()# 4. 定义需要插值的数值列(根据实际Excel列名调整)numeric_cols = ['本日', '昨日', '日环比', '上周', '周环比','上月度', '与上月比', '去年同期', '与去年比']# 5. 线性插值填充(取前后值的平均值)for col in numeric_cols:if col in df.columns:if col in ['日环比', '周环比', '与上月比', '与去年比']:df[col] = df[col].str.rstrip('%').astype(float) / 100# 2. 线性插值df[col] = df[col].interpolate(method='linear')# 3. 还原为百分比字符串df[col] = (df[col] * 100).round(2).astype(str) + '%'else:# 2. 线性插值,保留两位小数# df[col] = df[col].astype(float).interpolate(method='linear', limit_direction='both', limit=2)df[col] = pd.to_numeric(df[col], errors='coerce')  # 非数值转为NaNdf[col] = df[col].interpolate(method='linear').round(2)# 6. 格式化日期为YYYY/MM/DDdf['时间'] = df['时间'].dt.strftime('%Y/%m/%d')from openpyxl.styles import Alignment# 目标文件路径(注意Windows路径要用双反斜杠或原始字符串)file_path = r"C:\pythonProject\项目五(钢铁价格预测)\我的钢铁网\gyp4.xlsx"# 核心逻辑:追加或新建if os.path.exists(file_path):# 追加模式(加载现有工作簿)with pd.ExcelWriter(file_path, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:book = load_workbook(file_path)if sheet_name in book.sheetnames:# # 读取现有数据并合并# existing_df = pd.read_excel(file_path,  sheet_name=sheet_name)# combined_df = pd.concat([existing_df,  new_df], ignore_index=True)combined_df = df# 移除旧表(为了覆盖写入)book.remove(book[sheet_name])else:combined_df = df# 写入合并后的数据combined_df.to_excel(writer, index=False, sheet_name=sheet_name)# 调整列宽worksheet = writer.sheets[sheet_name]for col in worksheet.columns:max_length = max(len(str(cell.value)) for cell in col)worksheet.column_dimensions[col[0].column_letter].width = max_length + 5for cell in col:cell.alignment = Alignment(horizontal='left')  # 左对else:# 新建模式with pd.ExcelWriter(file_path, engine='openpyxl') as writer:df.to_excel(writer, index=False, sheet_name=sheet_name)# 调整列宽worksheet = writer.sheets[sheet_name]for col in worksheet.columns:max_length = max(len(str(cell.value)) for cell in col)worksheet.column_dimensions[col[0].column_letter].width = max_length + 5for cell in col:cell.alignment = Alignment(horizontal='left')  # 左对print(f"数据已保存到:{file_path}")return [types.TextContent(type="text", text=f"数据已保存到:{file_path}")]async  def Save_To_MySQL(data_str:str):# 把爬取的text数据保存到MySQL数据库中import pandas as pdfrom openpyxl import load_workbookimport osimport re# 示例数据准备(替换为你的实际数据)Small_type1 = '综合'# 按行拆分数据并转换为列表lines = [line.split() for line in data_str.strip().split('\n')]# 工作簿名称,去掉空格sheet_name = str(lines[0]).strip()# sheet_name = Large_type1 + '-' + Small_type1# sheet_name = re.sub(r'[^\u4e00-\u9fa5]',  '', sheet_name)header = lines[1]  # 假设第一行是表头rows = lines[2:]  # 数据行df = pd.DataFrame(rows, columns=header)'''数据插值,然后存入表格'''print(df.columns.tolist())  # 查看所有列名df['时间'] = pd.to_datetime(df['时间'], format='%Y/%m/%d')  # 确保日期为datetime类型# 2. 创建完整日期范围(从数据最早日期到最晚日期)date_range = pd.date_range(start=df['时间'].min(),end=df['时间'].max(),freq='D')# 3. 重新索引并保留原始数据df = df.set_index('时间').reindex(date_range).rename_axis('时间').reset_index()# 4. 定义需要插值的数值列(根据实际Excel列名调整)numeric_cols = ['本日', '昨日', '日环比', '上周', '周环比','上月度', '与上月比', '去年同期', '与去年比']# 5. 线性插值填充(取前后值的平均值)for col in numeric_cols:if col in df.columns:if col in ['日环比', '周环比', '与上月比', '与去年比']:df[col] = df[col].str.rstrip('%').astype(float) / 100# 2. 线性插值df[col] = df[col].interpolate(method='linear')# 3. 还原为百分比字符串df[col] = (df[col] * 100).round(2).astype(str) + '%'else:# 2. 线性插值,保留两位小数# df[col] = df[col].astype(float).interpolate(method='linear', limit_direction='both', limit=2)df[col] = pd.to_numeric(df[col], errors='coerce')  # 非数值转为NaNdf[col] = df[col].interpolate(method='linear').round(2)# 6. 格式化日期为YYYY/MM/DDdf['时间'] = df['时间'].dt.strftime('%Y/%m/%d')print("插值成功")df.insert(0, '类型', sheet_name)print("第一列插入类型成功")import pandas as pdfrom sqlalchemy import create_engine, text, Datefrom sqlalchemy.exc import SQLAlchemyError# MySQL连接配置(替换为你的实际配置)db_config = {'host': 'localhost','user': 'root','password': 'root111111','database': 'gyp_test','port': 3306}try:# 创建SQLAlchemy引擎engine = create_engine(f"mysql+pymysql://{db_config['user']}:{db_config['password']}@{db_config['host']}:{db_config['port']}/{db_config['database']}?charset=utf8mb4")# 检查表是否存在,不存在则创建with engine.connect() as conn:table_exists = conn.execute(text(f"SHOW TABLES LIKE '原材料指数表'")).fetchone()if not table_exists:create_table_sql = """CREATE TABLE `原材料指数表` (`id` INT AUTO_INCREMENT PRIMARY KEY,`类型` VARCHAR(50) NOT NULL,`时间` DATE NOT NULL,`本日` DECIMAL(10,2),`昨日` DECIMAL(10,2),`日环比` VARCHAR(20),`上周` DECIMAL(10,2),`周环比` VARCHAR(20),`上月度` DECIMAL(10,2),`与上月比` VARCHAR(20),`去年同期` DECIMAL(10,2),`与去年比` VARCHAR(20),INDEX (`时间`),INDEX (`类型`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"""conn.execute(text(create_table_sql))print("表'原材料指数表'创建成功")# 将DataFrame写入数据库df.to_sql(name='原材料指数表',con=engine,if_exists='append',  # 追加模式index=False,dtype={'时间': Date  # 确保日期格式正确})print("数据插入成功")return [types.TextContent(type="text", text="数据插入MySQL数据库成功")]except SQLAlchemyError as e:print(f"数据库操作出错: {e}")except Exception as e:print(f"发生错误: {e}")finally:engine.dispose()from math import pi
@click.command()
@click.option("--port", default=8000, help="Port to listen on for SSE")
@click.option("--transport",type=click.Choice(["stdio", "sse"]),default="stdio",help="Transport type",
)
def main(port: int, transport: str) -> int:app = Server("mcp-website-fetcher")@app.call_tool()async def call_tool(name: str, arguments: dict) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:# if name != "fetch":#     raise ValueError(f"Unknown tool: {name}")# if "url" not in arguments:#     raise ValueError("Missing required argument 'url'")if name=="Web_crawling":return await fetch_website(arguments["data_type"], arguments["Start_Time"], arguments["End_Time"])elif name=="add_gyp":A = arguments["A"]B = arguments["B"]return [types.TextContent(type="text", text=str(A+B+pi))]elif name=="save_to_excel":return await Save_To_Excel(arguments["data_str"])elif name=="save_to_mysql":return await Save_To_MySQL(arguments["data_str"])#普钢:类型为综合,2025.1.1-2025.5.6的数据@app.list_tools()async def list_tools() -> list[types.Tool]:return [types.Tool(name="Web_crawling",description="抓取网页数据,需要输入抓取钢材的类型,开始时间,结束时间三个参数",inputSchema={"type": "object","required": ["data_type", "Start_Time", "End_Time"],"properties":{"data_type": {"type": "string", "description":"普钢['综合', '长材', '扁平', '一次材', '螺纹', '线材', '型材', '中厚', '锅炉容器板', '造船板', '热卷', '窄带', '冷板', '镀锌板卷', '无缝管', '聊城无缝钢管', '焊管', '盘扣式钢管脚手架'],""特钢['综合', '特钢'],""铁矿石['综合','进口矿','国产矿'],""焦炭['综合']"},"Start_Time": {"type": "string", "format": "date", "description": "开始时间(YYYY-MM-DD)"},"End_Time": {"type": "string", "format": "date", "description": "结束时间(YYYY-MM-DD)"},},},),types.Tool(name="add_gyp",description="这是一个计算器工具",inputSchema={"type": "object","required": ["A", "B"],"properties":{"A": {"type": "number", "description": "第一个数"},"B": {"type": "number", "description": "第二个数"},},},),types.Tool(name="save_to_excel",description="保存爬取的数据到本地Excel文档当中",inputSchema={"type": "object","required": ["data_str"],"properties":{"data_str": {"type": "string", "description": "爬取的数据"},},},),types.Tool(name="save_to_mysql",description="保存爬取的数据到MySQL数据库中",inputSchema={"type": "object","required": ["data_str"],"properties":{"data_str": {"type": "string", "description": "爬取的数据"},},},)]# @app.list_resources()# async def list_resources() -> list[types.Resource]:#     return [#         types.Resource(#             uri=FileUrl(r"D:\桌面文件\test-知识库.xlsx"),#             name='student_grade',#             description="这是一个成绩单",#             mimeType="text/plain",#         )#     ]# @app.read_resource()# async def read_resource(name: str,uri:FileUrl) -> str | bytes:#     if name=="student_grade":#         with open(uri.path, "rb") as f:#             return f.read()if transport == "sse":from mcp.server.sse import SseServerTransportfrom starlette.applications import Starlettefrom starlette.responses import Responsefrom starlette.routing import Mount, Routesse = SseServerTransport("/messages/")async def handle_sse(request):async with sse.connect_sse(request.scope, request.receive, request._send) as streams:await app.run(streams[0], streams[1], app.create_initialization_options())return Response()starlette_app = Starlette(debug=True,routes=[Route("/sse", endpoint=handle_sse, methods=["GET"]),Mount("/messages/", app=sse.handle_post_message),],)import uvicornuvicorn.run(starlette_app, host="0.0.0.0", port=port)else:from mcp.server.stdio import stdio_serverasync def arun():async with stdio_server() as streams:await app.run(streams[0], streams[1], app.create_initialization_options())anyio.run(arun)return 0

相关文章:

【人工智能-agent】--Dify中MCP工具存数据到MySQL

本文记录的工作如下&#xff1a; 自定义MCP工具&#xff0c;爬取我的钢铁网数据爬取的数据插值处理自定义MCP工具&#xff0c;把爬取到的数据&#xff08;str&#xff09;存入本地excel表格中自定义MCP工具&#xff0c;把爬取到的数据&#xff08;str&#xff09;存入本地MySQ…...

ctfshow——web入门351~356

SSRF没有出网的部分 web入门351 $ch curl_init($url); 作用&#xff1a;初始化一个 cURL 会话&#xff0c;并设置目标 URL。解释&#xff1a; curl_init($url) 创建一个新的 cURL 资源&#xff0c;并将其与 $url 关联。这里的 $url 是用户提供的&#xff0c;因此目标地址完全…...

堆复习(C语言版)

目录 1.树的相关概念&#xff1a; 2.堆的实现 3.TopK问题 4.总结 1.树的相关概念&#xff1a; 1.结点的度&#xff1a;一个结点含有的子树&#xff08;孩子&#xff09;个数。 A的度为6 2.叶结点or终端结点&#xff1a;度为0的结点。 J、K、L、H、I 都是叶子结点 3.非终端结…...

解决LangChain4j报错HTTP/1.1 header parser received no bytes

问题描述 当使用langchain4j-open-ai调用自己部署的大模型服务时报错&#xff1a; public static void main(String[] args) {OpenAiChatModel model OpenAiChatModel.builder().apiKey("none").modelName("qwen2.5-instruct").baseUrl("http://19…...

深入解析MySQL联合查询(UNION):案例与实战技巧

在数据库操作中&#xff0c;查询是最常用的操作之一。MySQL提供了强大的查询功能&#xff0c;联合查询&#xff08;UNION&#xff09;是其中非常有用的一项操作。联合查询可以将多个查询结果合并成一个结果集&#xff0c;使得从不同来源的数据整合变得更加简单高效。本文将详细…...

[计算机科学#14]:数据结构

【核知坊】&#xff1a;释放青春想象&#xff0c;码动全新视野。 我们希望使用精简的信息传达知识的骨架&#xff0c;启发创造者开启创造之路&#xff01;&#xff01;&#xff01; 内容摘要&#xff1a;数据结构是计算机科学中的核心概念&#xff0c;用于…...

【计算机网络】HTTP 协议

HTTP是什么&#xff1f; HTTP 全称是“超文本传输协议”&#xff0c;是互联网上应用最广泛的应用层协议&#xff0c;用于客户端和服务器之间的通信。 HTTP 的实现在 HTTP 3.0之前都是基于传输层的 TCP 实现的&#xff0c; HTTP 3.0 改为了基于 UDP 实现&#xff0c;但是现在市…...

原生的 XMLHttpRequest 和基于 jQuery 的 $.ajax 方法的异同之处以及使用场景

近期参与一个项目的开发&#xff0c;发现项目中的ajax请求有两种不同的写法&#xff0c;查询了下两种写法的异同之处以及使用场景。 下面将从以下两段简单代码进行异同之处的分析及使用场景的介绍&#xff1a; // 写法一&#xff1a; var xhr new XMLHttpRequest(); xhr.open…...

横向移动(上)

横向移动&#xff08;上&#xff09; 横向移动指的是攻击者在内网中获得初始访问权限之后&#xff0c;通过相关技术扩大敏感数据和高价值资产权限的行为 常见的横向移动的方式 1.通过web漏洞 2.通过远程桌面 3.通过账号密码 4.通过不安全的配置 5.通过系统漏洞 利用远控…...

关于 js:7. 模块化、构建与工具链

一、模块系统&#xff1a;CommonJS、ESM、UMD 模块系统的目标&#xff1a; 将代码拆分为独立的逻辑单元&#xff08;模块&#xff09;&#xff0c;实现封装、复用、依赖管理。 在 Web 前端/Node 中&#xff0c;因为 JavaScript 起初没有模块机制&#xff0c;因此出现了多个模…...

一次IPA被破解后的教训(附Ipa Guard等混淆工具实测)

一行代码的疏忽&#xff0c;一个默认的类名&#xff0c;一个未混淆的资源路径&#xff0c;都可能成为攻击者入侵的入口。 背景&#xff1a;一次“不值一提”的上线&#xff0c;成了代价惨重的经验 故事的起点很简单&#xff1a;我们给销售部门做了一款小型内部演示 App&#x…...

麒麟系统安装.net core环境变量

本文主要记录在麒麟系统上安装.net core的运行环境&#xff0c;这里使用的是麒麟V10桌面版&#xff0c;后续测试服务器到了之后再使用服务器版进行安装测试。 环境安装 下载 这里由于是桌面版&#xff0c;我直接使用浏览器下的包&#xff0c;下完之后在终端中安装。 安装 1…...

如何使用 React Hooks 替代类组件的生命周期方法?

文章目录 1. 引言2. useEffect 概述3. 模拟类组件的生命周期方法3.1 模拟 componentDidMount3.2 模拟 componentDidUpdate3.3 模拟 componentWillUnmount 4. 多个 useEffect 的使用5. 注意事项6. 总结 1. 引言 在 React 16.8 版本之前&#xff0c;开发者主要通过类组件&#x…...

windows 在安装 Ubuntu-20.04 显示操作超时解决办法

1. 问题概述与原因分析 在我们用下面命令安装 Ubuntu-20.04 时系统显示操作超时&#xff1a; wsl --install -d Ubuntu-20.04大概率是没打开 Windows 虚拟机监控程序平台&#xff0c;可以在控制面板–>程序和功能里面打开 2. 解决办法与步骤 解决方式如下&#xff1a; 我…...

Spring Boot中Redis序列化配置详解

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 引言 在使用Spring Boot集成Redis时&#xff0c;序列化方式的选择直接影响数据存储的效率和系统兼容性。默认的JDK序列化存在可读性差、存储空间大等问题&am…...

OpenCV进阶操作:光流估计

文章目录 前言一、光流估计1、光流估计是什么&#xff1f;2、光流估计的前提&#xff1f;1&#xff09;亮度恒定2&#xff09;小运动3&#xff09;空间一致 3、OpenCV中的经典光流算法1&#xff09;Lucas-Kanade方法&#xff08;稀疏光流&#xff09;2&#xff09; Farneback方…...

2025年渗透测试面试题总结-渗透测试红队面试八(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 渗透测试红队面试八 二百一十一、常见中间件解析漏洞利用方式 二百一十二、MySQL用户密码存储与加密 …...

前端面试高频50个问题,解答

以下是前端面试中常见的50个高频问题及其简要解答&#xff1a; HTML HTML5 有哪些新特性&#xff1f; 语义化标签&#xff08;如 <header>、<footer>&#xff09;、多媒体支持&#xff08;如 <audio>、<video>&#xff09;、本地存储&#xff08;如 l…...

Elasticsearch架构原理

1、Elasticsearch的节点类型 1.1 Master节点 在Elasticsearch启动时&#xff0c;会选举出来一个Master节点。当某个节点启动后&#xff0c;然后 使用Zen Discovery机制找到集群中的其他节点&#xff0c;并建立连接。 discovery.seed_hosts: ["192.168.21.130", &qu…...

前端面试宝典---webpack面试题

webpack 的 tree shaking 的原理 Webpack 的 Tree Shaking 过程主要包含以下步骤&#xff1a; 模块依赖分析&#xff1a;Webpack 首先构建一个完整的模块依赖图&#xff0c;确定每个模块之间的依赖关系。导出值分析&#xff1a;通过分析模块之间的 import 和 export&#xff…...

Vue 2 项目中配置 Tailwind CSS 和 Font Awesome 的最佳实践

Vue 2 项目中配置 Tailwind CSS 和 Font Awesome 的最佳实践 一、Tailwind CSS 配置 1. 安装依赖 npm install tailwindcssnpm:tailwindcss/postcss7-compat tailwindcss/postcss7-compat postcss^7 autoprefixer^92. 创建配置文件 npx tailwindcss init3. 创建样式文件 在…...

hiveserver2与beeline进行远程连接hive配置及遇到的问题

1、hiveserver2 参与用户模拟功能&#xff0c;因为开启后才能保证各用户之间的权限隔离。 1.1、配置 $HADOOP_HOME/etc/hadoop/core-site.xml <!--配置所有节点的root用户都可作为代理用户--> <property><name>hadoop.proxyuser.root.hosts</name>&…...

单词短语0512

当然可以&#xff0c;下面是“opportunity”在考研英语中的常用意思和高频短语&#xff0c;采用大字体展示&#xff0c;便于记忆&#xff1a; ✅ opportunity 的考研常用意思&#xff1a; &#x1f449; 机会&#xff0c;良机 表示有利的时机或条件&#xff0c;尤指成功的可能…...

c++刷题便捷函数(类似于stoi的小函数)

标题 stoi(字符串转整形)map和set都有count成员函数&#xff0c;返回值是该key的个数&#xff0c;可以用来查是否存在该元素。bool is_sorted(nums.begin(), nums.end() 检验是否有序INT_MAX,INT_MIN分别是整形最大和最小初始化二维矩阵 vector<vector\<int>> mart…...

想实现一个基于MCP的pptx生成系统架构图【初版实现】

技术栈:Python + MCP协议 + python-pptx + FastMCP 核心创新点:通过MCP协议实现PPTX元素的动态化生成与标准化模板管理 当前还是个半成品,后续持续更新。 主要先介绍一下思路。 一、MCP协议与系统设计原理 1.1 为什么选择MCP? 标准化工具调用:通过MCP将PPTX元素生成逻辑封…...

jwt学习

基于token的鉴权机制也是无状态的(类似于http协议)&#xff0c;不需要保在服务端保留用户的认证或会话信息。 构成 jwt由三部分构成&#xff1a;头部、payload、签名&#xff0c;中间用.隔开 头部(header) 包含两部分信息&#xff1a;声明类型、声明加密的算法 例如&#xff1a…...

pth的模型格式怎么变成SafeTensors了?

文章目录 背景传统模型格式的安全隐患效率与资源瓶颈跨框架兼容性限制Hugging Face 的解决方案&#xff1a;SafeTensors行业与社区的推动SafeTensors 的意义总结 背景 最近要找一些适合embedding的模型&#xff0c;在huggingface模型库上看到一些排名比较靠前的&#xff0c;准…...

如何判断IP是否被平台标记

一、基础检测&#xff1a;连通性与黑名单筛查 网络连通性测试 Ping与Traceroute&#xff1a;通过命令测试延迟和路由路径&#xff0c;若延迟>50ms或存在异常节点&#xff08;如某跳延迟>200ms&#xff09;&#xff0c;可能影响可用性。示例命令&#xff1a; bash ping 8.…...

【c++】异常详解

目录 C语言处理错误的局限性异常的定义异常的具体使用细则异常的抛出与捕获在函数调用链中异常栈展开匹配原则异常的重新抛出异常规范throw(类型)noexcept 成熟的异常体系c自己的异常体系异常的优缺点优点缺点 异常安全 C语言处理错误的局限性 C语言处理错误常常会用到assert和…...

从模型加密到授权交付,CodeMeter赋能3D打印商业化全流程

引言 在数字化制造快速演进的当下&#xff0c;3D 打印&#xff08;增材制造&#xff09;作为具备高度灵活性与创新潜力的制造方式&#xff0c;正重塑备件供应链与产品生命周期管理。然而&#xff0c;随着应用场景不断扩展&#xff0c;企业面临的知识产权保护、数字资产商业化与…...

ESP32开发之freeRTOS的事件组

什么是事件组事件组的应用场景事件组的API函数事件组应用举例总结什么是事件组 概念:事件组就是一个整数,高8位给内核使用,其他位用来表示事件。在ESP32的IDF freeRTOS中,这个整数是32位的,低24位用来供事件组使用。 举一个生活中的例子: 你在等快递,有三个包裹来自不…...

K8S中构建双架构镜像-从零到成功

背景介绍 公司一个客户的项目使用的全信创的环境&#xff0c;服务器采用arm64的机器&#xff0c;而我们的应用全部是amd64的&#xff0c;于是需要对现在公司流水线进行arm64版本的同步镜像生成。本文介绍从最开始到最终生成双架构的全部过程&#xff0c;以及其中使用的相关配置…...

腾讯怎样基于DeepSeek搭建企业应用?怎样私有化部署满血版DS?直播:腾讯云X DeepSeek!

2025新春&#xff0c;DeepSeek横空出世&#xff0c;震撼全球&#xff01; 通过算法优化&#xff0c;DeepSeek将训练与推理成本降低至国际同类模型的1/10&#xff0c;极大的降低了AI应用开发的门槛。 可以预见&#xff0c;2025年&#xff0c;是AI应用落地爆发之年&#xff01; ✔…...

【论信息系统项目的质量管理】

论信息系统项目的质量管理 前言一、抓好质量管理规划工作&#xff0c;为质量管理和确认提供指南和方向。二、做好管理质量相关工作&#xff0c;促进质量过程改进。三、抓好控制质量&#xff0c;确保实现质量目标四、综合协调质量与成本、进度、范围的关系总结 前言 为解决日常出…...

SpringAI框架中的RAG模块详解及应用示例

SpringAI框架中的RAG模块详解及应用示例 RAG&#xff08;Retrieval-Augmented Generation&#xff09;可以通过检索知识库&#xff0c;克服大模型训练完成后参数冻结的局限性&#xff0c;携带知识让大模型根据知识进行回答。SpringAI框架提供了模块化的API来支持RAG&#xff0…...

图像增强技术

一、目的 通过本实验加深对数字图像增强操作的理解&#xff0c;熟悉MATLAB中的有关函数&#xff1b;了解直方图均衡化和卷积滤波的原理&#xff1b;熟悉低通和高通滤波模板的构造方法。 二、实验内容与设计思想 1、观察实验结果可看出&#xff0c; 原图像 I的对比度较低&…...

【Java学习笔记】多态参数

多态参数 应用&#xff1a;方法定义的形参类型为父类类型&#xff0c;实参允许为子类类型 // 父类 package polyparemeter;public class employee {private String name;private double salary;//构造器public employee(){}public employee(String name, double salary) {thi…...

计算机网络核心技术解析:从基础架构到应用实践

计算机网络作为现代信息社会的基石&#xff0c;承载着全球数据交换与资源共享的核心功能。本文将从网络基础架构、核心协议、分层模型到实际应用场景&#xff0c;全面解析计算机网络的核心技术&#xff0c;并结合行业最新趋势&#xff0c;为读者构建系统的知识体系。 一、计算机…...

LiveData:Android响应式编程的核心利器

LiveData是一种可观察的数据持有类,用于在Android应用中实现数据的响应式编程。它具有以下特点和作用: 特点 生命周期感知:LiveData能够感知与其关联的组件(如Activity、Fragment)的生命周期状态。只有当组件处于活跃状态(如Activity处于RESUMED状态)时,LiveData才会将…...

【LeeCode】1.两数之和

文章目录 1. 暴力求解2. 哈希表具体过程1. nums [2, 7, 11, 15]&#xff0c;target 9&#xff1a;2. nums [11, 15, 2, 7]&#xff0c; target 9 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff…...

继承关系下创建对象的具体流程

public class Person {int x initX(); // 显式初始化&#xff1a;调用方法 initX()public Person() {System.out.println("Parent 构造器执行, x " x);}int initX() {System.out.println("initX() 被调用了");return 100;} }public class Child extends…...

基于世界土壤数据库(HWSD)的中国土壤数据集(v1.1)(2009)

时间分辨率&#xff1a;年共享方式&#xff1a;开放获取数据大小&#xff1a;156.47 MB数据时间范围&#xff1a;2009元数据更新时间&#xff1a;2020-03-26 数据集摘要 数据来源于联合国粮农组织&#xff08;FAO&#xff09;和维也纳国际应用系统研究所(IIASA)所构建的世界土…...

mac M2能安装的虚拟机和linux系统系统

目前网上的资料大多错误&#xff0c;能支持M2的很少。 推荐安装的改造过的centos7也无法进行yum操作&#xff0c;建议安装centos8 VMware Fusion下载地址&#xff1a; https://pan.baidu.com/s/14v3Dy83nuLr2xOy_qf0Jvw 提取码: jri4 centos8下载地址&#xff1a; https://…...

212. 单词搜索 II【 力扣(LeetCode) 】

文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 212. 单词搜索 II 一、题目描述 给定一个 m x n 二维字符网格 board 和一个单词&#xff08;字符串&#xff09;列表 words&#xff0c; 返回所有二维网格上的单词 。 单词必须按照字母…...

【软考-高级】【信息系统项目管理师】论文写作注意事项及2014年至2024年历年论文题目汇总

论文写作注意事项 要求 字数要求&#xff1a;2500字以内&#xff08;2024年超过2500字&#xff0c;在线答题系统无法输入&#xff09;时长要求&#xff1a;2小时&#xff08;大多数人不够用&#xff09;内容要求&#xff1a; 必须响应子标题&#xff0c;如子标题要求写如何优…...

MySQL数据库表的约束

目录 1.null属性 2.默认值约束&#xff08;default&#xff09; 3.comment 4.zerofill 5.主键&#xff08;primary key&#xff09; 6.自增长&#xff08;auto_increment&#xff09; 7.唯一键&#xff08;unique&#xff09; ​编辑 8.外键 约束是为了安全插入数据&a…...

硅基计划2.0 学习总结 壹 Java初阶

一、初见Java &#xff08;1&#xff09;Java简介 首先不得不承认Java是一门优秀的程序设计语言 其系列的计算机软件和跨平台体系包括国内的生态链完善是C/C语言难以弥补的 &#xff08;2&#xff09;Java SE 全称Java Standard Edition&#xff0c;是Java体系的基础 &am…...

逆向破解:x64dbg

文章目录 一、CPU窗口1、反汇编窗口2、寄存器窗口3、栈地址窗口4、十六进制数据窗口5、堆栈参数解析窗口 二、常用快捷键三、字符串检索功能四、调试功能1、上一步 一、CPU窗口 1、反汇编窗口 2、寄存器窗口 寄存器窗口用于显示和解释当前线程环境下CPU寄存器的各种状态值和内…...

从MCU到SoC的开发思维转变

目录 1、硬件设计 2、软件开发 3、调试与测试 4、电源管理 微控制器单元&#xff08;MCU&#xff09;和系统级芯片&#xff08;SoC&#xff09;是嵌入式开发中最常见的两种处理器类型。MCU以其简单、低功耗的特点&#xff0c;广泛应用于特定控制任务&#xff1b;而SoC凭借强…...

3DGS-to-PC:3DGS模型一键丝滑转 点云 or Mesh 【Ubuntu 20.04】【2025最新版!!】

一、引言 3D高斯泼溅(3DGS)是一种新兴的三维场景表示方法&#xff0c;可以生成高质量的场景重建结果。然而&#xff0c;要查看这些重建场景&#xff0c;需要特殊的高斯渲染器。大多数3D处理软件并不兼容3D高斯分布模型&#xff0c;但它们通常都兼容点云文件。 3DGS-to-PC项目提…...