简介本资源为基于Python的招聘岗位数据爬虫及可视化分析完整项目面向计算机相关专业在校学生、教师及企业员工尤其适合作为毕业设计、课程设计或项目立项演示。项目采用Pycharm与Python3.7开发通过Requests库爬取招聘网站数据并存入MySQL数据库再借助Echarts实现饼图、直方图、折线图、扇图等可视化展示首页即可查看综合分析图表。压缩包共59个文件约10.34MB涵盖py源码、pyc编译文件、xml配置、png与jpg图表、js脚本、sql建表数据、xls数据表、pptx演示文稿及md说明文档等结构完整便于二次开发。目前已有240人浏览学习。代码均经测试运行成功答辩评审平均分达96分读者可获取完整爬虫与可视化方案、数据库脚本、演示文稿及文档说明适合小白进阶学习也可在现有基础上修改扩展实现其他功能。1. 招聘岗位数据爬虫与可视化从数据采集到洞察的完整落地路径招聘网站上的岗位数据是观察一个行业技术栈变迁最直接的温度计。但很多人卡在第一步想分析 Python 岗位的薪资分布、技能要求、地域差异却不知道数据从哪来、怎么存、怎么画。这个项目要解决的就是这条链路——用 Python 爬虫抓取招聘岗位数据清洗后做可视化分析最终输出一份能看懂、能复用的分析报告。适合有 Python 基础、想做一个完整数据项目练手的人也适合需要快速了解招聘市场行情的 HR 或求职者。核心不是爬虫本身而是从采集到洞察的完整闭环。2. 招聘数据爬虫的选型与最小可运行采集方案2.1 为什么选 requests BeautifulSoup 而不是 Scrapy做招聘数据采集第一件事是选工具。常见做法有三种requests BeautifulSoup、Scrapy 框架、Selenium 模拟浏览器。我一般会先看目标站点的反爬强度和页面渲染方式。招聘网站大多属于服务端渲染岗位列表和详情页的 HTML 里直接包含数据不需要等 JavaScript 执行。这种情况下requests 拿到的响应文本里就有完整信息用 BeautifulSoup 或 lxml 解析足够。Scrapy 更适合大规模、多站点、需要去重和断点续爬的场景但它的学习曲线和调试成本对一个小型分析项目来说偏重。Selenium 则是页面完全由 JS 渲染、或者需要模拟点击翻页时的兜底方案缺点是慢、吃资源。这个项目定位是“岗位数据采集 可视化分析”数据量通常在几千到几万条requests BeautifulSoup 的组合最直接。下面是一个最小可运行的采集脚本以某招聘列表页为例import requests from bs4 import BeautifulSoup import csv import time import random HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, } def fetch_page(url): 请求单页返回 HTML 文本 try: resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.text except requests.RequestException as e: print(f请求失败: {url} - {e}) return None def parse_list(html): 解析列表页提取岗位链接和基础信息 soup BeautifulSoup(html, lxml) items [] for card in soup.select(.job-list-item): title card.select_one(.job-title) company card.select_one(.company-name) salary card.select_one(.salary) link card.select_one(a) if not title or not link: continue items.append({ title: title.get_text(stripTrue), company: company.get_text(stripTrue) if company else , salary: salary.get_text(stripTrue) if salary else , url: link.get(href, ), }) return items def save_csv(rows, filenamejobs.csv): 追加写入 CSV with open(filename, a, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[title, company, salary, url]) writer.writeheader() if f.tell() 0 else None writer.writerows(rows) if __name__ __main__: base_url https://example.com/jobs?page{} for page in range(1, 6): html fetch_page(base_url.format(page)) if not html: continue rows parse_list(html) save_csv(rows) print(f第 {page} 页采集 {len(rows)} 条) time.sleep(random.uniform(1.5, 3.5))这段代码的逻辑很直白fetch_page负责带请求头拿页面parse_list用 CSS 选择器定位岗位卡片save_csv追加写入。关键参数有三个User-Agent必须伪装成正常浏览器否则大概率被拒timeout10防止某个请求卡死整个流程time.sleep的随机间隔是给服务器留缓冲也降低被封的概率。选择器.job-list-item、.job-title这些需要根据目标站点的实际 DOM 结构调整。打开浏览器开发者工具右键岗位卡片“检查”找到包裹每条数据的父元素类名再逐层定位子元素即可。2.2 翻页策略与详情页字段补全列表页通常只有岗位名称、公司、薪资、地点这些摘要信息。要分析技能要求必须进详情页抓取岗位描述。翻页有两种常见方式URL 参数翻页和点击“下一页”按钮。优先用 URL 参数因为构造简单、可并发、可断点续爬。详情页采集时建议把列表页和详情页分成两个阶段。第一阶段只抓列表把岗位 URL 存下来第二阶段遍历 URL 列表逐个抓详情。这样做的好处是列表页翻车了不用重抓详情详情页某条失败也不影响整体进度。def parse_detail(html): 解析详情页提取岗位描述和技能关键词 soup BeautifulSoup(html, lxml) desc soup.select_one(.job-detail-section) if not desc: return None text desc.get_text(separator\n, stripTrue) return {description: text} def crawl_details(csv_filejobs.csv, outputjobs_detail.csv): 读取列表 CSV逐条抓详情 import pandas as pd df pd.read_csv(csv_file) results [] for _, row in df.iterrows(): html fetch_page(row[url]) if not html: continue detail parse_detail(html) if detail: results.append({**row.to_dict(), **detail}) time.sleep(random.uniform(2, 4)) pd.DataFrame(results).to_csv(output, indexFalse, encodingutf-8-sig)这里用 pandas 做中间存储因为后续可视化分析也要用 DataFrame。parse_detail里的.job-detail-section同样是示例选择器实际要替换。注意time.sleep在详情页阶段要更长因为详情页请求更重频率太高容易触发验证码。提示如果目标站点有登录墙或验证码不要硬闯。优先找不需要登录的列表页或者用公开的招聘数据接口。采集频率控制在每分钟 1020 次以内既是礼貌也是自保。3. 数据清洗与字段标准化把脏数据变成可分析的表3.1 薪资字段的解析与统一量纲爬下来的薪资字段五花八门“15-25K·13薪”、“20K-40K”、“面议”、“300元/天”。直接拿来做统计结果一定是错的。必须先把薪资拆成数值区间统一成月薪元再分析。常见做法是写一个解析函数用正则匹配数字和单位再根据“K”、“万”、“元/天”、“·13薪”这些关键词做换算。下面是一个可复用的解析逻辑import re def parse_salary(s): 解析薪资字符串返回 (最低月薪, 最高月薪, 薪资月数) 无法解析时返回 (None, None, None) if not s or 面议 in s: return None, None, None # 提取薪资月数如 13薪、14薪 months 12 m re.search(r·(\d)薪, s) if m: months int(m.group(1)) # 匹配 15-25K 或 20K-40K 或 1.5-2.5万 pattern r(\d\.?\d*)\s*[-~]\s*(\d\.?\d*)\s*([Kk万]) m re.search(pattern, s) if m: low, high, unit float(m.group(1)), float(m.group(2)), m.group(3) if unit in (K, k): return int(low * 1000), int(high * 1000), months if unit 万: return int(low * 10000), int(high * 10000), months # 匹配单个数值如 20K m re.search(r(\d\.?\d*)\s*([Kk万]), s) if m: val, unit float(m.group(1)), m.group(2) if unit in (K, k): return int(val * 1000), int(val * 1000), months if unit 万: return int(val * 10000), int(val * 10000), months # 匹配 元/天 m re.search(r(\d)\s*元/天, s) if m: daily int(m.group(1)) return daily * 22, daily * 22, months return None, None, None这个函数先处理“·13薪”这种月数信息再按“K/万”单位换算最后兜底“元/天”按每月 22 个工作日折算。返回三个值最低月薪、最高月薪、薪资月数。后续分析时通常取中位数(low high) / 2作为该岗位的代表薪资。参数说明months默认 12如果字符串里有“·13薪”就改成 13。这个字段在计算年薪时有用但大多数可视化只看月薪可以先忽略。3.2 技能关键词提取与城市字段归一岗位描述里藏着技能要求但它是大段文本。要分析“Python 岗位最常要求哪些技能”需要把描述拆成关键词。常见做法是维护一个技能词典用字符串匹配统计出现次数。SKILLS [ Python, Java, Go, C, SQL, MySQL, Redis, Django, Flask, FastAPI, Scrapy, Pandas, NumPy, 机器学习, 深度学习, TensorFlow, PyTorch, Docker, Kubernetes, Linux, Git, AWS, ] def extract_skills(text): 从岗位描述中提取技能关键词 if not isinstance(text, str): return [] found [] for skill in SKILLS: if skill.lower() in text.lower(): found.append(skill) return found这个函数简单但有效。注意大小写不敏感匹配因为“python”和“Python”在描述里都可能出现。技能词典可以根据分析目标扩充比如加上“爬虫”、“数据分析”、“可视化”这些中文词。城市字段的归一化更简单把“北京·朝阳区”、“北京-海淀”统一成“北京”。用split取第一个分隔符前的部分即可def normalize_city(city): if not isinstance(city, str): return 未知 for sep in [·, -, , ]: if sep in city: city city.split(sep)[0] return city.strip()清洗完成后把结果存成一张宽表字段包括岗位名称、公司、城市、最低月薪、最高月薪、平均月薪、技能列表、薪资月数。这张表就是后续所有可视化的数据源。注意技能提取不要用if skill in text做精确匹配因为“Java”会匹配到“JavaScript”。如果词典里有这类包含关系的词要么用正则加词边界要么把“JavaScript”单独列出来优先匹配。4. 可视化分析用 matplotlib 和 pyecharts 把结论画出来4.1 薪资分布与城市对比的静态图可视化第一步是看薪资分布。用 matplotlib 画直方图能直观看到大部分岗位集中在哪个区间。下面是一个完整的绘图脚本import pandas as pd import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams[font.sans-serif] [SimHei] matplotlib.rcParams[axes.unicode_minus] False df pd.read_csv(jobs_clean.csv) df df.dropna(subset[avg_salary]) df df[df[avg_salary] 100000] # 过滤异常高薪 fig, axes plt.subplots(1, 2, figsize(14, 5)) # 薪资直方图 axes[0].hist(df[avg_salary], bins30, color#4C72B0, edgecolorwhite) axes[0].set_xlabel(平均月薪元) axes[0].set_ylabel(岗位数量) axes[0].set_title(Python 岗位薪资分布) axes[0].axvline(df[avg_salary].median(), colorred, linestyle--, labelf中位数 {int(df[avg_salary].median())}) axes[0].legend() # 城市平均薪资 Top 10 city_salary df.groupby(city)[avg_salary].mean().sort_values(ascendingFalse).head(10) axes[1].barh(city_salary.index[::-1], city_salary.values[::-1], color#55A868) axes[1].set_xlabel(平均月薪元) axes[1].set_title(城市平均薪资 Top 10) plt.tight_layout() plt.savefig(salary_analysis.png, dpi150) plt.show()关键参数bins30控制直方图柱子数量太少看不出分布太多显得杂乱axvline画中位数参考线比只看平均值更稳健figsize(14, 5)保证两个子图并排时不挤。matplotlib.rcParams那两行是解决中文显示和负号显示问题的不设的话标题会变成方框。城市对比用横向柱状图因为城市名较长横向排列更易读。sort_values(ascendingFalse).head(10)取薪资最高的 10 个城市再用[::-1]反转让最高的城市显示在最上面。4.2 技能词云与交互式仪表盘技能关键词适合用词云展示。wordcloud库配合 matplotlib 可以快速出图from wordcloud import WordCloud from collections import Counter all_skills [] for skills in df[skills].dropna(): all_skills.extend(eval(skills)) # skills 列存的是列表字符串 counter Counter(all_skills) wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, width800, height400, background_colorwhite, max_words50, ) wc.generate_from_frequencies(counter) wc.to_file(skills_wordcloud.png)font_path必须指定中文字体路径否则中文会显示成方块。max_words50控制显示前 50 个高频技能。generate_from_frequencies直接接受{技能: 次数}字典比传大段文本更可控。如果需要交互式图表pyecharts 是更好的选择。它能生成 HTML 文件鼠标悬停显示数值适合做汇报from pyecharts.charts import Bar, Pie from pyecharts import options as opts city_counts df[city].value_counts().head(10) bar ( Bar() .add_xaxis(city_counts.index.tolist()) .add_yaxis(岗位数量, city_counts.values.tolist()) .set_global_opts( title_optsopts.TitleOpts(title城市岗位数量 Top 10), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate30)), ) ) bar.render(city_jobs.html)pyecharts 的链式调用写起来顺手rotate30解决城市名重叠问题。生成的 HTML 可以直接用浏览器打开也可以嵌入到报告里。提示词云图好看但信息密度低正式分析报告里建议把词云和柱状图配合使用。柱状图看精确排名词云看整体印象。5. 避坑与排查招聘数据采集分析中最容易翻车的 5 个点5.1 翻页到一半被封 IP现象前几页正常返回第 5 页之后全部超时或返回 403。原因请求频率过高或者没有携带 Referer、Cookie 等上下文信息被风控识别为爬虫。解决把time.sleep间隔调到 35 秒加上Referer头指向列表页必要时先访问首页获取 Cookie 再带上去。如果仍然被封换用移动端 User-Agent 试试移动端页面结构更简单风控也相对松。5.2 薪资解析结果出现 0 或负数现象清洗后的avg_salary列里有 0、负数或者几百万的异常值。原因正则匹配到了“经验不限”、“0-20人”这类非薪资数字或者“元/天”折算时乘错了系数。解决在解析函数里加前置过滤只对包含“K”、“万”、“元/天”的字符串做解析。解析完成后用df df[(df[avg_salary] 1000) (df[avg_salary] 200000)]做一次合理区间过滤。5.3 技能提取把 JavaScript 算成 Java现象技能统计里 Java 的出现次数异常高但实际岗位是 Python 方向。原因Java in JavaScript返回 True简单字符串匹配无法区分。解决把技能词典按长度降序排列先匹配长的。或者用正则\bJava\b加词边界但要注意中文文本里\b的行为可能不符合预期。最稳妥的做法是维护一个排除列表匹配到“JavaScript”时跳过“Java”。5.4 中文图表显示方块现象matplotlib 画出来的图标题和坐标轴中文全是方框。原因默认字体不支持中文。解决在绘图前设置matplotlib.rcParams[font.sans-serif] [SimHei]并确保系统里有 SimHei 字体。Linux 环境下如果没有 SimHei可以换成[WenQuanYi Micro Hei]或下载字体文件后用font_manager注册。5.5 详情页采集速度过慢现象几千条岗位详情抓了几个小时还没完。原因串行请求 每次 sleep 24 秒总耗时是线性累加的。解决用concurrent.futures.ThreadPoolExecutor做并发但并发数控制在 35 个线程每个线程仍然保持 sleep。不要用多进程因为瓶颈在网络 IO 不在 CPU。如果目标站点有 API 接口优先走接口速度比解析 HTML 快一个数量级。6. 从分析结果到可复用报告几个让数据真正有用的技巧做完基础可视化很多人就停了。但一份能拿得出手的分析报告还需要多做几步。我自己的习惯是先看薪资中位数而不是平均值因为招聘数据里总有极端高薪拉高均值再看技能共现关系比如“Python SQL”同时出现的比例比单独统计每个技能更有说服力最后按经验要求分层把“应届”、“1-3年”、“3-5年”分开画薪资箱线图结论会清晰很多。技能共现可以用简单的组合统计from itertools import combinations from collections import Counter pair_counter Counter() for skills in df[skills].dropna(): skill_list eval(skills) for pair in combinations(sorted(skill_list), 2): pair_counter[pair] 1 top_pairs pair_counter.most_common(15) for (a, b), count in top_pairs: print(f{a} {b}: {count})这段代码用combinations生成技能两两组合统计出现次数。sorted保证“Python SQL”和“SQL Python”算同一组。输出结果能直接告诉你哪些技能组合最受招聘方青睐。另一个实用技巧是导出 Excel 时按城市分 sheetwith pd.ExcelWriter(jobs_report.xlsx) as writer: df.to_excel(writer, sheet_name全部岗位, indexFalse) for city in df[city].value_counts().head(5).index: city_df df[df[city] city] city_df.to_excel(writer, sheet_namecity[:20], indexFalse)sheet_name有 31 字符限制所以用city[:20]截断。这样 HR 或求职者打开 Excel 就能按城市快速筛选比给一个几万行的总表友好得多。验证分析结果是否靠谱有一个简单方法随机抽 10 条岗位手动打开原链接核对薪资和技能要求看解析结果是否一致。如果准确率低于 90%说明解析规则需要调整。这个步骤花不了十分钟但能避免整份报告建立在错误数据上。最后说一个我踩过的坑不要一次性把全部数据都爬完再分析。先爬 200 条跑通全流程确认字段解析、清洗、可视化都没问题再扩大采集量。我早期做类似项目时花了两天爬了五万条结果发现薪资字段解析全错只能删库重来。先小后大是数据项目最省时间的习惯。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
MySQL锁优化与超时参数的核心差异与实践 1. 锁优化与超时参数的本质差异当MySQL遇到锁等待问题时,很多DBA的第一反应是调大innodb_lock_wait_timeout参数。这个默认50秒的超时设置确实能暂时缓解问题,但就像用止痛药治疗骨折——它掩盖了症状却治标不治本。我在处理电商秒杀系统时曾遇到典型案例… · 2026/11/13 18:49:00
复杂系统管理:从工程师到园丁的范式转变 1. 复杂系统管理的范式转变:从工程师到园丁当智能体规模突破临界点,传统的"工程师式"管理方法开始失效。我曾参与过一个金融风控系统的升级项目,当规则引擎中的策略从300条扩展到3000条时,团队突然发现:每次… · 2026/11/13 18:49:00
徐州在哪补电工证 上岗必备的实操考试考什么 徐州在哪补电工证 上岗必备的实操考试考什么 你是不是学历不高,担心自己报不了电工证?别急,今天咱们就来聊聊【徐州在哪补电工证】这事儿,看看这个 上岗必备… · 2026/11/13 18:48:54
钦州电工证在哪里年审通过率揭秘:怕考不过白交培训费? 钦州电工证在哪里年审通过率揭秘:怕考不过白交培训费? 你怕考不过白交培训费?现在是时候搞清楚钦州电工证在哪里年审,还有通过率揭秘了!很多人因为不了解流程和政策,结果钱花了,证没拿到,甚至年审也搞不清楚,白白浪费时间和金钱。今天咱们就围绕【钦… · 2026/11/13 18:48:54
宁明县办理电工证良心建议:学历不高也能顺利报考的干货全解析 宁明县办理电工证良心建议:学历不高也能顺利报考的干货全解析 你是不是也担心自己学历不高,报不了电工证?别慌,这确实是很多人的 真实痛点 ,尤其是那些想从零开始进入电工行业的朋友。今天,我就从 宁明县办理电工证 的实际情况出发,结合… · 2026/11/13 18:48:54
淄博新星技校电工证怎么转?电子证书全国通用吗? 淄博新星技校电工证怎么转?电子证书全国通用吗? 之前考的证在外省能不能转过来?这个问题是很多正在考虑报考淄博新星技校电工证的学员最关心的事。特别是之前在外地考的证书,现在想在山东地区继续使用,或者转岗、换地区工作,是否还能继续有效?答案是,… · 2026/11/13 18:48:54
2019质监局特种工证书值不值得考?跨省转证全流程详解 2019质监局特种工证书值不值得考?跨省转证全流程详解 之前考的证在外省能不能转过来?这个问题困扰了太多人,尤其是像你这样打算在河南安阳发展,但之前在外地考了特种作业证的朋友。2019年质监局特种工证在如今仍然具有实际价值,但很多人不知道怎… · 2026/11/13 18:47:25
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/12 14:09:01
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/13 5:40:45
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/12 23:27:43
维修电公用考电工证吗 在哪考最靠谱 维修电公用考电工证吗 在哪考最靠谱 工地太忙根本没时间复习考试,你是维修电工,天天在项目上跑,连抽个空背个题都难。那问题来了—— 维修电公用考电工证吗 ?答案是: 必须考 。尤其是在当前施工行业,没有电工证,连进场都可能被拦下。而且,… · 2026/11/13 0:00:08
电工证是不是可以登高?含金量和证书范围你必须知道的事 电工证是不是可以登高?含金量和证书范围你必须知道的事 证书过期了不知道怎么复审延期,这事儿很多人遇到过,尤其是像你这样在郑州金水区做劳务班组负责人,手里握着几十个工人的证书,稍微一疏忽,就可能影响工程进度和公司信誉。特别是电工证是不是可以登… · 2026/11/13 0:00:08
鸡西电工证哪家下证快官方报名入口怎么找 鸡西电工证哪家下证快官方报名入口怎么找 你是不是也担心 学历不高怕报不上名 ?别急,今天我就来跟你说说, 鸡西电工证哪家下证快 ,还有 官方报名入口 到底怎么找,别让那些“代办”“代考”耽误了你的机会。… · 2026/11/13 0:00:08
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 6:12:21
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/13 6:11:51
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/12 21:15:21