news 2026/11/2 14:45:11

Python实战:爬虫抓取招聘数据到可视化大屏的完整分析流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python实战:爬虫抓取招聘数据到可视化大屏的完整分析流程

简介:面向计划入门Python爬虫与数据分析的学习者,这份北京市大数据岗位招聘数据分析及可视化项目,提供了从数据采集、清洗分析到前端展示的完整参考链路。项目以Boss直聘岗位信息为对象,演示Scrapy/requests发起抓取、BeautifulSoup/lxml解析页面、pandas完成缺失值处理与分组聚合、matplotlib/seaborn绘制分布图表等关键操作,并配套生成可视化图表与可交互页面,可直接观察招聘市场地域、薪资、经验需求的分布规律。压缩包共111个文件,涵盖py爬虫与处理脚本、csv/db数据文件、html/css/js可视化页面及xls结果表,另含Bootstrap、图标库等静态资源,整体6.58MB,工程结构完整,方便用IDE打开逐模块阅读。目前已有727人学习下载,对希望通过真实案例串起爬虫、数据分析与可视化知识点,或需要课程设计、毕业设计参考的开发者很有价值。

1. 为什么拆这份Python招聘数据分析源码:从爬虫到可视化大屏的一次完整闭环

这份基于Python实现的北京市大数据岗位招聘分析项目,不是那种只丢给你一堆代码、跑起来全是报错的源码包,而是一整套能直接转起来的数据链路:用爬虫从Boss直聘上抓岗位数据,pandas做清洗和分析,最后落到一个Bootstrap搭出来的可视化大屏上。我拆完的最大感受是,它把数据分析岗日常要做的采集、清洗、分析、展示四件事压缩成了一个可复现的完整项目。正在学Python数据分析、想练爬虫实战,或者准备数据岗面试作品的人,照着这份源码走一遍,要比看十篇零散教程有用。下面按我实际跑通的顺序写:先抓数据,清洗,再分析出图,把坑都列出来,最后给一条可重复执行的进阶路径。

2. 数据采集:用requests和BeautifulSoup抓北京大数据岗位,先处理反爬再谈数据量

这套项目里最容易被新人卡住的就是第一步:数据到底怎么进到本地CSV的。很多人在网上看到Boss直聘的职位页,觉得结构复杂、字段藏在弹窗里,直接就想上selenium去模拟浏览器。但在这个项目里我验证下来,requests加BeautifulSoup就够了,而且更轻、更好调,出问题也更好排查。

2.1 先看项目结构:哪些文件要动,哪些文件不要动

把源码包解压后,先别急着跑,按目录把文件分三类看清楚。第一类是爬虫相关,一般是spider.py或者boss_spider.py,这是入口,里面写了搜索关键词和URL模板;第二类是数据文件,比如jobs.csv、cleaned_jobs.csv,第一次跑之前可能不存在,跑完才会生成;第三类是展示相关的静态资源,像bootstrap.css、bootstrap.min.css、icofont.min.css、animate.css、boxicons.min.css、aos.css这一串,都是可视化页面要用的前端库,正常情况下你不需要去改它们,更别因为加载不出来就删掉。

我一般会先用文本编辑器打开爬虫脚本,确认三件事:目标URL是不是北京地区的大数据岗位搜索页,headers里有没有写User-Agent,以及最后保存文件名是什么。这三处确认完,再把数据文件和展示目录单独看一眼,心里有数之后才开始跑。原因是爬虫这步一旦跑偏,后面所有分析都是在错误数据上做文章。

2.2 列表页抓取核心代码与参数说明

Boss直聘的列表页,抓取思路是先发一个GET请求拿到HTML,再用BeautifulSoup定位职位卡片节点。下面是这个项目里我跑通的简化版核心代码,去掉了一些项目路径相关的配置,保留了主流程。

