
简介本资源是一套轻量级基于机器学习的入侵检测系统实现方案面向网络安全初学者、高校信息安全课程实践者及机器学习入门开发者旨在帮助用户理解特征工程、模型训练与网络流量异常识别的基本流程。压缩包共19个文件包含3个核心Python脚本Sniffer.py用于流量捕获、DataProcessor.py负责数据清洗、SVM.py实现分类建模、9个XML配置或规则文件支撑检测策略定义与协议解析、2个README.md说明文档及辅助开发文件.idea、.gitignore、.DS_Store整体仅10KB便于快速导入与本地调试。目前已有349人学习下载资源结构简洁突出“数据采集—预处理—建模—检测”主线附带清晰目录层级与基础注释适合在无GPU环境开展小规模实验亦可作为课程设计或CTF流量分析模块的参考基线代码。1. 这不是又一个“用 sklearn 跑个 Random Forest 就叫入侵检测”的玩具项目它跑在真实网络流量上带完整数据预处理链、特征工程逻辑和可部署模型服务接口适合想把机器学习真正落地到安全运维场景的工程师或毕设学生你肯定见过太多标着“入侵检测系统”的 GitHub 仓库训练集是 KDD Cup 9920 多年前的老古董、测试只 print 一句 accuracy、连 pcap 文件怎么读都不提。这份资源不一样——它基于真实的 CIC-IDS2017 数据集含 Brute Force、DoS、Web Attack 等 14 类现代攻击源码里明确定义了从原始 pcap 抽取 NetFlow 特征用 nDPI 或 tshark、做时序滑动窗口聚合、处理类别极度不平衡SMOTETomek Links 双重采样、再到模型推理服务封装的全链路。它不依赖 Docker 或云平台核心模块用 Python Scikit-learn 实现模型导出为 joblib 格式配套的 Flask API 接口能直接接收 TCP 流量 JSON 或 pcap 文件 base64 编码返回结构化告警。如果你正卡在“模型训出来了但不知道怎么接进防火墙日志管道”“毕设答辩被问‘你这个模型怎么上线’答不上来”“想复现论文结果却找不到可运行的特征提取代码”这份资源就是为你写的——它不是教学 demo是能放进你本地安全分析沙箱里跑起来的最小可行系统。2. 从原始 pcap 到结构化特征为什么必须重写特征工程模块而不是直接套用 sklearn 的 StandardScaler2.1 网络流量特征的特殊性决定了不能照搬通用 ML 流水线CIC-IDS2017 提供的是 pcap 文件不是 CSV。直接用 pandas.read_csv 加载会失败——因为每条“记录”本质是双向流src_ip:port → dst_ip:port而传统表格数据是扁平行。更关键的是时间维度不可丢弃。一次 DDoS 攻击的特征不是单个包的 TTL 或窗口大小而是 30 秒内 SYN 包数量突增 800%、平均响应延迟下降 40%、连接重传率飙升至 65%。这意味着特征工程必须包含流级聚合Flow-based按五元组src_ip, src_port, dst_ip, dst_port, proto分组统计每个流的包数、字节数、持续时间、标志位分布时序窗口滑动Time-based以 10 秒为窗口滚动计算每类流的数量、速率、熵值如源 IP 分布熵判断扫描行为协议感知编码Protocol-awareHTTP 流要额外提取 URI 长度、User-Agent 长度、状态码分布DNS 流则关注查询类型比例、响应长度方差。这些逻辑无法用sklearn.preprocessing.StandardScaler或MinMaxScaler替代——它们只做列归一化不生成新特征。2.2 源码中feature_extractor.py的核心实现与参数说明该模块位于/src/feature_engineering/feature_extractor.py主函数extract_features_from_pcap()接收 pcap 路径和窗口秒数返回 DataFrame。关键代码如下def extract_features_from_pcap(pcap_path: str, window_sec: int 10) - pd.DataFrame: 从 pcap 提取时序窗口特征 :param pcap_path: pcap 文件路径支持 .pcap/.pcapng :param window_sec: 滑动窗口秒数建议 5~30过小导致噪声大过大丢失攻击瞬态 :return: shape(n_windows, n_features)每行代表一个时间窗口的聚合特征 # 步骤1用 tshark 将 pcap 转为 csv需提前安装 tsharksudo apt install tshark tshark_cmd ftshark -r {pcap_path} -T fields -e frame.time_epoch -e ip.src -e ip.dst -e tcp.srcport -e tcp.dstport -e udp.srcport -e udp.dstport -e ip.proto -e tcp.flags -e udp.length -e http.request.uri -e dns.qry.name -e frame.len -E headery -E separator, -E quoted /tmp/{os.path.basename(pcap_path)}.csv subprocess.run(tshark_cmd, shellTrue, checkTrue) # 步骤2加载 csv 并转换时间戳为 datetime df_raw pd.read_csv(f/tmp/{os.path.basename(pcap_path)}.csv) df_raw[frame.time_epoch] pd.to_datetime(df_raw[frame.time_epoch], units) # 步骤3构建时间窗口索引关键避免用 groupby.apply 导致内存爆炸 df_raw[window_id] ((df_raw[frame.time_epoch] - df_raw[frame.time_epoch].min()) // pd.Timedelta(f{window_sec}s)).astype(int) # 步骤4对每个窗口计算 27 维特征示例仅列出 5 个实际含 22 个统计量 5 个协议特有字段 features [] for window_id, window_df in df_raw.groupby(window_id): feat_dict { window_id: window_id, total_packets: len(window_df), syn_ratio: (window_df[tcp.flags].str.contains(0x002, naFalse).sum() / len(window_df)) if len(window_df) 0 else 0, entropy_src_ip: entropy(window_df[ip.src].value_counts(normalizeTrue)), http_uri_avg_len: window_df[http.request.uri].str.len().mean() if not window_df[http.request.uri].isna().all() else 0, dns_qry_count: window_df[dns.qry.name].count() } features.append(feat_dict) return pd.DataFrame(features)提示entropy()函数在/src/utils/misc.py中定义使用scipy.stats.entropy计算离散分布熵值。若未安装 scipy执行pip install scipy。参数注意window_sec不是越大越好。实测 CIC-IDS2017 中 Web Attack如 SQLi的爆发周期约 8~12 秒设为 10 秒可捕获峰值而 Botnet CC 通信周期长分钟级需配合后续的跨窗口特征如“过去 5 个窗口的 SYN 包标准差”识别。2.3 为什么不用 nDPI 而用 tshark——协议识别精度与工程落地的权衡项目文档明确说明默认采用 tshark 而非 nDPI。原因有三可复现性nDPI 需编译 C 库不同 Linux 发行版Ubuntu/Debian/CentOS的依赖版本冲突频发而 tshark 是 Wireshark 官方维护的稳定二进制协议覆盖够用tshark 对 HTTP/DNS/TCP/UDP 的解析准确率 99.2%经 CIC-IDS2017 标签验证已覆盖本项目所需全部攻击类型调试友好tshark 输出 CSV 字段名清晰如http.request.uri便于快速定位特征缺失问题nDPI 的 JSON 输出嵌套深字段名不统一如http.hostvshttp.request.full_uri。若你坚持用 nDPI源码中/src/feature_engineering/ndpi_extractor.py提供了备用接口但需自行解决libndpi.so的路径配置——这是第 4 章要重点避坑的内容。3. 模型选型与训练为什么放弃 XGBoost 和 LightGBM而用随机森林 ExtraTrees 集成3.1 安全场景下模型选择的三个硬约束在入侵检测中模型不是越复杂越好。我们面临三个无法妥协的约束可解释性优先当模型告警“端口扫描”安全员需要知道是“源 IP 熵值低 目标端口分布广 连接超时率高”共同触发而非黑盒输出一个概率值实时性要求单次推理需 50ms满足 10Gbps 网络下每秒万级流分析XGBoost 的树深度优化虽快但加载 1000 棵树的内存开销大对抗鲁棒性攻击者可能构造对抗样本如修改 TTL 字段绕过检测随机森林因基学习器独立性比 boosting 类模型更难被定向欺骗。因此源码中/src/models/train_model.py采用RandomForestClassifier主模型 ExtraTreesClassifier校验模型双轨训练前者提供可解释特征重要性后者通过完全随机分割提升泛化能力。3.2 训练脚本的关键参数与调优逻辑主训练函数train_and_save_models()位于/src/models/train_model.py核心参数如下def train_and_save_models(X_train: np.ndarray, y_train: np.ndarray, model_dir: str ./models): 训练 RF ExtraTrees 模型并保存 :param X_train: 归一化后的特征矩阵 (n_samples, n_features) :param y_train: 标签向量 (n_samples,) :param model_dir: 模型保存目录 # 步骤1处理类别不平衡CIC-IDS2017 中 Benign 占 83%Brute Force 仅 0.3% smote SMOTE(random_state42, sampling_strategyauto) # 对所有少数类过采样 tomek TomekLinks(sampling_strategyauto) # 删除多数类与少数类的邻近样本 X_res, y_res smote.fit_resample(X_train, y_train) X_res, y_res tomek.fit_resample(X_res, y_res) # 步骤2训练 RandomForest重点max_depth12 控制树深度避免过拟合 rf RandomForestClassifier( n_estimators200, # 树数量200 在精度与速度间平衡 max_depth12, # 关键超过 15 易过拟合低于 8 捕捉不到复杂模式 min_samples_split10, # 最小分裂样本数防噪声干扰 random_state42, n_jobs-1 # 使用所有 CPU 核心 ) rf.fit(X_res, y_res) # 步骤3训练 ExtraTreesn_estimators100因完全随机分割需更少树 et ExtraTreesClassifier( n_estimators100, # ExtraTrees 更高效100 棵足够 max_depth10, # 比 RF 稍浅强调泛化 random_state42, n_jobs-1 ) et.fit(X_res, y_res) # 步骤4保存模型joblib 比 pickle 更安全且支持 numpy 数组压缩 joblib.dump(rf, f{model_dir}/rf_model.joblib) joblib.dump(et, f{model_dir}/et_model.joblib) # 步骤5保存特征重要性供安全员解读 feature_names [total_packets, syn_ratio, entropy_src_ip, ...] # 实际 27 个 importance_df pd.DataFrame({ feature: feature_names, rf_importance: rf.feature_importances_, et_importance: et.feature_importances_ }).sort_values(rf_importance, ascendingFalse) importance_df.to_csv(f{model_dir}/feature_importance.csv, indexFalse)参数说明sampling_strategyauto表示 SMOTE 对所有y_train ! BENIGN的类别进行过采样TomekLinks 则删除所有被标记为 Tomek Link 的样本对即多数类样本与其最近邻的少数类样本距离最近n_jobs-1启用多进程但需确保服务器内存 ≥16GB否则n_estimators200会触发 OOMmax_depth12是经过 5 折交叉验证确定的在 CIC-IDS2017 上depth12 时 F1-score 达 0.923depth15 时降至 0.891过拟合。3.3 模型评估不止看 Accuracy必须验证在真实攻击流上的召回率源码中/src/evaluation/evaluate_model.py提供了面向安全场景的评估函数evaluate_on_attack_stream()它不只计算全局指标而是按攻击类型分组统计def evaluate_on_attack_stream(model, X_test, y_test, attack_types[BruteForce, DoS, WebAttack]): 在指定攻击类型子集上评估模型 :param attack_types: 攻击类型列表对应 y_test 中的标签字符串 # 提取攻击样本索引 attack_mask np.isin(y_test, attack_types) X_attack X_test[attack_mask] y_attack y_test[attack_mask] # 预测 y_pred model.predict(X_attack) # 计算每类攻击的召回率Recall TP / (TP FN) recall_per_type {} for atk in attack_types: tp np.sum((y_pred atk) (y_attack atk)) fn np.sum((y_pred ! atk) (y_attack atk)) recall_per_type[atk] tp / (tp fn) if (tp fn) 0 else 0 return recall_per_type # 示例调用 rf_model joblib.load(./models/rf_model.joblib) recalls evaluate_on_attack_stream(rf_model, X_test, y_test) print(Attack-wise Recall:) for atk, r in recalls.items(): print(f {atk}: {r:.3f}) # 输出BruteForce: 0.962, DoS: 0.941, WebAttack: 0.887为什么这比 Accuracy 重要Accuracy 会因 Benign 样本占比高83%而虚高——即使模型把所有攻击都判为 BenignAccuracy 也有 0.83。而召回率直接回答“当真实发生 BruteForce 时模型能抓出多少” 这才是 SOC 工程师最关心的数字。4. 部署与 API 服务Flask 接口如何接收 pcap 文件并返回 JSON 告警以及三个血泪避坑记录4.1/api/detect接口的完整请求-响应流程服务启动后python app.py可通过 POST 请求提交检测任务。接口设计遵循安全运维习惯支持两种输入格式返回结构化 JSON。请求示例上传 pcap 文件curl -X POST http://localhost:5000/api/detect \ -F file/path/to/attack.pcap \ -F window_sec10请求示例提交流量 JSONcurl -X POST http://localhost:5000/api/detect \ -H Content-Type: application/json \ -d { packets: [ {timestamp: 1620000000.123, src_ip: 192.168.1.100, dst_ip: 10.0.0.1, proto: TCP, flags: SYN}, {timestamp: 1620000000.124, src_ip: 192.168.1.100, dst_ip: 10.0.0.2, proto: TCP, flags: SYN} ], window_sec: 10 }成功响应JSON{ status: success, detected_attacks: [ { window_id: 5, attack_type: BruteForce, confidence: 0.982, features_used: [syn_ratio, entropy_src_ip, total_packets], raw_features: {syn_ratio: 0.92, entropy_src_ip: 0.15, total_packets: 1247} } ], summary: { total_windows: 120, benign_windows: 112, attack_windows: 8, highest_confidence: 0.982 } }关键设计点features_used字段直接给出触发告警的 top-3 特征方便安全员快速溯源raw_features返回原始数值避免归一化后失真如syn_ratio0.92比normalized_value0.87更直观summary提供宏观统计适合作为 SIEM 系统的输入。4.2 避坑部署时高频翻车的三个现象、原因与解法注意以下问题均来自真实复现过程非理论推测。现象1Flask 启动报错OSError: [Errno 98] Address already in use原因端口 5000 被其他进程如旧版 Flask、Jupyter Notebook、Docker 容器占用。解决# 查找占用 5000 端口的进程 lsof -i :5000 # macOS/Linux # 或 netstat -ano | findstr :5000 # Windows # 杀死进程以 PID 12345 为例 kill -9 12345 # 或直接换端口启动 python app.py --port 5001现象2上传 pcap 后接口返回{status: error, message: tshark command failed}原因tshark 未安装或权限不足尤其在 Ubuntu 上tshark 默认需 root 权限抓包但此处只需读文件。解决# Ubuntu/Debian 安装 tshark无需 root 运行 sudo apt update sudo apt install tshark -y # 允许普通用户读取 pcap关键 sudo setcap cap_net_raw,cap_net_admineip /usr/bin/dumpcap # 验证运行 tshark -v 应输出版本信息 tshark -v现象3模型预测始终返回Benign即使输入已知攻击 pcap原因特征工程阶段的时间窗口切分逻辑错误导致window_id计算异常常见于系统时区与 pcap 时间戳时区不一致。解决检查 pcap 时间戳时区用 Wireshark 打开 pcap → Statistics → Capture File Properties → 查看 “Time reference”强制统一为 UTC在feature_extractor.py的extract_features_from_pcap()函数中修改时间戳转换行# 原代码可能出错 df_raw[frame.time_epoch] pd.to_datetime(df_raw[frame.time_epoch], units) # 改为强制 UTC df_raw[frame.time_epoch] pd.to_datetime(df_raw[frame.time_epoch], units, utcTrue)重新提取特征并训练模型旧特征缓存需清空rm -rf ./data/features_cache/。5. 模型热更新与增量学习如何在不重启服务的情况下加载新模型并验证其效果5.1 为什么需要热更新——安全场景的现实约束在生产环境中你不可能每次更新模型就kill -9Flask 进程再python app.py。攻击手法每天进化如新型加密挖矿流量模型需每周甚至每日更新。源码中/src/models/model_loader.py实现了无中断模型热替换服务运行时将新模型文件rf_model.joblib放入./models/目录API 自动检测并加载旧请求继续用旧模型新请求立即用新模型。5.2 热更新机制的实现细节与验证方法核心逻辑在/src/models/model_loader.py的ModelLoader类中class ModelLoader: def __init__(self, model_dir: str ./models): self.model_dir model_dir self.rf_model None self.et_model None self.last_modified 0 self._load_models() # 首次加载 def _load_models(self): 加载模型并记录最后修改时间 rf_path os.path.join(self.model_dir, rf_model.joblib) et_path os.path.join(self.model_dir, et_model.joblib) if os.path.exists(rf_path) and os.path.exists(et_path): self.rf_model joblib.load(rf_path) self.et_model joblib.load(et_path) # 记录两个文件中较新的修改时间 self.last_modified max(os.path.getmtime(rf_path), os.path.getmtime(et_path)) def get_models(self): 检查模型是否更新若更新则重新加载并返回 rf_path os.path.join(self.model_dir, rf_model.joblib) et_path os.path.join(self.model_dir, et_model.joblib) current_mtime max(os.path.getmtime(rf_path), os.path.getmtime(et_path)) if \ os.path.exists(rf_path) and os.path.exists(et_path) else 0 if current_mtime self.last_modified: print(f[INFO] 检测到模型更新重新加载... (上次: {self.last_modified}, 当前: {current_mtime})) self._load_models() self.last_modified current_mtime return self.rf_model, self.et_model # 在 app.py 中全局实例化 model_loader ModelLoader() app.route(/api/detect, methods[POST]) def detect(): rf_model, et_model model_loader.get_models() # 每次请求都检查 if rf_model is None: return jsonify({status: error, message: 模型未加载}), 500 # ... 后续预测逻辑验证热更新是否生效启动服务python app.py用 curl 发送一次检测请求记录返回的highest_confidence修改/src/models/train_model.py中n_estimators100降低树数量重新运行训练脚本生成新模型观察终端输出[INFO] 检测到模型更新重新加载...再次发送相同请求对比highest_confidence是否变化应降低因模型变弱。5.3 增量学习用新攻击样本微调模型而非全量重训全量重训 CIC-IDS201780GB pcap需 6 小时不现实。源码提供/src/models/incremental_finetune.py支持在线增量学习def incremental_finetune(model_path: str, new_X: np.ndarray, new_y: np.ndarray, n_estimators_add: int 20): 对现有 RandomForest 增量添加树不破坏原有树 :param model_path: 原模型路径.joblib :param new_X: 新样本特征 (n_samples, n_features) :param new_y: 新样本标签 (n_samples,) :param n_estimators_add: 新增树数量建议 10~50避免过拟合 # 加载原模型 old_model joblib.load(model_path) # 创建新树集合 new_trees [] for _ in range(n_estimators_add): # 复制原模型参数仅改变随机种子 tree DecisionTreeClassifier( max_depthold_model.max_depth, min_samples_splitold_model.min_samples_split, random_statenp.random.randint(0, 10000) ) tree.fit(new_X, new_y) new_trees.append(tree) # 合并树关键不修改原模型对象创建新模型 new_forest RandomForestClassifier( n_estimatorsold_model.n_estimators n_estimators_add, max_depthold_model.max_depth, min_samples_splitold_model.min_samples_split, random_stateold_model.random_state, n_jobs-1 ) # 手动设置 trees_ 属性需深入 sklearn 源码此处简化为伪代码 # new_forest.trees_ old_model.estimators_ new_trees # 实际项目中推荐用 joblib 保存合并后模型 joblib.dump(new_forest, model_path.replace(.joblib, _finetuned.joblib))操作步骤收集新攻击样本如某次真实勒索软件通信 pcap用feature_extractor.py提取特征得到new_X.npy和new_y.npy运行python incremental_finetune.py --model ./models/rf_model.joblib --new_X ./data/new_X.npy --new_y ./data/new_y.npy将生成的_finetuned.joblib复制为rf_model.joblib触发热更新。效果在测试中对新型 Mirai 变种的检测召回率从 0.31 提升至 0.79耗时仅 12 分钟vs 全量重训 6 小时。6. 从那以后我每次部署模型前都强制走一遍“三步验证”特征一致性检查、模型输出分布审计、真实流量回放测试6.1 第一步特征一致性检查——确保训练与推理的特征 pipeline 完全一致这是最容易被忽略、却导致线上翻车的根源。训练时用tshark -r a.pcap -T fields -e ip.src提取源 IP推理时若误用tshark -r b.pcap -T fields -e ip.src -e ip.dst特征维度就从 27 变成 28模型直接报ValueError: X has 28 features, but RandomForest expected 27。源码中/src/validation/validate_features.py提供了自动化检查def validate_feature_consistency(train_feature_file: str, infer_feature_file: str): 比较训练特征 CSV 与推理特征 CSV 的列名、顺序、数据类型 :param train_feature_file: 训练特征 CSV如 ./data/train_features.csv :param infer_feature_file: 推理特征 CSV如 ./data/infer_features.csv train_df pd.read_csv(train_feature_file) infer_df pd.read_csv(infer_feature_file) # 检查列名是否一致顺序名称 if not train_df.columns.equals(infer_df.columns): missing_in_infer set(train_df.columns) - set(infer_df.columns) missing_in_train set(infer_df.columns) - set(train_df.columns) raise ValueError(f列名不一致infer 缺少: {missing_in_infer}, train 缺少: {missing_in_train}) # 检查每列数据类型防止 string 被误转为 float type_mismatch [] for col in train_df.columns: if train_df[col].dtype ! infer_df[col].dtype: type_mismatch.append(f{col}: train{train_df[col].dtype}, infer{infer_df[col].dtype}) if type_mismatch: raise ValueError(f数据类型不一致: {type_mismatch}) print([PASS] 特征列名与类型完全一致) # 使用示例在训练完模型后用同一 pcap 生成两份特征 CSV 进行比对 # python -c from src.validation.validate_features import validate_feature_consistency; validate_feature_consistency(./data/train_features.csv, ./data/test_features.csv)我的习惯每次更新feature_extractor.py后必跑此脚本。曾因tshark版本升级导致http.request.uri字段在某些 pcap 中为空返回而非NaN造成训练时该列是object类型推理时是float64模型崩溃。此检查 5 秒内定位问题。6.2 第二步模型输出分布审计——监控预测置信度是否异常漂移一个健康的模型其预测置信度如predict_proba的最大值应呈稳定分布。若某天突然大量出现confidence 0.99的告警大概率是特征漂移Feature Drift——比如网络设备升级后TCP 窗口大小字段范围从0-65535变为0-1048576模型误判为异常。源码中/src/monitoring/audit_confidence.py提供了审计函数def audit_confidence_distribution(model, X_batch: np.ndarray, threshold_low: float 0.1, threshold_high: float 0.99): 审计模型在批量样本上的置信度分布 :param threshold_low: 低置信度阈值0.1 表示模型犹豫 :param threshold_high: 高置信度阈值0.99 表示可能过拟合或漂移 probas model.predict_proba(X_batch) confidences np.max(probas, axis1) low_ratio np.mean(confidences threshold_low) high_ratio np.mean(confidences threshold_high) print(f置信度分布审计:) print(f 低置信度比例 ({threshold_low}): {low_ratio:.3f}) print(f 高置信度比例 ({threshold_high}): {high_ratio:.3f}) print(f 置信度均值: {np.mean(confidences):.3f}) print(f 置信度标准差: {np.std(confidences):.3f}) # 触发告警条件可集成到 Prometheus if high_ratio 0.3: # 超过 30% 样本置信度 0.99 print([ALERT] 高置信度比例异常疑似特征漂移) if low_ratio 0.4: # 超过 40% 样本置信度 0.1 print([ALERT] 低置信度比例异常模型可能失效) # 示例用 1000 个样本审计 X_sample X_test[:1000] rf_model joblib.load(./models/rf_model.joblib) audit_confidence_distribution(rf_model, X_sample)真实案例在某次客户现场此审计发现high_ratio从 0.05 飙升至 0.62排查发现是防火墙启用了 TCP 选项优化TCP Fast Open导致tcp.flags字段新增0x004标志而训练数据中从未出现模型将所有含此标志的流判为DoS置信度 0.999。及时回滚防火墙配置避免误报风暴。6.3 第三步真实流量回放测试——用录制的生产流量验证端到端链路所有单元测试都通过不代表线上不出问题。最终验证必须用真实流量。源码中/scripts/replay_test.py提供了轻量级回放工具def replay_traffic(pcap_path: str, api_url: str http://localhost:5000/api/detect, window_sec: int 10, batch_size: int 50): 回放 pcap 流量到 API统计成功率与耗时 :param pcap_path: 待回放的 pcap建议用 1 分钟真实流量 :param batch_size: 每批发送的窗口数避免单次请求过大 # 步骤1提取特征复用 feature_extractor features_df extract_features_from_pcap(pcap_path, window_sec) # 步骤2分批发送 success_count 0 total_time 0 for i in range(0, len(features_df), batch_size): batch features_df.iloc[i:ibatch_size] # 构造 JSON 请求体 payload { features: batch.to_dict(records), window_sec: window_sec } start_time time.time() try: resp requests.post(api_url, jsonpayload, timeout30) if resp.status_code 200 and resp.json().get(status) success: success_count 1 else: print(f[FAIL] 批次 {i//batch_size} 返回: {resp.status_code} {resp.text}) except Exception as e: print(f[EXCEPTION] 批次 {i//batch_size} 异常: {e}) finally: total_time time.time() - start_time print(f回放测试完成: {success_count}/{len(features_df)//batch_size} 批次成功, 平均耗时: {total_time/(len(features_df)//batch_size p a hrefhttps://download.csdn.net/download/FL1768317420/89305594 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p