尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Python招聘数据分析与可视化:从CSV清洗到Echarts大屏实战

发布时间:2026/9/24 20:04:08

资讯中心
01
ARTICLE

Python招聘数据分析与可视化:从CSV清洗到Echarts大屏实战

Python招聘数据分析与可视化:从CSV清洗到Echarts大屏实战
简介这是一套基于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看保留率再动可视化。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。