import requests from bs4 import BeautifulSoup import time import csv HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.5", "Referer": "https://www.zhipin.com/" } def fetch_list_page(url): resp = requests.get(url, headers=HEADERS, timeout=10) resp.encoding = resp.apparent_encoding print("HTTP状态码:", resp.status_code, "页面长度:", len(resp.text)) return resp.text def parse_list_page(html): soup = BeautifulSoup(html, "html.parser") items = [] # 这里的选择器以源码包里的注释为准,不同页面版本 class 名会有差异 for card in soup.select(".job-card-wrapper"): title_node = card.select_one(".job-name") salary_node = card.select_one(".salary") company_node = card.select_one(".company-name") if title_node and salary_node: items.append({ "岗位": title_node.get_text(strip=True), "薪资": salary_node.get_text(strip=True), "公司": company_node.get_text(strip=True) if company_node else "", }) return items

这里有两个参数值得注意。第一个是resp.apparent_encoding,它通过页面文本自动猜测编码,用来规避中文乱码,比直接写死utf-8要稳。第二个是HEADERS里的Referer,很多招聘站会校验请求来源,带上它能少触发一道风控。你把这个脚本跑通之后,再考虑分页问题。Boss直聘的分页是通过URL里的page参数控制的,写一个循环,每翻一页time.sleep(2)到3秒,比一次性把几十页全部拉下来安全得多。

2.3 详情页字段补全:抓不到就先跳过,别让单条报错毁掉整批数据

列表页能拿到岗位名称、薪资、公司名这三个核心字段,但做数据分析只有这些远远不够。职位描述、经验要求、学历要求、技能关键词都在详情页里。这个项目的做法是先从列表页拿到每个岗位的详情链接,再逐个请求详情页补全字段。这里有个关键设计:字段缺失时用空字符串兜底,而不是直接抛异常中断整个爬虫。因为实际情况里总有几条岗位页面结构不一致,或者被风控拦截,一条报错就让全批重跑,代价太高。

def fetch_job_detail(detail_url): try: resp = requests.get(detail_url, headers=HEADERS, timeout=8) resp.encoding = resp.apparent_encoding soup = BeautifulSoup(resp.text, "html.parser") desc_node = soup.select_one(".job-sec-text") exp_node = soup.select_one(".job-banner-info .info-list") return { "职位描述": desc_node.get_text("\n", strip=True) if desc_node else "", "经验学历": exp_node.get_text("|", strip=True) if exp_node else "", } except Exception as e: print("详情页抓取失败:", detail_url, e) return {"职位描述": "", "经验学历": ""}

这段代码里的get_text("\n", strip=True)是把描述里多行文本用换行符拼起来,保留段落结构,方便后面做技能关键词统计。try...except包住整段请求,失败时返回空字典,保证主循环不中断。我实际统计过,一个500条规模的采集任务,因为网络抖动或页面结构差异,大约会有3%到5%的详情页抓不到,这部分数据直接留空就好,放进清洗环节处理,比当场报错强。

数据下载完统一写入CSV时,注意用newline=""打开文件,避免Windows下多出一个空行。爬虫这一步做到这里就够了,不要贪多。有人问我为什么不用scrapy,这个项目requests已经能覆盖需求,scrapy调度能力强但配置成本高,新手在这个体量下用requests反而更容易理解整个流程。

3. 数据清洗:把“20-40K·13薪”拆成可计算的字段,顺便收拾缺失值

爬虫落库只是原始数据,离能分析还差一大步。我刚拿到这个项目的数据文件时第一反应是,CSV里那些薪资字段根本没法直接计算。“30-50K·16薪”“250-300元/天”“面议”混在一列里,pandas的mean()算不出来。所以数据清洗是这个项目第二个重头戏,也是面试官最喜欢深挖的部分。

3.1 原始数据到底长什么样:先跑describe和info再动手

拿到CSV后第一件事不是写清洗逻辑,而是先看数据的长相。我会在项目根目录打开一个Python终端,执行下面这几行:

import pandas as pd df = pd.read_csv("jobs.csv") print(df.shape) print(df.columns.tolist()) print(df.head(10).to_string()) print(df.info())

df.shape告诉你总共有多少行、多少列。info()会列每一列的非空值数量,空值数量就是两者差值。这一步能快速判断哪些列缺失严重,哪些列格式统一,哪些列得重点清洗。在我拆的这个项目里,最脏的永远是薪资、经验、学历这三列。薪资混杂“K”和“元/天”两种单位,经验有“1-3年”“在校/应届”“不限”三种形态,这些都要在清洗阶段统一口径。

