资讯详情

招聘数据分析可视化系统:Python+MySQL+Flask+ECharts实战

📅 2026/10/5 15:01:58 | 华诺云谱 👁 阅读
招聘数据分析可视化系统:Python+MySQL+Flask+ECharts实战
简介压缩包内是面向毕业设计的招聘数据分析可视化系统完整源码基于Python技术生态附带数据库脚本与文档说明。项目整合数据采集、后台管理、缓存与可视化展示可实现招聘职位统计、数据查询和管理员操作等场景适合计算机、通信、自动化等专业学生或从业者用于毕业设计、课程设计及二次进阶。包体共161个文件、约7.11MB主要包含Java后端服务、Vue前端页面、Python脚本、SVG图标、JavaScript交互逻辑、SCSS样式以及SQL初始化脚本分别对应业务逻辑、页面渲染、抓取解析、图表绘制、样式组织和数据库建表等用途。后端代码涵盖职位管理、管理员鉴权与Redis缓存等结构前端通过可视化组件呈现分析结果配套文档说明可帮助快速部署。源码已经调试测试作者答辩评审分达98分目前已有336人学习浏览适合需要一套完整、可运行、便于扩展的招聘数据分析可视化系统作参考的读者。1. 招聘数据分析可视化系统为什么代码简单、数据才是牛鼻子每年毕业设计选题里数据分析可视化系统都是高发区基于Python的招聘数据分析可视化系统更是典型中的典型一套能跑的Python源码、一份MySQL数据库脚本、一篇配套的毕业设计文档凑齐这三样一个完整的毕设课题就立住了。但绝大多数人拿到或准备做这套系统时第一反应是研究图表怎么画真正让他们熬夜翻车的却是数据——文件是乱码、数据库连不上、中文全是问号、图表一渲染就卡死。这篇文章就是顺着这个真实路径来的从建库、造数据、清洗入库到用Flask搭接口、用ECharts拼可视化大屏最后是五条血泪踩坑记录。适合准备做毕设但不想照抄模板的同学也适合想一天之内跑通一个数据分析可视化演示的开发者。2. 建库与数据准备模拟数据生成、清洗入库与 MySQL 表设计2.1 三种数据来源怎么选为什么我推荐先造模拟数据招聘数据分析的第一步是拿到招聘数据这一步卡掉了至少一半的人。常见的数据来源有三种我分别说下真实感受。第一种是写爬虫去招聘网站抓数据。这条路径听起来最“真实”但实际跑起来会碰到访问频率限制、页面结构改版、验证码等一连串问题而且招聘网站的字段结构并不稳定今天能抓的字段明天可能就变了。更关键的是把时间耗在采集与反爬对抗上会让整个毕设的节奏失控。合规风险也要考虑我一般不建议在毕设阶段硬碰这条。第二种是公开数据集比如各类数据竞赛平台上的招聘信息。数据质量不错但字段大多是英文与国内招聘场景里的“本科”“3-5年”“8K-15K”这些说法对不上后期做中文词云和学历分析时还要二次加工麻烦程度不低于自己造数据。第三种是我最推荐的做法写一个脚本生成模拟招聘数据。模拟数据的分布可以完全贴合你的分析维度——城市、岗位、学历、经验、薪资区间都能控制还能故意混入一些脏数据用于演示清洗过程。毕业设计评审关注的是“数据库设计—数据分析—可视化展示”这条链路是否完整闭合数据本身从哪来反而是次要的。先把链路跑通后面有余力再换真实数据这是性价比最高的路径。2.2 数据库表怎么设计两张表加一个字段约定数据链路的第一步是设计数据库。很多人一上来就照着“规范化理论”拆出五六张表公司表、职位表、城市表、学历表拆到最后查询要连四张表把自己绕晕。做毕业设计我一般建议控制在两张表一张业务主表jobs一张管理员表admin。jobs表存招聘岗位的全部业务字段admin表只用来做登录功能便于论文里写“系统具有用户认证模块”。CREATE DATABASE recruitment DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE recruitment; CREATE TABLE jobs ( id INT PRIMARY KEY AUTO_INCREMENT, company VARCHAR(100), position VARCHAR(100), city VARCHAR(50), education VARCHAR(20), experience VARCHAR(50), salary_min INT, salary_max INT, skill_tags VARCHAR(200), publish_date DATE, source_url VARCHAR(255) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE, password_hash VARCHAR(255) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段建表 SQL 里有几个参数需要说明。DEFAULT CHARACTER SET utf8mb4是必选项很多人在 Windows 上建表时漏了它后续插入中文就报Incorrect string value错误COLLATE utf8mb4_unicode_ci是排序规则保证中文查询时“北京”和“北京 ”不会因为尾随空格产生歧义。salary_min和salary_max为什么要拆成两个 INT 而不是直接存字符串“8K-15K”因为字符串无法做聚合排序统计分析时要算平均薪资就只能干瞪眼。这是招聘分析系统里最经典的一个设计约定论文的数据库设计章节可以直接引用这套表结构。2.3 把 CSV 清洗后批量入库Python 里的增删改查表建好之后下一步是生成模拟数据并写入数据库。下面的脚本会生成 5000 条招聘记录同时故意混入两类脏数据3% 的记录缺失最低薪资2% 的记录把薪资写成了字符串。这样后面清洗环节才有真实的处理对象。import csv import random cities [北京, 上海, 广州, 深圳, 杭州, 成都, 武汉, 南京] positions [Python开发工程师, 数据分析师, Java开发工程师, 前端开发工程师, 算法工程师, 运维工程师] companies [云创科技, 数智未来, 蓝海信息, 合众软件, 天工网络] educations [大专, 本科, 硕士, 不限] experiences [应届, 1-3年, 3-5年, 5年以上] def gen_row(): salary_min random.randint(6, 20) * 1000 salary_max random.randint(12, 35) * 1000 if random.random() 0.03: salary_min None # 制造缺失值 if random.random() 0.02: salary_max str(salary_max) # 制造类型错误 days random.randint(0, 365) return [random.choice(companies), random.choice(positions), random.choice(cities), random.choice(educations), random.choice(experiences), salary_min, salary_max, days] with open(recruitment_raw.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([company, position, city, education, experience, salary_min, salary_max, publish_days]) for _ in range(5000): writer.writerow(gen_row())生成逻辑的关键在salary_min和salary_max的取值范围最低薪资落在 6000—20000最高薪资落在 12000—35000两者之间天然存在一个区间符合真实招聘信息的形态。None和字符串混入后CSV 里会出现空单元格和带引号的数字这就是后续清洗要处理的对象。publish_days存的是距离今天的天数写入数据库前再换算成具体日期比在生成阶段就直接造日期更方便。生成 CSV 之后进入清洗与入库环节。清洗的核心任务有三个删除完全重复的行、剔除薪资缺失的记录、把薪资字段统一转成数值类型。import pandas as pd import pymysql from datetime import datetime, timedelta df pd.read_csv(recruitment_raw.csv, encodingutf-8) df df.drop_duplicates(subset[company, position, city, publish_days]) df df.dropna(subset[salary_min, salary_max]) df df[df[salary_min] 0] df[salary_min] df[salary_min].astype(int) df[salary_max] df[salary_max].astype(int) df[publish_date] datetime.now().date() - pd.to_timedelta(df[publish_days], unitD) conn pymysql.connect(hostlocalhost, userroot, password123456, databaserecruitment, charsetutf8mb4) cursor conn.cursor() data [tuple(row) for row in df[[company, position, city, education, experience, salary_min, salary_max, publish_date]].values] sql (INSERT INTO jobs (company, position, city, education, experience, salary_min, salary_max, publish_date) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)) cursor.executemany(sql, data) conn.commit() cursor.close() conn.close()这段代码里有两个地方要重点说明。第一drop_duplicates的subset参数只传了四个字段没有把id传进去因为主键本身就不允许重复按业务字段去重才是真实场景下的操作。第二cursor.executemany是批量写入比逐条execute快一个数量级5000 条数据在这个量级下基本是瞬间完成conn.commit()必须显式调用否则事务不会落库。整个过程就是 MySQL 最常见的增删改查中的插入操作但使用了参数化占位符%s避免拼接 SQL 带来的转义和注入问题。到这里数据库里已经有了一批干净可用的数据。接下来要做的就是把数据从数据库里取出来、算成指标、变成图表。3. 数据分析和可视化大屏Flask API 与 ECharts 的配合实践3.1 先框定四个分析维度岗位、城市、薪资、学历做数据分析和可视化实践之前第一件事不是写代码而是把分析维度定下来。招聘数据分析一般围绕四个问题展开哪些岗位招得最多、哪些城市给得最高、学历要求怎么分布、经验年限怎么分布。这四个问题对应四类图表岗位需求量用柱状图或横向条形图城市薪资水平用柱状图学历占比用饼图经验要求用环形图或柱状图。再补一个技能关键词词云整套可视化大屏就丰满了。维度定下来之后图表指标也就锁定了。岗位需求算的是COUNT(*)城市薪资算的是AVG((salary_min salary_max) / 2)学历和经验占比也是计数。这些指标看起来简单但它们决定了后面 SQL 怎么写、API 返回什么结构、前端图表渲染什么字段。把口径先写在纸上比先写代码再返工高效得多。我见过太多人先画图再想指标结果图出来了答辩老师问“这个数字怎么算的”就答不上来。3.2 用 pandas 聚合出图表要的数指标定好之后先用 pandas 在本地跑一遍聚合逻辑确认结果符合预期再把它翻译成 API 接口。这一步相当于给整套可视化方案做一次离线预演不用启动任何 Web 服务就能看到最终数字。import pandas as pd import pymysql conn pymysql.connect(hostlocalhost, userroot, password123456, databaserecruitment, charsetutf8mb4) df pd.read_sql(SELECT * FROM jobs, conn) city_salary (df.assign(avg_salary(df[salary_min] df[salary_max]) / 2) .groupby(city)[avg_salary] .mean() .sort_values(ascendingFalse)) top_positions df[position].value_counts().head(10) edu_dist df[education].value_counts() print(city_salary) print(top_positions) print(edu_dist)df.assign的作用是临时新增一列avg_salary这一列不在数据库表结构里但做统计时用得到这比修改原表更安全。groupby(city)[avg_salary].mean()先按城市分组再对平均薪资列求均值得到每个城市的平均月薪sort_values(ascendingFalse)让结果从高到低排列前端条形图就不用再排序了。三个聚合结果出来之后和业务直觉对照一下一线城市薪资靠前、岗位集中在技术方向就说明数据质量基本可用。3.3 Flask 封装查询接口一次请求对应一张图pandas 验证通过后把这些聚合逻辑搬进 Flask 接口。设计原则是“一个图表对应一个 API 路由”这样前端代码结构清晰后端排查问题也简单。下面是两个核心接口的完整实现。from flask import Flask, jsonify import pymysql app Flask(__name__) def query(sql): conn pymysql.connect(hostlocalhost, userroot, password123456, databaserecruitment, charsetutf8mb4) cursor conn.cursor() cursor.execute(sql) rows cursor.fetchall() conn.close() return rows app.route(/api/city_salary) def city_salary(): sql (SELECT city, ROUND(AVG((salary_min salary_max) / 2), 2) AS avg_salary FROM jobs GROUP BY city ORDER BY avg_salary DESC) rows query(sql) return jsonify([{name: r[0], value: r[1]} for r in rows]) app.route(/api/top_positions) def top_positions(): sql (SELECT position, COUNT(*) AS cnt FROM jobs GROUP BY position ORDER BY cnt DESC LIMIT 10) rows query(sql) return jsonify([{name: r[0], value: r[1]} for r in rows]) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)这套接口设计有几个参数值得注意。query函数内部每次创建新连接、用完即关避免长连接闲置导致 MySQL 自动断开5000 条数据量级下这种开销完全可以忽略但如果你准备换成真实全量数据就要改成连接池。jsonify返回的列表里每个元素都是{name: ..., value: ...}结构这是 ECharts 最常见的输入格式前后端约定这个结构前端代码就不用做二次拆解。app.run里host0.0.0.0是刻意为之这样同一局域网的演示电脑也能访问答辩现场很实用debugFalse是因为调试模式下的 Werkzeug 工具会暴露本地调试接口演示环境不该开。3.4 前端 ECharts 大屏JSON 进来、图表出去后端接口就绪后前端用 ECharts 渲染。选 ECharts 而不是其他图表库的原因很直接可视化效果接近商业大屏、文档完善、支持按需引入而且可以整个 JS 文件下载到本地答辩现场断网也能正常展示。下面是城市薪资柱状图的完整前后端交互。div idcitySalary stylewidth:100%; height:360px;/div script srcjs/echarts.min.js/script script fetch(/api/city_salary) .then(res res.json()) .then(data { const chart echarts.init(document.getElementById(citySalary)); chart.setOption({ title: { text: 各城市平均月薪 }, tooltip: { trigger: axis }, xAxis: { type: category, data: data.map(d d.name) }, yAxis: { type: value }, series: [{ type: bar, data: data.map(d d.value), itemStyle: { color: #2f89fc } }] }); }); /script这段代码展示的是完整请求链路浏览器fetch请求到 Flask 接口Flask 查数据库返回 JSON前端拿到数组后分别映射到xAxis.data和series.data。data.map(d d.name)这种写法比单独维护两个数组更不容易出错后端新增城市后前端自动同步。itemStyle.color只在柱状图里统一颜色大屏整体配色建议单独维护一个主题配置文件不要每张图各自写色值。其他图表照葫芦画瓢即可饼图把type换成pie、把data换成[{name:..., value:...}]格式词云图用wordcloud类型的 ECharts 扩展需要额外引入一个 JS 文件。到这里一个能跑通的招聘数据分析可视化系统已经成型MySQL 存数据、Flask 出接口、ECharts 画图表。接下来才是真正决定你答辩顺利与否的部分——部署和排错。4. 跑通前后的五个大坑从中文乱码到 ECharts 卡顿的排查记录4.1 CSV 中文乱码图表的坐标轴全是问号现象用 pandas 读取 CSV 后打印 DataFrame城市和岗位全是乱码图表渲染出来坐标轴上的标签是一排问号。原因Windows 环境下 Excel 保存的 CSV 默认是 GBK 编码而 pandas 的read_csv默认按 UTF-8 解码两边对不上就出现乱码或解析错误。这是“源码能跑、数据文件翻车”的最常见情形。解决读取时显式指定编码格式或者统一转码后另存。df pd.read_csv(recruitment_raw.csv, encodinggbk) df pd.read_csv(recruitment_raw.csv, encodingutf-8)如果原始文件是 GBK第一种写法能直接读如果文件已经是 UTF-8第一种写法会报UnicodeDecodeError这时改回第二种。最稳妥的做法是把源文件统一转成 UTF-8后续所有环节只认一种编码。我的习惯是拿到数据先跑一句df.head()看输出再决定后续处理不盲目假设编码。4.2 MySQL 8.0 连接报 caching_sha2_password现象pymysql 连接 MySQL 时报Authentication plugin caching_sha2_password cannot be loaded程序直接退出。原因MySQL 8.0 默认的认证插件是caching_sha2_password而一些旧版本客户端和驱动不认识这个插件。这不是密码错误是认证方式不匹配。解决把账号认证方式改回mysql_native_password或者升级连接库到支持新插件的新版本。ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;如果你用的是远程连接rootlocalhost要改成root%否则改了也连不上。这条ALTER USER本质上是修改数据库用户的结构也属于数据库日常维护的范畴。改完之后重启 MySQL 服务再用原连接代码就能正常连上。4.3 词云输出一片方框缺中文字体现象词云图生成成功但所有词都渲染成一个个方框英文正常、中文全黑块。原因wordcloud 库默认字体是 DroidSansMono这套字体不包含中文字形遇到汉字只能显示占位符。这不是配置文件写错是字体文件的锅。解决显式指定一个包含中文字形的字体文件路径。from wordcloud import WordCloud wc WordCloud( width800, height600, font_pathC:/Windows/Fonts/simhei.ttf, background_colorwhite ).generate(text)simhei.ttf是 Windows 自带的黑体路径按实际系统调整macOS 可以换成PingFang.ttc。这个坑第一次遇到会感觉很玄学——图像能生成、代码不报错但输出就是没法看。答辩前一定要在生成词云的那台机器上确认字体路径存在换机器跑就重新检查一次。4.4 ECharts 渲染上万点卡成 PPT现象图表数据量不大时很流畅但一旦查询结果返回几千行甚至上万行页面交互明显卡顿缩放和 tooltip 都跟不上。原因ECharts 的scatter和line图表在处理大量数据点时默认全量渲染而成每个点都对应一个 DOM 对象浏览器扛不住。数据量到这个级别问题已经从前端展示变成了后端设计思路。解决后端先聚合、压点数前端配合开启数据降采样。# 后端按城市岗位聚合而不是返回原始明细 sql (SELECT city, position, COUNT(*) AS cnt, ROUND(AVG((salary_min salary_max) / 2), 2) AS avg_salary FROM jobs GROUP BY city, position)前端在series里加一行sampling: lttbECharts 会按拟采样算法抽取特征点。本质思路是图表的视觉容量有限把几千条聚合到几百条信息不丢流畅度却会大幅提升。答辩演示时数据量通常不大但如果你把真实招聘数据灌进来这个优化几乎是必然要做的。4.5 SQL 拼接查询出错顺手留的注入后门现象查询条件改成包含引号的字符串时程序报错更有经验的老师会直接指出代码存在注入风险。原因用 f-string 或直接拼接 SQL 语句当参数里有单引号时 SQL 语法被破坏当参数是恶意构造的字符串时等于把数据库查询权限交给了调用方。毕设代码里出现这种写法会被划入“安全意识不足”的评语里。解决全部改用参数化查询。# 错误写法 sql fSELECT * FROM jobs WHERE city {city} # 正确写法 sql SELECT * FROM jobs WHERE city %s cursor.execute(sql, (city,))参数化查询不是“最好”的写法而是必选项。%s占位符让驱动替你把转义和格式化处理好SQL 注入的入口直接从根上关掉了。这是整套系统里最不应该妥协的一处代码质量也是文档说明部分最容易写清楚的一个点直接给出正确写法和错误写法的对比导师一眼就能看到你确实理解了这个安全问题。5. 让系统经得起答辩图表口径验证、演示脚本与进阶方向5.1 用一条 SQL 复查每张图的口径答辩前最后一个习惯每张图背后都对应一条可复现的 SQL。把前端的图表和这条 SQL 的结果对齐能发现大量低级错误——比如城市薪资柱状图的排序和 SQL 的ORDER BY不一致、饼图百分比加起来不等于 100%。我一般直接把聚合 SQL 写在论文的“系统测试”一节里让每张图都能回溯到数据库记录。SELECT city, COUNT(*) AS cnt, ROUND(AVG((salary_min salary_max) / 2), 2) AS avg_salary FROM jobs GROUP BY city ORDER BY avg_salary DESC;这条 SQL 查出的cnt和avg_salary应该和前端图表上的数值一一对应。答辩老师随机抽一个数字问你“这个怎么来的”你能指到对应的表和查询语句整个系统的可信度就立住了。5.2 三分钟演示脚本与两个进阶方向答辩演示脚本按“总—分—总”控制三分钟先展示大屏整体效果说清系统用了什么技术栈再点开两张核心图表讲数据从哪来、指标怎么算最后关掉大屏切到数据库客户端展示原始表的行数和几条样本数据证明数据真是落地在 MySQL 里不是写死在前端。这个流程短但完整能把系统从“看得到”讲到“查得到”。进阶方向有两个值得做一是给系统加上登录权限把admin表真正用起来配合 Flask 的 session 做登录校验论文里就能多写一个功能模块二是对薪资做简单回归分析用sklearn的线性回归预测不同城市、不同岗位的薪资区间把系统从“统计展示”升级成“分析与预测”这正好呼应数据分析课程的实践目标。说句个人教训我第一次做这类系统时把图表数据和 SQL 口径都验证好了却忘了检查词云的中文字体答辩现场投影上一片方框场面相当尴尬。后来养成的习惯是答辩前一天用演示电脑完整跑一遍全流程截图留底。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