鱼泡直聘蓝领岗位招聘数据可视化分析系统 - 技术文档
1. 项目概述
1.1 项目简介
本系统是基于 Python 的蓝领岗位招聘数据可视化分析平台,针对 2024 年 12 月鱼泡直聘网(鱼泡网)的蓝领招聘数据进行多维度分析。系统提供薪资分布、岗位类别、城市分布、福利待遇、结算方式等基础分析,以及统计检验、回归分析、异常检测、聚类分析、关联规则挖掘等高级数据挖掘功能。
1.2 技术栈
| | |
|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| Starlette SessionMiddleware | |
| | |
| | |












































1.3 项目结构
code/├── main.py # 主路由文件(所有 Web 路由定义)├── database.py # 数据库操作层(MySQL CRUD)├── run.py # 启动脚本(依赖检查、数据转换、启动服务)├── config.yaml # 项目配置(数据集、模块开关、分析参数)├── requirements.txt # Python 依赖清单├── data/ # 数据目录│ ├── 2024年12月鱼泡网招聘信息已处理.xlsx # 原始 Excel 数据│ └── 2024年12月鱼泡网招聘信息已处理.csv # 转换后的 CSV(供 shared 模块使用)├── reports/ # 生成的报告文件目录├── shared/ # 核心分析模块(纯 Python,无 Web 依赖)│ ├── __init__.py│ ├── analysis_core.py # 通用数据分析(概览、相关性矩阵)│ ├── stats_core.py # 统计检验(描述性统计、正态性检验、卡方检验)│ ├── regression_core.py # 回归分析(简单/多元线性回归、诊断)│ ├── anomaly_core.py # 异常检测(IQR、Z-Score、Isolation Forest)│ ├── clustering_core.py # 聚类分析(K-Means、DBSCAN、最优K)│ ├── association_core.py # 关联规则(Apriori 频繁项集、规则生成)│ └── report_core.py # 报告导出(Excel、PDF)├── static/ # 静态资源│ ├── css/│ │ ├── style.css # 主样式文件(CSS 变量、组件样式)│ │ ├── bootstrap.min.css # Bootstrap 5│ │ └── bootstrap-icons.css│ └── js/│ ├── echarts.min.js # ECharts 图表库│ └── bootstrap.bundle.min.js└── templates/ # Jinja2 模板 ├── base.html # 基础模板(导航栏、页脚、全局脚本) ├── index.html # 首页 Dashboard ├── login.html # 登录页 ├── register.html # 注册页 ├── profile.html # 个人中心 ├── edit_profile.html # 编辑资料 ├── change_password.html # 修改密码 ├── analytics.html # 数据概览 ├── salary_analysis.html # 薪资分布分析 ├── job_category.html # 岗位类别分析 ├── city_distribution.html # 城市分布分析 ├── benefits_analysis.html # 福利待遇分析 ├── payment_analysis.html # 结算方式分析 ├── correlation.html # 相关性分析 ├── stat_tests.html # 统计检验分析 ├── regression.html # 薪资回归分析 ├── anomaly.html # 异常检测 ├── clustering.html # 聚类分析 ├── association.html # 关联规则分析 ├── geo_map.html # 城市地图 ├── reports.html # 报告导出 ├── data_manage.html # 数据管理列表 ├── data_manage_edit.html # 数据编辑/新增 ├── admin/ │ ├── dashboard.html # 管理员仪表盘 │ └── users.html # 用户管理 └── macros/ # Jinja2 可复用宏 ├── kpi.html # KPI 指标卡片宏 ├── charts.html # 图表容器宏 └── tables.html # 数据表格、页面头宏
2. 系统架构
2.1 架构设计
系统采用经典的 MVC(Model-View-Controller)分层架构:
┌─────────────────────────────────────────────────────┐│ 浏览器 (Browser) ││ Bootstrap 5 + ECharts + JavaScript │└──────────────────────┬──────────────────────────────┘ │ HTTP┌──────────────────────▼──────────────────────────────┐│ Web 层 (main.py) ││ FastAPI 路由 + Jinja2 模板渲染 ││ 职责:请求转发、数据组装、模板渲染 │├─────────────────────────────────────────────────────┤│ 数据层 (database.py) ││ PyMySQL 数据库操作 ││ 职责:用户管理、数据集 CRUD │├─────────────────────────────────────────────────────┤│ 分析核心层 (shared/*.py) ││ analysis_core stats_core regression_core ││ anomaly_core clustering_core association_core ││ report_core ││ 职责:纯数据分析逻辑,无 Web 依赖 │├─────────────────────────────────────────────────────┤│ 数据源层 ││ MySQL 数据库 + CSV/Excel 文件 │└─────────────────────────────────────────────────────┘
2.2 核心设计原则
- 关注点分离:
shared/ 核心模块纯 Python 实现,不依赖 FastAPI/Jinja2,可独立测试和复用 - 配置驱动:
config.yaml 集中管理数据集定义、模块开关、分析参数,新增分析页面只需加配置 - 宏复用:Jinja2 宏(
macros/)封装 KPI 卡片、图表容器、表格等重复 UI 组件 - CSS 变量主题:所有颜色、间距通过 CSS 自定义属性管理,支持一键换肤
- 懒加载:核心模块的 DataFrame 通过
@property 延迟加载,首次访问时读取 CSV
2.3 数据流
Excel 文件 → run.py 转换 → CSV 文件 → shared 模块读取 → 分析结果 (dict) ↓MySQL 数据库 ← database.py 导入 ← CSV 文件 ↓main.py 路由 ← 调用 shared 模块 + database 操作 ↓Jinja2 模板渲染 → HTML + JSON 数据 → ECharts 渲染图表
3. 配置文件 (config.yaml)
3.1 项目配置
project: id: '145' name: 鱼泡直聘蓝领岗位招聘数据可视化分析系统 name_en: recruitment stack: fastapi_bootstrap db_name: design_145_recruitment # MySQL 数据库名 db_host: localhost db_port: 3306 db_user: root db_password: '123456' port: 8000 # Web 服务端口 theme_color: '#2563EB' # 主题色
3.2 数据集配置
dataset: file: 2024年12月鱼泡网招聘信息已处理.xlsx target_column: '' # 目标列(本项目无预测目标) encoding: utf-8 features: # 特征定义列表 - key: 岗位标题 # 数据库列名 / CSV 列名 label: 岗位标题 # 中文显示名 type: text # 类型: text / numeric / categorical / binary - key: 薪资下限 label: 薪资下限 type: numeric unit: 元/月 # 单位(可选) range: [180, 100000] # 合理范围(可选) - key: 结算方式 label: 结算方式 type: categorical options: [月结, 日结, 未知, 周结, ...] # 分类选项
特征类型说明:
3.3 模块开关
modules: auth: true # 用户认证 roles: [user, admin] # 角色定义 prediction: false # 预测功能(未启用) analytics: true # 数据分析 data_manage: true # 数据管理 dashboard: true # 首页仪表盘 statistical_analysis: true # 统计检验 regression: true # 回归分析 anomaly_detection: true # 异常检测 clustering: true # 聚类分析 association_rules: true # 关联规则 report_export: true # 报告导出 geo_map: true # 地图可视化
3.4 分析页面配置
analysis_pages:- route: salary_analysis title: 薪资分布分析 description: 薪资下限、上限、平均薪资的分布与交叉分析 columns: [薪资下限, 薪资上限, 平均薪资] group: 薪资分析- route: regression title: 薪资回归分析 type: regression target: 薪资上限 # 回归目标变量 predictors: [薪资下限] # 回归自变量- route: anomaly title: 薪资异常检测 type: anomaly columns: [薪资下限, 薪资上限, 平均薪资]
3.5 聚类与关联规则配置
clustering: columns: [薪资下限, 薪资上限, 平均薪资] # 聚类特征 max_k: 8 # 最大 K 值association: columns: [结算方式, 是否包住, 是否包吃, 城市] # 关联规则分析列
4. 数据库设计
4.1 数据库配置
- 引擎
- 库名
- 字符集
- 驱动: PyMySQL(DictCursor 模式,返回字典而非元组)
4.2 表结构
users 表(用户表)
默认管理员账号: admin / admin123
dataset_data 表(数据集表)
列根据 config.yaml 的 features 动态生成:
数据量: 500 条招聘记录,覆盖 20 个城市
4.3 数据库初始化流程
Database.__init__() ├── CREATE TABLE users(如不存在) ├── ALTER TABLE users ADD COLUMN(兼容旧表) ├── 创建默认管理员(如无 admin 用户) ├── CREATE TABLE dataset_data(列从 config.yaml 动态生成) └── 如 dataset_data 为空 → 自动调用 import_csv() 导入数据
4.4 数据导入机制
import_csv() 方法支持 Excel 和 CSV 两种格式:
- 根据文件扩展名选择
pd.read_excel() 或 pd.read_csv() - 批量插入(每 500 条一批
executemany)
5. 核心分析模块 (shared/)
5.1 通用设计模式
所有核心模块遵循统一的接口模式:
class XxxCore: def __init__(self, data_path: str, feature_config: List[Dict]): self.data_path = data_path self.feature_config = feature_config self._df = None # 懒加载 @property def df(self) -> pd.DataFrame: if self._df is None: self._df = pd.read_csv(self.data_path) return self._df def reload(self): self._df = None # 强制重新加载
返回值规范:所有方法返回 dict 或 list,可直接 json.dumps() 序列化。
5.2 analysis_core.py - 通用数据分析
功能:
overview(): 数据概览(行列数、列类型、缺失值统计、基本统计量)demographics(columns)correlation_matrix(columns)
调用示例:
analyzer = AnalysisCore(data_path="data/dataset.csv", target_column="", feature_config=[...])overview = analyzer.overview()corr = analyzer.correlation_matrix(["薪资下限", "薪资上限", "平均薪资"])# corr = {"columns": [...], "matrix": [[1.0, 0.98, 0.99], ...]}
5.3 stats_core.py - 统计检验
功能:
descriptive_stats(columns): 描述性统计(均值、中位数、标准差、偏度、峰度)normality_test(column)summary(numeric_cols, categorical_cols)
依赖: SciPy (scipy.stats)
5.4 regression_core.py - 回归分析
功能:
linear_regression(x_col, y_col)multiple_regression(target, predictors)diagnostics(target, predictors)
返回值结构(多元回归):
{ "target": "薪资上限", "predictors": ["薪资下限"], "r_squared": 0.9555, "adj_r_squared": 0.9554, "f_statistic": 10680.5, "intercept": 523.45, "coefficients": [ {"name": "薪资下限", "coefficient": 1.234, "std_err": 0.012, "t_statistic": 103.2, "p_value": 0.0, "significant": True} ], "residual_stats": {"mean": 0.0, "std": 1234.5, "min": -5000, "max": 4500, ...}}
依赖: NumPy(矩阵运算 np.linalg.lstsq)
5.5 anomaly_core.py - 异常检测
功能:
iqr_detection(columns, multiplier=1.5)zscore_detection(columns, threshold=3.0)isolation_forest(columns, contamination=0.05)summary(columns)
IQR 返回值:
{ "薪资下限": { "q1": 5000.0, "q3": 9000.0, "iqr": 4000.0, "lower_bound": -1000.0, "upper_bound": 15000.0, "outlier_count": 23, "outlier_pct": 4.6, "box_data": {"min": 180, "q1": 5000, "median": 7000, "q3": 9000, "max": 14500} }}
依赖: scikit-learn(Isolation Forest)
5.6 clustering_core.py - 聚类分析
功能:
find_optimal_k(columns, max_k=10)kmeans(columns, k=3)dbscan(columns, eps=0.5, min_samples=5)
K-Means 返回值:
{ "k": 3, "total": 500, "cluster_sizes": {0: 200, 1: 180, 2: 120}, "centers": {"薪资下限": {0: 5000, 1: 8000, 2: 12000}, ...}, "silhouette_score": 0.45, "scatter_data": {"x": [...], "y": [...], "labels": [...]}, "profile": [{"cluster": 0, "size": 200, "avg_薪资下限": 5000, ...}, ...]}
依赖: scikit-learn(KMeans, DBSCAN, StandardScaler, silhouette_score)
5.7 association_core.py - 关联规则
功能:
prepare_transactions(columns)mine_rules(columns, min_support, min_confidence)top_rules(columns, top_n, sort_by)
返回值:
{ "rules": [ {"antecedents": ["是否包住=是"], "consequents": ["结算方式=月结"], "support": 0.15, "confidence": 0.72, "lift": 2.3} ], "frequent_itemsets": [ {"items": ["是否包住=是"], "support": 0.21} ], "total_rules": 741, "total_frequent_itemsets": 294}
依赖: mlxtend(apriori, association_rules)
注意: mlxtend 0.23+ 的 association_rules() 需要 num_itemsets 参数。
5.8 report_core.py - 报告导出
功能:
list_reports(output_dir)generate_excel(output_dir, title)generate_pdf(output_dir, title)
报告内容:数据概览、描述性统计、薪资分布、城市统计、岗位类别统计等。
6. Web 路由设计 (main.py)
6.1 路由总览
| | | |
|---|
/ | | | |
/login | | | |
/register | | | |
/logout | | | |
/profile | | | |
/edit_profile | | | |
/change_password | | | |
/analytics/overview | | | |
/analytics/salary_analysis | | | |
/analytics/job_category | | | |
/analytics/city_distribution | | | |
/analytics/benefits_analysis | | | |
/analytics/payment_analysis | | | |
/analytics/correlation | | | |
/analytics/stat_tests | | | |
/analytics/regression | | | |
/analytics/anomaly | | | |
/clustering | | | |
/clustering/run | | | |
/association | | | |
/association/mine | | | |
/geo_map | | | |
/reports | | | |
/reports/generate | | | |
/reports/download/{filename} | | | |
/data_manage | | | |
/data_manage/add | | | |
/data_manage/edit/{id} | | | |
/data_manage/delete/{id} | | | |
/data_manage/import_csv | | | |
/admin | | | |
/admin/users | | | |
/admin/users/create | | | |
/admin/users/{id}/role | | | |
/admin/users/{id}/status | | | |
/admin/users/{id}/reset_password | | | |
/admin/users/{id}/delete | | | |
/api/overview | | | |
6.2 认证与权限
系统使用 Starlette 的 SessionMiddleware 基于 Cookie 的会话管理:
app.add_middleware(SessionMiddleware, secret_key="dev-secret-change-me", same_site="lax")
权限函数:
get_current_user(request)require_login(request)require_admin(request)is_admin(user)
权限控制模式:
@app.get("/protected")async def protected_page(request: Request): user = require_login(request) if not user: return RedirectResponse(url="/login", status_code=302) # ... 业务逻辑
6.3 数据分析路由模式
所有分析路由遵循统一模式:
@app.get("/analytics/xxx", response_class=HTMLResponse)async def analytics_xxx(request: Request): user = get_current_user(request) # 1. 获取用户 df = _load_dataframe() # 2. 加载数据 # 3. 数据处理(Pandas 操作) result = df.groupby(...).agg(...) # 4. 渲染模板 return templates.TemplateResponse("xxx.html", { "request": request, "current_user": user, "page_config": {"route": "xxx", "title": "..."}, "field_mapping": FIELD_MAPPING, "data": result, })
6.4 辅助函数
def _load_dataframe() -> pd.DataFrame: """加载数据集为 DataFrame(自动判断 Excel/CSV)"""def _get_numeric_cols() -> list: """获取所有数值列名"""def _get_categorical_cols() -> list: """获取所有分类列名"""def _cn(col: str) -> str: """列名中文映射(Jinja2 全局函数)"""
7. 前端设计
7.1 模板继承体系
base.html├── 导航栏 (navbar)├── {% block content %} ← 子模板填充├── 页脚 (footer)├── 全局 JavaScript│ ├── chartColors 配色方案│ ├── cn() 中文映射函数│ ├── initChart() ECharts 初始化│ ├── 导航高亮│ ├── 回到顶部按钮│ └── KPI 计数动画└── {% block extra_js %} ← 子模板额外 JS
7.2 Jinja2 宏组件
KPI 卡片宏 (macros/kpi.html)
{{ kpi.kpi_card(value, label, icon, variant, suffix, prefix) }}
variant: primary / success / warning / info(决定配色)- 响应式:
col-md-3 col-6(桌面 4 列,手机 2 列) - 支持计数动画(IntersectionObserver 触发)
图表容器宏 (macros/charts.html)
{{ charts.chart_card(chart_id, title, icon, height) }} {# 半宽 #}{{ charts.chart_card_full(chart_id, title, icon, height) }} {# 全宽 #}{{ charts.chart_card_md4(chart_id, title, icon, height) }} {# 1/3 宽 #}
- 内含
.chart-card 样式(圆角、阴影、hover 效果) chart_id 用于 ECharts initChart(chartId, option) 绑定
页面头宏 (macros/tables.html)
{{ tbl.page_header(title, subtitle, icon) }}
7.3 ECharts 图表初始化模式
// 基础初始化var chart = initChart('chartDomId', { color: chartColors, // 全局配色方案 tooltip: { trigger: 'axis' }, grid: { left: '10%', right: '5%', top: '10%', bottom: '10%' }, xAxis: { type: 'category', data: [...] }, yAxis: { type: 'value' }, series: [{ type: 'bar', data: [...] }]});// 数据从 Jinja2 注入var data = {{ result | tojson }};
7.4 CSS 设计系统
CSS 变量
:root { /* 主题色(蓝色系) */ --primary: #2563EB; --primary-dark: #1D4ED8; --primary-darker: #1E40AF; --primary-light: #3B82F6; --primary-lighter: #60A5FA; --primary-50: #EFF6FF; /* 背景色 */ --bg-white: #FFFFFF; --bg-body: #F8FAFC; /* 文字色 */ --text-primary: #1E293B; --text-secondary: #64748B; --text-muted: #94A3B8; /* 边框 */ --border: rgba(0, 0, 0, 0.08); /* 玻璃态 */ --glass-bg: rgba(255, 255, 255, 0.7); /* 圆角 */ --radius-sm: 0.375rem; --radius-md: 0.5rem; --radius-lg: 0.75rem; --radius-xl: 1rem; --radius-2xl: 1.5rem; --radius-full: 9999px;}
设计风格
- 玻璃态 (Glassmorphism): 导航栏、页脚使用
backdrop-filter: blur(20px) - 微渐变: 背景使用多层
radial-gradient 叠加 - 柔和阴影: 卡片使用
0 1px 3px rgba(0,0,0,0.04) 等低透明度阴影 - 同色系配色
- 入场动画:
.content-wrapper 使用 pageEnter 关键帧动画
7.5 响应式布局
基于 Bootstrap 5 的栅格系统:
- 桌面 (≥768px): 侧边导航、4 列 KPI、2 列图表
- 手机 (<768px): 折叠导航、2 列 KPI、1 列图表
- 表格
8. 数据集说明
8.1 数据来源
8.2 字段说明
8.3 覆盖城市(20 个)
北京、上海、天津、重庆、厦门、广州、深圳、佛山、郑州、武汉、长沙、南京、苏州、无锡、青岛、西安、成都、杭州、宁波、合肥
9. 功能模块详细说明
9.1 首页 Dashboard
路由: GET /
展示内容:
- 4 个 KPI 卡片:总岗位数、平均薪资、覆盖城市数、岗位类别数
数据处理:
# 薪资分箱bins = [0, 3000, 5000, 8000, 12000, 20000, 150000]labels = ['3k以下', '3k-5k', '5k-8k', '8k-12k', '12k-20k', '20k以上']
9.2 薪资分布分析
路由: GET /analytics/salary_analysis
图表:
9.3 岗位类别分析
路由: GET /analytics/job_category
图表:
- Top15 岗位类别柱状图(数量 + 平均薪资双轴)
9.4 城市分布分析
路由: GET /analytics/city_distribution
图表:
9.5 福利待遇分析
路由: GET /analytics/benefits_analysis
图表:
9.6 结算方式分析
路由: GET /analytics/payment_analysis
图表:
9.7 相关性分析
路由: GET /analytics/correlation
图表:
算法:np.corrcoef() 计算 Pearson 相关系数
9.8 统计检验分析
路由: GET /analytics/stat_tests
内容:
9.9 薪资回归分析
路由: GET /analytics/regression
配置(config.yaml):
- route: regression target: 薪资上限 predictors: [薪资下限]
图表:
- 回归系数表(系数、标准误、t 统计量、p 值、显著性)
9.10 异常检测
路由: GET /analytics/anomaly
三种方法:
- IQR 法:Q1 - 1.5×IQR 以下或 Q3 + 1.5×IQR 以上为异常
- Z-Score 法
- Isolation Forest:contamination=0.05,sklearn 实现
图表:
- KPI:总数据量、IF 异常数、IF 异常占比、IQR 分析变量数
9.11 聚类分析
路由: POST /clustering/run
支持算法:
功能:
9.12 关联规则分析
路由: POST /association/mine
参数:
min_supportmin_confidence
图表:
9.13 城市地图
路由: GET /geo_map
实现:
- GeoJSON 来源:阿里 DataV 省级 GeoJSON
- 城市→省份映射:通过
cityToProvince 对象转换 - 双图层:省级色块(map)+ 城市散点标记(scatter on geo)
9.14 报告导出
路由: GET /reports, POST /reports/generate
格式:
- Excel (.xlsx):多 Sheet(概览、统计、分布等)
- PDF
文件命名: report_YYYYMMDD_HHMMSS.xlsx/pdf
9.15 数据管理(管理员)
路由: /data_manage/*
功能:
9.16 用户管理(管理员)
路由: /admin/users/*
功能:
10. 启动与部署
10.1 环境要求
- 操作系统:Windows / Linux / macOS
10.2 安装依赖
pip install -r requirements.txt
主要依赖:
fastapi>=0.100.0uvicorn>=0.23.0jinja2>=3.1.0python-multipart>=0.0.6itsdangerous>=2.1.0starlette>=0.27.0pandas>=2.0.0numpy>=1.24.0scipy>=1.10.0scikit-learn>=1.3.0mlxtend>=0.23.0pymysql>=1.1.0passlib>=1.7.4bcrypt>=4.0.0pyyaml>=6.0openpyxl>=3.1.0
10.3 启动方式
# 方式一:使用启动脚本(推荐)python run.py# 方式二:直接启动python main.py# 方式三:Uvicorn 命令行uvicorn main:app --host 127.0.0.1 --port 8000 --reload
10.4 启动流程 (run.py)
1. 加载 config.yaml2. 检查所有依赖是否安装3. 检查数据文件是否存在4. Excel → CSV 转换(如 CSV 不存在)5. 创建必要目录(reports/, static/, templates/)6. 启动 Uvicorn 服务器(reload=True 热重载)
10.5 访问地址
- 首页: http://localhost:8000
- 登录: http://localhost:8000/login
11. 开发规范
11.1 硬性规范
- 禁止蓝紫渐变配色
- 禁止 N+1 查询
- 代码复用
11.2 新增分析页面流程
- 在
config.yaml 的 analysis_pages 添加配置
11.3 核心模块调用规范
11.4 模板开发规范
- 图表数据通过
{{ data | tojson }} 注入 JavaScript
12. 已知问题与注意事项
12.1 Bootstrap Icons 版本
项目使用的是捆绑的 bootstrap-icons.css,部分新版图标不可用。已知不可用的图标及替代:
12.2 Jinja2 保留字冲突
字典的 .items() 方法会覆盖同名键的访问。对于包含 items 键的字典,必须使用方括号语法:
{# 错误 #}{{ item.items | join(', ') }}{# 正确 #}{{ item['items'] | join(', ') }}
12.3 mlxtend 版本兼容
mlxtend 0.23+ 的 association_rules() 新增了 num_itemsets 必需参数:
# 旧版rules = association_rules(freq_items, metric="confidence", min_threshold=0.5)# 新版 (0.23+)rules = association_rules(freq_items, num_itemsets=len(encoded), metric="confidence", min_threshold=0.5)
12.4 GeoJSON 地图限制
阿里 DataV 的 GeoJSON 是省级行政区划,城市名(如"深圳市")不在其中。系统通过 cityToProvince 映射将城市转为省份进行着色,同时用散点标记显示具体城市位置。
12.5 回归分析配置
回归目标和自变量不能有包含关系(如用"薪资下限+薪资上限"预测"平均薪资"会导致 R²=1 的数据泄露)。当前配置:
target: 薪资上限predictors: [薪资下限]
13. API 接口
13.1 JSON API
目前仅提供一个 JSON API 端点:
返回数据概览 JSON:
{ "total_rows": 500, "total_columns": 15, "columns": [...], "dtypes": {...}, "missing": {...}, "numeric_stats": {...}}
14. 版本历史