3.2 薪资字段拆分:正则兼容K、k、元/天、面议的写法

薪资拆分的核心是用正则把字符串里的数字区间抠出来,再统一单位。下面是项目里清洗薪资的简化代码,它处理了大小写K混用、元/天换算、面议兜底三种情况。

import re def parse_salary(text): if not isinstance(text, str): return None, None text = text.upper().replace(" ", "") m = re.search(r"(\d+(?:\.\d+)?)[-~](\d+(?:\.\d+)?)K", text) if m: return float(m.group(1)), float(m.group(2)) m_day = re.search(r"(\d+(?:\.\d+)?)[-~](\d+(?:\.\d+)?)元/天", text) if m_day: # 按每月21.75个工作日换算成月薪 d1, d2 = float(m_day.group(1)), float(m_day.group(2)) return round(d1 * 21.75, 1), round(d2 * 21.75, 1) if "面议" in text: return None, None return None, None df["最低月薪"], df["最高月薪"] = zip(*df["薪资"].apply(parse_salary)) df["平均月薪"] = df[["最低月薪", "最高月薪"]].mean(axis=1)

这段代码里值得注意的有两点。第一是先统一upper()再用正则,这样K和k都能被匹配到,避免同一个薪资写法拆出两种结果。第二是“元/天”的岗位按21.75个工作日换算成月薪,这样“20-40K·13薪”和“250-300元/天”才能放进同一个坐标系比较。实际跑的时候还会遇到“20-40K·13薪”这种带年终奖的写法,主正则匹配完数字区间后,“13薪”会被忽略掉,这不算错,因为月薪对比本身已经能反映水平,年终奖信息可以在后续分析里单独统计。如果你以后想算年薪,可以再写一个parser_bonus函数把“X薪”抽出来乘进去。

3.3 经验、学历、技能标签的标准化

经验字段“在校/应届”“1-3年”“3-5年”“5-10年”“不限”这几种写法,直接做分组统计时顺序是乱的,所以我一般会选择把它映射成数值区间:在校/应届记成0-1年,不限记成0-50年,或者干脆单独设一个“不限”类别。学历字段类似,把“本科及以上”“统招本科”“大专”归一化成“本科”“大专”“硕士”这样的粗粒度。这一步的意义不是简化数据,而是让后续分组分析的口径统一,不然同一家公司岗位的学历要求会分裂出七八种写法。

技能标签的提取我放在清洗之后。爬虫拿到的职位描述是整段文本,清洗阶段先不拆词,等分析阶段再用jieba或直接按标点切分统计高频词。这里只需要保证描述文本是干净的纯文本,没有HTML标签,没有乱码字符,就足够后续用了。

3.4 清洗结果落盘:为什么CSV要用utf-8-sig

清洗完成的数据我习惯写成新文件,不覆盖原始爬虫数据,目的是保留溯源能力。任何一列清洗逻辑出错,回头还能拿原始数据重新跑一遍。保存时用to_csv指定编码为utf-8-sig,这一点是血泪教训换来的。

df_clean = df.dropna(subset=["岗位", "公司"]) df_clean.to_csv("cleaned_jobs.csv", index=False, encoding="utf-8-sig")

utf-8-sig和utf-8的区别在于文件开头会写入BOM头。BOM头能让Excel打开CSV时自动识别成UTF-8编码,不出现中文乱码。如果你只追求代码在终端里能跑,utf-8完全够用,但数据交到非技术同事手里,大概率打开就是乱码。项目里的展示大屏如果直接从CSV读数据,这个细节就更重要,因为浏览器和Excel对编码的默认处理方式不一样。保存完CSV,用df_clean.describe()看一眼平均月薪的最小值、最大值和分位数,确认没有出现年薪千万这种离谱值,清洗这步才真正结束。

4. 数据分析与可视化展示:从groupby聚合到Bootstrap大屏

数据洗到可计算的状态,接下来就是把这个项目最有看头的部分做出来:回答几个具体问题,然后把答案画成图,拼成可视化大屏。很多初学者在一开始就沉迷调整图表样式,我的习惯是先算清楚再画图,统计口径不对,图再漂亮也是错的。

