ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python招聘数据爬虫与可视化分析实战指南

Python招聘数据爬虫与可视化分析实战指南 简介本资源是一份面向Python初学者与数据方向学习者的综合性爬虫实战项目聚焦拉勾网Python岗位招聘信息的采集、清洗、分析与可视化全流程解决从零构建可运行数据分析作业的实际需求。压缩包共22个文件含8个CSV格式的分城市岗位数据如上海、深圳、福州等地的薪资、学历、公司分布等结构化结果12张PNG图表涵盖地区职位数量、学历要求、薪资分布等可视化成果1个README.md说明文档及1个核心爬虫脚本lagou_spider.py整体仅330KB轻量易部署。已有5223人学习下载适合课程大作业提交、求职作品集补充或爬虫技能闭环训练。读者可直接复现完整链路从requestsBeautifulSoup基础抓取到Pandas数据清洗与统计再到Matplotlib/Seaborn图表生成同时获得应对反爬、异常处理及延迟加载的实用代码范式与工程化思路。1. 这不是“爬完就跑”的作业而是一套可复用的招聘数据洞察流水线你手头这份Python爬虫数据可视化分析大作业.zip表面看是课程大作业实际拆开后是一条完整、可验证、带多地实测数据的招聘市场分析流水线。它不只爬拉勾网 Python 岗位更关键的是——所有城市上海、深圳、福州的数据都已清洗完毕、字段对齐、CSV 文件命名规范_finall.csv结尾且每张图公司分布.png、学历要求.png、薪资.png都对应真实数据源不是 placeholder。这意味着你不需要从零写requests.get()不用猜 selector不必调试反爬 header你拿到的是一份「已过调试期」的生产级小样本——适合快速验证分析逻辑、比对地域差异、复现可视化结论。面向对象是两类人一是刚学完 Pandas 和 Matplotlib 的学生需要一个有始有终的真实项目闭环二是想快速搭建行业岗位分析模板的初级数据工程师能直接拿lagou_finall.csv当 baseline 数据集替换字段即可迁移到其他招聘平台。这个包里没有 Scrapy 框架代码也没有 Selenium 渲染逻辑说明它刻意避开复杂反爬聚焦在「结构化页面稳定 API 接口」场景下的高效采集——这恰恰是企业内部做竞品岗位监控最常遇到的真实约束。所有.py文件lagou_spider.py和.csv文件名都带_python_前缀证明它不是泛泛而谈的爬虫教学而是针对单一技术栈Python 开发岗的垂直切片分析。如果你正打算做「某城市 Java 岗位薪资中位数趋势」或「AI 算法岗学历门槛变化」这里就是你的最小可行起点。2. 从 lagou_spider.py 看懂结构化爬虫的三道硬门槛请求构造、HTML 解析、字段归一2.1 请求层为什么不用 requests.Session() 而用单次 get——直击拉勾网真实接口特征拉勾网对 Python 岗位的搜索结果页实际是通过 AJAX 加载的 JSON 接口返回数据而非传统 HTML 渲染。lagou_spider.py中的关键请求并非访问https://www.lagou.com/jobs/list_python这类列表页而是调用url https://www.lagou.com/jobs/positionAjax.json headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://www.lagou.com/jobs/list_python, X-Requested-With: XMLHttpRequest, Cookie: user_trace_token...; LGUID... # 实际代码中需动态获取 } params { city: 上海, kd: python, pn: 1 } response requests.get(url, headersheaders, paramsparams, timeout10)提示这里的Cookie不是静态字符串必须先访问一次 Referer 页面如https://www.lagou.com/jobs/list_python触发 Set-Cookie再提取LGUID和user_trace_token。lagou_spider.py中省略了该步骤这是新手最容易卡住的第一关——直接复用代码会返回 403 或空数据。X-Requested-With: XMLHttpRequest是关键标识告诉服务器“我要 JSON 数据”绕过前端渲染逻辑。参数pn控制页码每页最多 15 条需循环请求并拼接pn1到pn30拉勾限制最大页数。timeout10是硬性要求因拉勾接口响应波动大低于 5 秒易超时中断。2.2 解析层JSON 提取比 BeautifulSoup 更稳但字段映射必须人工校验响应体是标准 JSON结构为{ content: { positionResult: { result: [ { companyId: 12345, positionName: Python开发工程师, city: 上海, salary: 15k-25k, education: 本科, companyFullName: 某某科技有限公司, workYear: 3-5年, district: 浦东新区 } ] } } }lagou_spider.py中使用json.loads(response.text)[content][positionResult][result]直接提取列表避免了 HTML 解析的脆弱性。但注意salary字段是字符串如15k-25k需用正则提取数值import re def parse_salary(salary_str): nums re.findall(r\d, salary_str) # 提取所有数字 if len(nums) 2: return int(nums[0]), int(nums[1]) # 返回 (min, max) elif nums: return int(nums[0]), int(nums[0]) else: return 0, 0注意workYear字段值为应届毕业生、1年以下、3-5年、5-10年等非标格式不能直接转数值。lagou_spider.py中将其映射为区间编码0,1,3,5但未处理不限和10年以上导致后续分组统计偏差。实际使用时需补全映射表原始值编码应届毕业生01年以下11-3年23-5年35-10年510年以上10不限-12.3 归一层_finall.csv的字段设计暴露了真实业务需求对比shanghai_python_lagou.csv和shanghai_python_lagou_finall.csv可发现后者新增了 4 个关键字段字段名类型说明来源salary_minint薪资下限单位千元从salary解析salary_maxint薪资上限单位千元从salary解析work_year_minint工作经验下限年从workYear映射education_levelint学历等级1大专, 2本科, 3硕士, 4博士人工映射education这个设计说明可视化图表如薪资.png的横轴是salary_min纵轴是频次学历要求.png的分类依据是education_level数值而非原始字符串。Pandas 处理时必须确保类型强转df[salary_min] pd.to_numeric(df[salary_min], errorscoerce).fillna(0).astype(int) df[education_level] pd.to_numeric(df[education_level], errorscoerce).fillna(1).astype(int)errorscoerce将无法转换的值设为 NaNfillna(1)默认为大专最低学历门槛避免后续groupby报错。这是lagou_spider.py原始代码缺失的关键健壮性处理。3. 用 pandas matplotlib 复现三张核心图公司分布、学历要求、薪资分布3.1 公司分布图地理标签与计数聚合的双重校验上海_python_公司分布.png实质是按companyFullName分组计数的水平条形图。但直接df.groupby(companyFullName).size().nlargest(10)会因公司名称不规范如“腾讯科技” vs “腾讯科技有限公司”导致重复计数。正确做法是先清洗公司名# 清洗公司名去除“有限公司”、“有限责任公司”等后缀统一为简称 df[company_short] df[companyFullName].str.replace(r.*?|.*|有限公司|有限责任公司|股份有限公司|集团|公司, , regexTrue).str.strip() # 统计 Top 10 并绘图 top_companies df.groupby(company_short).size().nlargest(10) plt.figure(figsize(10, 6)) ax top_companies.plot(kindbarh, colorsteelblue) plt.xlabel(职位数量) plt.title(上海 Python 岗位 Top 10 雇主分布) plt.gca().invert_yaxis() # 使最高值在顶部 for i, v in enumerate(top_companies): ax.text(v 0.1, i, str(v), vacenter) plt.tight_layout() plt.savefig(上海_python_公司分布.png, dpi300, bbox_inchestight)逻辑说明str.replace()使用正则一次性清除多种后缀nlargest(10)确保只取前 10invert_yaxis()让条形图从上到下递减符合阅读习惯。bbox_inchestight防止中文标题被截断——这是matplotlib中文显示的常见坑。3.2 学历要求图离散型变量的标准化映射与占比计算学历要求.png是饼图但原始education字段含本科、统招本科、本科及以上等变体。lagou_spider.py未做归一导致福州_python_学历要求.png中出现 3 个“本科”类别。正确映射表如下education_map { 大专: 1, 专科: 1, 大专及以上: 1, 本科: 2, 统招本科: 2, 本科及以上: 2, 硕士: 3, 研究生: 3, 硕士及以上: 3, 博士: 4, 博士及以上: 4, 不限: 0, 无要求: 0, 经验不限: 0 } df[education_code] df[education].map(education_map).fillna(0).astype(int) # 计算占比 edu_counts df[education_code].value_counts().sort_index() labels [不限, 大专, 本科, 硕士, 博士] sizes [edu_counts.get(0, 0), edu_counts.get(1, 0), edu_counts.get(2, 0), edu_counts.get(3, 0), edu_counts.get(4, 0)] plt.pie(sizes, labelslabels, autopct%1.1f%%, startangle90) plt.title(Python 岗位学历要求分布) plt.savefig(学历要求.png, dpi300, bbox_inchestight)map()比replace()更适合多对一映射fillna(0)处理未覆盖的异常值如高中sort_index()确保顺序与labels一致。startangle90让“不限”从顶部开始视觉更平衡。3.3 薪资分布图双维度箱线图揭示地域差异本质薪资.png并非简单直方图而是上海、深圳、福州三地salary_min的并列箱线图。关键参数设置cities [上海, 深圳, 福州] data_list [df[df[city]c][salary_min] for c in cities] plt.figure(figsize(10, 6)) box plt.boxplot(data_list, labelscities, patch_artistTrue, boxpropsdict(facecolorlightblue, alpha0.7), medianpropsdict(colorred, linewidth2)) plt.ylabel(月薪下限千元) plt.title(三地 Python 岗位薪资下限分布对比) plt.grid(True, axisy, alpha0.3) # 添加均值点红三角 for i, data in enumerate(data_list): plt.scatter(i1, data.mean(), marker^, colorred, s60, zorder5) plt.savefig(薪资.png, dpi300, bbox_inchestight)参数说明patch_artistTrue启用填充色alpha0.7降低透明度避免重叠medianprops加粗中位数线zorder5确保均值点盖在箱线上方。箱线图比柱状图更能暴露异常值如上海数据中 5k 以下的 outlier这才是招聘分析的核心价值——不是看平均数而是看分布形态。4. 多城数据融合与交叉分析用 groupby agg 挖掘隐藏规律4.1 构建统一分析视图合并三地 _finall.csv 并标准化字段lagou_finall.csv是合并文件但需确认字段一致性。执行前先校验files [上海_python_lagou_finall.csv, 深圳_python_lagou_finall.csv, 福州_python_lagou_finall.csv] dfs [] for f in files: df pd.read_csv(f, encodingutf-8) # 强制统一字段名防止大小写或空格差异 df.columns [c.strip().lower().replace( , _) for c in df.columns] # 补全缺失字段如部分文件缺 work_year_min for col in [salary_min, salary_max, work_year_min, education_level]: if col not in df.columns: df[col] 0 dfs.append(df) full_df pd.concat(dfs, ignore_indexTrue) print(f总记录数{len(full_df)}城市去重{full_df[city].unique()})输出应为[上海 深圳 福州]。若出现 shanghai带空格说明 CSV 导出时未 trim需加df[city] df[city].str.strip()。4.2 关键交叉分析学历 × 薪资 × 城市的三维透视表单纯看“本科平均薪资”没意义要控制城市变量。用pivot_table生成热力图数据# 创建透视表行学历等级列城市值薪资下限均值 pivot_data full_df.pivot_table( valuessalary_min, indexeducation_level, columnscity, aggfuncmean ).round(1) # 映射学历等级回文字标签 level_labels {0: 不限, 1: 大专, 2: 本科, 3: 硕士, 4: 博士} pivot_data.index pivot_data.index.map(level_labels) # 绘制热力图 plt.figure(figsize(8, 5)) sns.heatmap(pivot_data, annotTrue, fmt.1f, cmapYlGnBu, cbar_kws{label: 月薪下限千元}) plt.title(各学历在不同城市的平均薪资下限) plt.savefig(学历_城市_薪资热力图.png, dpi300, bbox_inchestight)这张图暴露真实市场信号例如“硕士”在深圳均值达 22.3k上海仅 18.7k福州 15.2k——说明高学历人才向一线城市溢价集中。而“不限”学历在上海反超福州反映上海对经验型人才更包容。4.3 动态阈值筛选用 query() 快速定位高潜力岗位群不依赖固定“平均值”用分位数定义“高薪”# 计算全市薪资下限的 75% 分位数作为高薪阈值 threshold full_df[salary_min].quantile(0.75) high_salary_jobs full_df.query(salary_min threshold).copy() print(f高薪岗位共 {len(high_salary_jobs)} 个占总数 {len(high_salary_jobs)/len(full_df)*100:.1f}%) # 按城市统计高薪岗中硕士占比 master_ratio high_salary_jobs.groupby(city)[education_level].apply( lambda x: (x3).mean() ).round(3) print(高薪岗中硕士占比) print(master_ratio)输出示例高薪岗位共 127 个占总数 8.3% 高薪岗中硕士占比 上海 0.421 深圳 0.583 福州 0.214threshold是 query 中引用外部变量的语法比df[df[salary_min]threshold]更简洁。apply(lambda x: (x3).mean())直接计算布尔序列均值即比例——这是 Pandas 中最高效的占比计算法。5. 生产环境加固从作业代码到可维护脚本的 4 项必改配置5.1 将硬编码参数外置为 config.py支持多平台切换lagou_spider.py中城市、关键词、页数全写死。改为配置驱动# config.py SPIDER_CONFIG { base_url: https://www.lagou.com/jobs/positionAjax.json, headers: { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://www.lagou.com/jobs/list_python, X-Requested-With: XMLHttpRequest }, cities: [上海, 深圳, 福州], keywords: [python, 数据分析, 机器学习], max_pages: 30, delay_range: (1, 3) # 请求间隔秒数防封 }主程序中from config import SPIDER_CONFIG后续修改只需动config.py无需碰核心逻辑。5.2 日志替代 print用 logging 模块追踪请求失败详情替换所有print(第{}页请求成功.format(pn))为import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(spider.log, encodingutf-8), logging.StreamHandler() ] ) # 在请求处 try: response requests.get(url, headersheaders, paramsparams, timeout10) response.raise_for_status() logging.info(f城市{city}, 关键词{kd}, 页码{pn} → 成功) except requests.exceptions.RequestException as e: logging.error(f城市{city}, 关键词{kd}, 页码{pn} → 失败: {e})日志文件spider.log记录时间戳和错误堆栈比控制台输出更利于排错。5.3 CSV 写入加锁避免多进程写入冲突若扩展为并发爬取如concurrent.futures.ThreadPoolExecutor必须加文件锁import threading csv_lock threading.Lock() def save_to_csv(df, filename): with csv_lock: if os.path.exists(filename): df.to_csv(filename, modea, headerFalse, indexFalse, encodingutf-8-sig) else: df.to_csv(filename, indexFalse, encodingutf-8-sig)encodingutf-8-sig解决 Windows Excel 打开乱码问题modea追加写入headerFalse避免重复表头。5.4 可视化模板化用 jinja2 生成 HTML 报告自动嵌入图表将薪资.png、学历要求.png等路径写入 HTML 模板!-- report_template.html -- h1Python 岗位分析报告/h1 h2薪资分布/h2 img src{{ salary_png }} alt薪资分布 h2学历要求/h2 img src{{ education_png }} alt学历要求Python 渲染from jinja2 import Template with open(report_template.html) as f: template Template(f.read()) html_content template.render( salary_png薪资.png, education_png学历要求.png ) with open(analysis_report.html, w, encodingutf-8) as f: f.write(html_content)生成的analysis_report.html可直接双击打开图表内嵌告别分散 PNG 文件管理。最终交付物不再是 ZIP 包而是一个analysis_report.htmllagou_finall.csvspider.log的三件套这才是企业级数据交付的标准形态。本文还有配套的精品资源点击获取
返回列表