简介这是一套基于Python实现的招聘网站数据分析与可视化项目源码面向计算机相关专业的毕业设计、期末大作业与课程设计场景也适合想通过完整案例入门数据分析的初学者。项目已通过老师指导并取得高分代码为纯手写实现实战门槛较低。压缩包共54个文件约6.68MB包含4个py脚本与4个ipynb笔记本用于数据获取、清洗与分析8个csv存放前程无忧等招聘原始数据另有html、js、css等前端文件支撑Echarts大屏展示以及png、jpg图表与词云素材、xml配置和字体文件。目录按数据获取、数据清洗、数据分析、数据可视化、Echarts大屏展示等模块划分结构清晰。目前已有573人学习下载。读者可据此掌握招聘信息的采集、清洗、统计与可视化全流程直接复用词云图、工作经验与学历分布饼图等成果并参考大屏展示方案快速完成自己的项目。1. 从一份招聘 CSV 到能跑的大屏这套 Python 数据分析源码到底能干什么如果你手头正压着一个「招聘网站数据分析及数据可视化」的课设或毕设大概率卡在同一个地方爬虫能跑但数据脏得没法看图表能画但拼不成一张能交差的大屏。这套基于 Python 的招聘网站数据分析及数据可视化源码解决的正是从原始 CSV 到可视化大屏的完整链路问题。它把数据获取、数据清洗、数据分析、可视化展示拆成了四个独立目录每个环节都有对应的 ipynb 和 py 文件不是那种只丢一个 notebook 的半成品。适合两类人一是需要快速交付课程设计的学生二是想拿真实招聘数据练手 pandas 和 Echarts 的入门开发者。下面我按实际拆包顺序把每个模块的用法和坑讲清楚。2. 数据获取与清洗前程无忧 CSV 怎么从脏数据变成可用表2.1 数据获取目录里的三个脚本各管什么打开数据获取文件夹能看到前程无忧.py、中国大学.py、测试.py三个文件外加两份 CSV。这里的分工很明确前程无忧.py负责抓取招聘岗位信息中国大学.py大概率是抓取院校维度数据用于交叉分析测试.py是调试用的临时脚本。我一般会先看测试.py因为它通常保留了最原始的请求逻辑能快速判断目标站点当前的结构有没有变。# 前程无忧.py 典型结构根据目录推断的常见写法 import requests from bs4 import BeautifulSoup import pandas as pd def get_page(url): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, headersheaders, timeout10) resp.encoding gbk # 前程无忧老页面常用 gbk不设会乱码 return resp.text def parse_jobs(html): soup BeautifulSoup(html, html.parser) items soup.select(.j_joblist .e) # 岗位卡片选择器站点改版会失效 rows [] for item in items: rows.append({ 岗位: item.select_one(.jname).get_text(stripTrue), 公司: item.select_one(.cname).get_text(stripTrue), 薪资: item.select_one(.sal).get_text(stripTrue), 地点: item.select_one(.d.at).get_text(stripTrue), }) return rows if __name__ __main__: all_data [] for page in range(1, 11): # 翻 10 页别贪多容易被限 html get_page(fhttps://search.51job.com/list/000000,000000,0000,00,9,99,python,2,{page}.html) all_data.extend(parse_jobs(html)) pd.DataFrame(all_data).to_csv(前程无忧.csv, indexFalse, encodingutf-8-sig)这段代码的关键参数有三个encodinggbk不设会得到一堆问号timeout10防止单页卡死拖垮整个循环encodingutf-8-sig写 CSV 时用保证 Excel 打开不乱码。翻页范围我建议控制在 10 页以内超过之后返回空列表的概率明显上升这是血泪经验。2.2 数据清洗.ipynb 里的四步标准流程数据清洗目录下的数据清洗.ipynb是整个项目最值得细看的部分。它处理的输入是前程无忧.csv输出是干净的招聘信息.csv。我拆开看核心就四步去重、去空、薪资字段结构化、地点字段归一化。import pandas as pd import re df pd.read_csv(前程无忧.csv) # 第一步按岗位公司去重招聘网站重复投放很常见 df df.drop_duplicates(subset[岗位, 公司]) # 第二步丢掉关键字段为空的行 df df.dropna(subset[岗位, 薪资, 地点]) # 第三步薪资区间拆成最低和最高两列单位统一为千/月 def parse_salary(s): s str(s).replace(万/月, 0000).replace(千/月, 000) nums re.findall(r\d, s) if len(nums) 2: return int(nums[0]), int(nums[1]) return None, None df[[薪资下限, 薪资上限]] df[薪资].apply( lambda x: pd.Series(parse_salary(x)) ) # 第四步地点只保留城市名去掉区级后缀 df[城市] df[地点].str.split(-).str[0] df.to_csv(招聘信息.csv, indexFalse, encodingutf-8-sig)parse_salary这个函数是清洗环节最容易翻车的地方。前程无忧的薪资写法至少有五种1.5-2万/月、8-10千/月、15-20万/年、200元/天、面议。上面这段只覆盖了前两种遇到年薪和日薪会返回 None。常见做法是再加一层判断把「万/年」除以 12 换算成月薪把「元/天」乘以 21.75 换算成月薪。面议直接丢弃不要试图填充填充出来的数字会污染后续的薪资分布分析。提示清洗后的招聘信息.csv才是后续所有分析和可视化的输入不要跳过这一步直接拿原始 CSV 画图否则学历饼图里会出现「学历不限」「不限」两个几乎一样的扇区。3. 数据分析与词云从招聘信息里挖出学历、经验和福利关键词3.1 数据分析.ipynb 的三个分析维度数据分析.ipynb承接清洗后的招聘信息.csv主要做三件事学历分布统计、工作经验分布统计、福利关键词提取。这三个维度对应后面三张核心图表是整个项目的分析骨架。import pandas as pd from collections import Counter import jieba df pd.read_csv(招聘信息.csv) # 学历分布value_counts 直接出频次 edu_dist df[学历].value_counts() print(edu_dist) # 工作经验分布注意字段里可能有「经验不限」「1年以下」等混合值 exp_dist df[工作经验].value_counts() # 福利词云把福利字段拼接后分词统计 all_welfare .join(df[福利].dropna().astype(str)) words jieba.lcut(all_welfare) # 过滤掉单字和常见停用词 stopwords {的, 和, 等, 有, 无, 不限} word_freq Counter([w for w in words if len(w) 1 and w not in stopwords]) with open(福利词云图.txt, w, encodingutf-8) as f: for word, freq in word_freq.most_common(100): f.write(f{word} {freq}\n)value_counts()默认按频次降序直接喂给饼图就行。词云这部分的关键在停用词表的、和、等这类词不滤掉词云图会被它们占满真正的「五险一金」「带薪年假」「年终奖」反而被挤小。word_freq.most_common(100)取前 100 个词写入 txt这个 txt 就是后面词云图的输入。3.2 词云图.ipynb 的字体与蒙版参数数据可视化目录下的词云图.ipynb读取福利词云图.txt和招聘信息词云图.txt生成带形状的词云。目录里那几张蜡笔小新.jpg、哆啦A梦.jpg就是蒙版图用来控制词云的轮廓。from wordcloud import WordCloud import numpy as np from PIL import Image # 读取蒙版图非白区域才会填词 mask np.array(Image.open(蜡笔小新.jpg)) # 读取词频文件 freq {} with open(福利词云图.txt, encodingutf-8) as f: for line in f: word, count line.strip().split() freq[word] int(count) wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, # 不指定中文字体全是方块 background_colorwhite, maskmask, max_words150, max_font_size120, colormapviridis ) wc.generate_from_frequencies(freq) wc.to_file(福利词云图.png)font_path是词云图最大的坑不指定中文字体生成出来全是豆腐块。Windows 用simhei.ttfMac 换成/System/Library/Fonts/PingFang.ttc。mask参数要求蒙版图的背景是纯白如果蒙版图背景带一点灰词会填到轮廓外面去。max_words150控制词的数量太多会糊成一团太少又看不出分布。3.3 饼图.py 与 HTML 输出的分工饼图.py是独立脚本生成工作经验饼图.html和学历.html。这里用的是 pyecharts不是 matplotlib。选 pyecharts 的理由很实际输出的 HTML 可以直接嵌进大屏交互式悬停显示数值比静态 PNG 好看得多。from pyecharts import options as opts from pyecharts.charts import Pie import pandas as pd df pd.read_csv(招聘信息.csv) edu df[学历].value_counts() pie ( Pie() .add(, [list(z) for z in zip(edu.index.tolist(), edu.values.tolist())]) .set_global_opts(title_optsopts.TitleOpts(title学历分布)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {c} ({d}%))) ) pie.render(学历.html)formatter{b}: {c} ({d}%)里的{b}是类别名{c}是数值{d}是百分比这三个占位符是 pyecharts 的固定写法改顺序可以改字母会报错。render输出的 HTML 自带 echarts.min.js 的 CDN 引用离线打开会白屏这是后面大屏部署时要注意的点。4. Echarts 大屏展示把散落的 HTML 拼成一张能交差的页面4.1 myEcharts 目录的结构与入口文件Echarts大屏展示下的myEcharts是整个项目的门面。它不是一个单独的 HTML而是一组文件配合入口 HTML、样式 CSS、数据 JS可能还有几张背景图。我一般先找index.html打开看它引了哪些 JS 和 CSS就能反推出数据从哪来。!-- index.html 典型结构 -- !DOCTYPE html html head meta charsetutf-8 link relstylesheet hrefcss/style.css script srcjs/echarts.min.js/script !-- 本地引入不依赖 CDN -- /head body div ideduChart stylewidth:600px;height:400px;/div div idexpChart stylewidth:600px;height:400px;/div script srcjs/data.js/script script srcjs/chart.js/script /body /html关键点是echarts.min.js用本地引入而不是 CDN。很多同学直接把 pyecharts 生成的 HTML 往大屏里嵌结果一断网就白屏答辩现场翻车。正确做法是把 echarts.min.js 下载到js目录所有图表共用一份。4.2 数据对接从 CSV 到 JS 数组的转换大屏里的图表数据不是实时读 CSV 的而是提前转成 JS 数组写进data.js。转换方式有两种手动复制或者写个 Python 脚本自动生成。import pandas as pd import json df pd.read_csv(招聘信息.csv) edu df[学历].value_counts() # 转成 echarts 需要的 [{name: 本科, value: 120}, ...] 格式 data [{name: k, value: int(v)} for k, v in edu.items()] with open(data.js, w, encodingutf-8) as f: f.write(var eduData json.dumps(data, ensure_asciiFalse) ;)ensure_asciiFalse必须加否则中文会变成\u672c\u79d1这种转义序列虽然浏览器能解析但调试时看着头疼。生成的data.js里就是一个全局变量eduDatachart.js里直接引用即可。4.3 大屏自适应与分辨率适配大屏最常见的翻车是换台电脑就错位。原因是图表容器用了固定像素宽高而不同屏幕的缩放比例不一样。常见做法是用 rem 或者 vw/vh 做相对单位配合window.onresize重绘。// chart.js 里的自适应逻辑 var eduChart echarts.init(document.getElementById(eduChart)); eduChart.setOption({ series: [{ type: pie, data: eduData }] }); window.onresize function() { eduChart.resize(); // 窗口变化时重绘不加重绘会拉伸变形 };resize()这个方法不调用图表在窗口缩放后不会跟着变而是保持初始尺寸被拉伸饼图会变成椭圆。如果大屏里有多个图表每个实例都要单独调resize()或者用echarts.connect统一管理。注意答辩用的投影仪分辨率通常是 1024x768 或 1280x720开发时按 1920x1080 做的大屏投上去会溢出。我一般把设计稿按 1280 宽做再用 CSS 的transform: scale()适配大屏这样两边都不吃亏。5. 避坑与排查这套源码跑不起来时先查这五处5.1 现象前程无忧.py返回空列表CSV 只有表头原因目标站点改版.j_joblist .e这个选择器失效或者请求头被识别。解决先用测试.py单独请求一页把resp.text打印出来看结构用浏览器的开发者工具重新定位岗位卡片的选择器。如果返回的是验证页说明请求频率过高把翻页间隔加到 3 秒以上。5.2 现象词云图全是方块一个中文都看不到原因WordCloud的font_path没设或者设了一个不存在的路径。解决Windows 确认C:/Windows/Fonts/simhei.ttf存在Mac 用/System/Library/Fonts/PingFang.ttc。路径里的斜杠用正斜杠反斜杠在 Python 字符串里是转义符会报错。5.3 现象pyecharts 生成的 HTML 打开是空白页原因pyecharts 默认引用在线 CDN 的 echarts.min.js断网或 CDN 被墙时加载失败。解决在render之前调pie.render(学历.html)后手动把 HTML 里的 CDN 地址换成本地路径或者用CurrentConfig.ONLINE_HOST指定本地资源目录。5.4 现象薪资分析结果明显偏低大量岗位薪资为 0原因parse_salary没覆盖「万/年」和「元/天」两种格式返回 None 后被填充成 0。解决在函数里加分支判断遇到「万/年」先乘 10000 再除以 12遇到「元/天」乘 21.75。填充 0 是最坏的选择会让薪资分布图整体左移。5.5 现象大屏在别人电脑上打开图表全部叠在一起原因CSS 用了绝对定位但没设z-index或者容器宽高用了百分比但父元素没有明确高度。解决给每个图表容器设position: relative和明确的height父容器用 flex 布局而不是绝对定位。如果必须绝对定位给每个图表加不同的z-index。6. 进阶技巧用 pyecharts 的 Timeline 做动态学历变化图基础饼图只能展示某一时刻的学历分布但招聘数据里往往带时间维度。如果 CSV 里有「发布日期」字段可以用 pyecharts 的Timeline做按季度播放的动态图答辩时比静态图多一个记忆点。from pyecharts.charts import Pie, Timeline import pandas as pd df pd.read_csv(招聘信息.csv) df[季度] pd.to_datetime(df[发布日期]).dt.to_period(Q) timeline Timeline() for quarter in df[季度].unique(): sub df[df[季度] quarter][学历].value_counts() pie ( Pie() .add(, [list(z) for z in zip(sub.index.tolist(), sub.values.tolist())]) .set_global_opts(title_optsopts.TitleOpts(titlef{quarter} 学历分布)) ) timeline.add(pie, str(quarter)) timeline.render(学历变化时间轴.html)to_period(Q)把日期转成季度Timeline.add的第二个参数是时间轴上的标签。播放速度用timeline.add_schema(play_interval1000)控制1000 毫秒一帧太快看不清太慢答辩时间不够。这个技巧的边界是数据量少于 4 个季度时时间轴只有三四帧动画效果不明显不如直接画分组柱状图。还有一个验证技巧跑完所有 notebook 后用招聘信息.csv的行数反推清洗保留率。如果原始 CSV 有 5000 行清洗后只剩 800 行说明去重和去空太激进可能把有效数据也丢了。我一般把保留率控制在 60% 以上低于这个数就回头检查dropna的 subset 是不是设多了。从那以后我每次拿到这类项目都强制先跑一遍数据清洗.ipynb看保留率再动可视化。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
自研物流系统SLDS:从FO业务拆解到token exchange故障排查 这几年做自营电商物流系统,我越来越觉得,真正决定系统上限的往往不是那套花哨的算法,而是能不能把“履约”这两个字拆得足够细、接得足够稳。今天想聊的就是我们自研的SLDS(Self-operated Logistics Delivery System)自… · 2026/9/24 20:03:59
互联网公司如何选型AI低代码平台?米缀AI低代码深度评测 1. 互联网公司选型AI低代码平台的真实逻辑1.1 为什么“低代码”这个词已经不够用了前两年大家聊低代码,核心诉求还是“少写代码、快速出活”。表单拖拽、流程编排、报表配置,这套东西确实帮不少团队把内部管理系统的交付周期从两个月压到了两周。但到了2… · 2026/9/24 20:03:59
用Claude MCP与AdsPower搭建多账号自动化流水线:从开浏览器到订单抓取 先说一个我自己的体会:以前我每天打开 AdsPower,手动登录店铺后台、复制订单号、回消息、记库存,一百个账号轮完差不多要一整天。后来我用 Claude MCP 把 AdsPower 的本地接口接进来,让 AI 按照我的任务清单去打开浏览器、点页面、… · 2026/9/24 20:03:59
Java Swing 黄金矿工小游戏:抓钩状态机与碰撞检测实战 简介:这是一份基于Java实现的黄金矿工小游戏完整源码包,面向Java初学者、课程设计学生以及想通过经典小游戏练手的开发者,帮助读者理解Swing图形界面、游戏循环、碰撞检测与资源加载等核心机制。压缩包共30个文件,约141KB… · 2026/9/24 22:02:05
体育馆场地预约系统开发实战:微信小程序+Django+Flask架构解析 体育馆场地预约平台开发手记:从电话排队到小程序一键订场做体育馆场地预约系统,最早是因为一个朋友在高校体育部上班,天天被电话轰炸:羽毛球场地有没有?今晚七点的场子被人占了能不能调?隔壁单位想包场怎么… · 2026/9/24 22:02:05
GPT-Live-1+Agora构建AI会议助手实战指南 1. 这不是“又一个AI聊天框”,而是一个能真正坐在会议室里干活的数字同事GPT‑Live‑1 Agora 实战教程:做一个能参会、操作看板的 AI 助手——这个标题里藏着三个被多数人忽略的关键动作:“能参会”、“操作看板”、“实战教程”。它不讲大模… · 2026/9/24 22:02:05
全栈AI修图Agent实战:从架构设计到模型调度与踩坑记录 “又一个新项目完结”——这句话说出口的时候,我终于能把“全栈 AI 修图 Agent”从待办列表里划掉了。这个项目从立项到交付,前后差不多一个多月,期间推翻过一版架构,也踩了不少模型和前后端的坑。如果你最近也在折腾 AI 全栈项目… · 2026/9/24 22:02:05
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44