4.1 用pandas算清楚三个核心问题:谁薪资高、要什么经验、要什么技能

围绕这份北京市大数据岗位数据,值得回答的问题有几个:不同学历对应的平均月薪是多少;不同经验区间的岗位数量分布如何;哪些技能在职位描述里出现频率最高。下面这段代码集中处理这三个问题。

# 学历与薪资关系 edu_salary = df_clean.groupby("学历")["平均月薪"].agg(["mean", "median", "count"]) edu_salary = edu_salary.sort_values("mean", ascending=False) print(edu_salary) # 经验与岗位数量关系 exp_count = df_clean["经验区间"].value_counts() # 技能关键词频次 from collections import Counter def extract_skills(text, skill_list): if not isinstance(text, str): return [] return [s for s in skill_list if s in text] SKILLS = ["Python", "Java", "SQL", "Hadoop", "Spark", "Flink", "Hive", "数据挖掘", "机器学习", "深度学习"] skill_counter = Counter() for desc in df_clean["职位描述"]: for s in extract_skills(desc, SKILLS): skill_counter[s] += 1 print(skill_counter.most_common(10))

groupby("学历")["平均月薪"].agg(["mean", "median", "count"])一次把均值、中位数、样本量都算出来。为什么均值和中位数一起看?因为均值容易被个别高薪岗位拉高,中位数更稳健。如果两者差距太大,说明学历群体的薪资分布偏态严重,写分析结论时就应该用中位数而不用均值。技能频次统计用的是简单子串匹配,不是语义识别,好处是快、可控、不会引入模型误差,缺点是省不掉的,比如“Python开发”和“Python数据分析”会重复计数。这个项目里用这种方案够用,真要搞精准抽取就得用词向量或者标注数据了,先不必复杂化。

4.2 matplotlib出图:先出四张基础图,再决定做不做大屏

统计结果要变成可读的图,我先把基础四件套画出来:学历薪资柱状图、经验要求数量柱状图、技能频次横向条形图、平均月薪分布直方图。以下给出学历薪资柱状图的代码,其余图只是换数据源和图表类型,套路完全一致。

import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] plt.rcParams["axes.unicode_minus"] = False fig, ax = plt.subplots(figsize=(10, 6)) # 中文标签在matplotlib里的处理方式:中文字体做前后端分离 ax.bar(edu_salary.index, edu_salary["mean"], color="#2c7fb8") ax.set_title("北京大数据岗位:不同学历平均月薪(单位:K)") ax.set_xlabel("学历") ax.set_ylabel("平均月薪(K)") for i, v in enumerate(edu_salary["mean"]): ax.text(i, v + 0.5, round(v, 1), ha="center") plt.tight_layout() plt.savefig("fig_edu_salary.png", dpi=150) plt.close()

plt.rcParams["font.sans-serif"]指定中文字体,不然图上所有中文会显示成方块,这是matplotlib最常见的坑。我一般固定用SimHei加Microsoft YaHei两个备选,Linux服务器上没有这两个字体时再换成Noto Sans CJK。注意画完要执行plt.close()释放内存,否则在循环里连续出图会内存堆积,后面做批量出图时特别容易踩到。

四张基础图出来之后,再决定要不要做可视化大屏。如果只是自己分析数据,四张静态图已经够回答了核心问题。但这个项目的亮点是它自带Bootstrap系的CSS和AOS动画库,明显是奔着做展示页去的。所以下一步就是把统计结果接进一个网页大屏。

4.3 把统计结果接进可视化大屏:Bootstrap布局 + AOS动画的接法

可视化大屏本质上是一个静态HTML页面,数据不是实时通信获取的,而是爬虫和分析跑完后的快照。我查看了源码包里的CSS文件清单,bootstrap.css负责布局栅格,icofont和boxicons提供图标,animate.css和aos.css负责进场动画。前端逻辑上,最稳妥的做法是把4.1里算出的统计结果直接嵌入HTML片段。

<div class="container">import subprocess import sys steps = [ ("spider.py", "爬取阶段"), ("clean.py", "清洗阶段"), ("analyze.py", "分析出图阶段"), ("render_dashboard.py", "渲染大屏阶段"), ] for script, desc in steps: print(f"[{desc}] 执行 {script} ...") ret = subprocess.run([sys.executable, script]) if ret.returncode != 0: print(f"{script} 执行失败,终止流程") break print("全流程结束,验证 report_data.json 更新时间即可确认是否刷新")

这段脚本用subprocess.run按顺序执行四个阶段的独立脚本,每一步返回码非零就中断,避免带病往下跑。为什么强调独立脚本而不是一个大脚本?因为爬虫失败时,清洗和分析不该用旧数据硬跑一遍给一个假结果。拆开后每步可以单独调试,也可以定时任务里只重跑爬虫阶段。

验证结果可信度,我有两个习惯。第一是抽样对比线上数据,从大屏上随机挑几个公司和岗位,回到招聘网站搜索页面核对薪资区间和经验要求是否一致,抽样十条以上再下结论。第二是核对统计口径,比如学历平均月薪是按均值还是中位数,报告中要写清楚,不然别人复现时看到数字对不上,会以为代码错了。这两步走完,这份源码才真正从“别人的项目”变成你自己能掌控的数据分析作品。从那以后我每次拿到爬虫加可视化的项目,都强制走一遍“字段全、空值可查、口径可溯”三步,确认能落地才跑数据。这个项目让我把这三步练扎实了,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 14:45:01

VoiceStudio:一文读懂AI配音工作流与声音克隆实战

做内容的人这几年的一个共同痛点&#xff0c;就是配音成本。请真人录&#xff0c;按字算钱&#xff0c;改一次稿等于重录一版&#xff1b;自己硬着头皮拿手机录&#xff0c;又是喷麦又是口胡&#xff0c;光剪辑就能耗掉一下午。VoiceStudio是我最近一直在打磨的一套AI语音处理工…

作者头像 李华
网站建设 2026/11/2 14:44:50

hp自适应伪谱法求解绳系最优控制:原理、工程参数与避坑指南

简介&#xff1a;这份压缩包提供了一套基于hp-自适应伪谱法的MATLAB源码&#xff0c;用于求解系绳&#xff08;tether&#xff09;系统的最优控制问题&#xff0c;适合航空航天、无人机、风筝缆绳等领域的研究者与工程师。伪谱法先将连续时间最优控制问题离散化为非线性规划&am…

作者头像 李华
网站建设 2026/11/2 14:44:13

GitHub开源项目高效筛选与评估方法论

1. 项目概述&#xff1a;GitHub开源项目盘点方法论作为开发者社区最活跃的代码托管平台&#xff0c;GitHub每月都会涌现大量优质开源项目。但面对海量项目&#xff0c;如何高效挖掘真正有价值的"宝藏"&#xff1f;我总结了一套经过验证的筛选方法论&#xff1a;数据采…

作者头像 李华
网站建设 2026/11/2 14:43:28

本地AI语音合成实战:从训练到交付全流程指南

搞语音内容这些年&#xff0c;我最大的感受是&#xff1a;真正影响交付效率的往往不是录音设备有多贵&#xff0c;而是“改稿重录”这件事有多耗人。一句话语气不对、一个词读错了、客户临时要换一版口播稿&#xff0c;就得把配音老师请回来重来一遍。直到我开始用VoiceStudio这…

作者头像 李华
网站建设 2026/11/2 14:43:26

UIAutomatorViewer工具解析与Android UI自动化测试实战

1. UIAutomatorViewer工具解析与实战指南在移动应用测试领域&#xff0c;UI自动化测试已成为质量保障的重要环节。作为Android官方测试套件中的核心工具&#xff0c;UIAutomatorViewer为测试人员提供了可视化分析应用界面结构的强大能力。不同于需要编写脚本的自动化测试框架&a…

作者头像 李华
网站建设 2026/11/2 14:43:25

Linux网络编程:TCP/UDP套接字详解与实践

1. 套接字基础概念与网络通信模型套接字(Socket)是Linux网络编程中最核心的概念之一&#xff0c;它本质上是一个通信端点&#xff0c;可以理解为网络数据传输的"插座"。在实际开发中&#xff0c;我们通过创建套接字来建立网络连接、发送和接收数据。1.1 套接字的基本…

作者头像 李华