从爬虫到数据分析:用Pandas一键清洗你的Selenium爬取的8000条招聘数据(含常见脏数据处理)
从爬虫到数据分析用Pandas一键清洗你的Selenium爬取的8000条招聘数据含常见脏数据处理当你用Selenium爬取了8000条招聘数据兴奋地打开CSV文件时可能会被眼前的混乱景象吓到薪资字段写着15-25K地点混杂着北京·海淀区和上海-浦东公司描述里塞满了各种无关信息。别担心这篇文章将带你用Pandas从原始数据中提炼出真正有价值的信息。1. 原始数据质量诊断与预处理拿到爬虫数据后的第一步不是立即开始清洗而是全面了解数据的质量状况。让我们先加载数据并快速浏览import pandas as pd # 加载爬取的原始数据 df pd.read_csv(job_data_raw.csv, encodingutf-8) print(f数据总量{len(df)}条) print(\n前5条数据预览) display(df.head())典型的数据质量问题通常包括薪资字段15-25K、面议、10K以上等不一致格式地点信息北京·海淀区、上海-浦东新区、广州 等混杂格式公司描述混杂了融资情况、行业、规模等多项信息缺失值关键字段如薪资、工作地点的缺失情况异常值明显超出合理范围的数值如薪资1000-2000K数据质量检查清单各字段的缺失值比例文本字段的唯一值数量及示例数值字段的统计分布字段间的逻辑一致性检查# 各字段缺失值统计 missing_stats df.isnull().sum() / len(df) * 100 print(各字段缺失值比例) display(missing_stats.sort_values(ascendingFalse))2. 薪资字段的深度清洗与标准化薪资是招聘数据分析的核心字段但也是最混乱的字段之一。我们需要将其拆分为最低薪资和最高薪资两个数值型字段。2.1 薪资范围解析常见薪资格式及处理策略原始格式处理方式输出结果(min_salary/max_salary)15-25K提取数字并统一单位15000/2500020K以上取值为下限上限设为NaN20000/NaN面议设为NaNNaN/NaN10-15万/年转换成年薪再除以128333/12500def clean_salary(salary_str): if pd.isna(salary_str) or salary_str 面议: return pd.Series([None, None]) # 统一处理万/年的情况 if 万/年 in salary_str: salary_str salary_str.replace(万/年, ) multiplier 10000 / 12 else: multiplier 1000 if K in salary_str else 1 # 提取数字部分 numbers re.findall(r\d\.?\d*, salary_str) numbers [float(n) for n in numbers] if len(numbers) 2: return pd.Series([numbers[0]*multiplier, numbers[1]*multiplier]) elif 以上 in salary_str and len(numbers) 1: return pd.Series([numbers[0]*multiplier, None]) else: return pd.Series([None, None]) # 应用清洗函数 df[[min_salary, max_salary]] df[薪资].apply(clean_salary)2.2 薪资异常值检测清洗后需要检查薪资数据的合理性# 薪资合理性检查 salary_stats df[[min_salary, max_salary]].describe() print(清洗后薪资统计) display(salary_stats) # 标记异常值 df[salary_outlier] ((df[min_salary] 100000) | (df[max_salary] 200000) | (df[min_salary] df[max_salary]))3. 工作地点信息的结构化处理工作地点通常包含城市和区域信息我们需要将其分离并标准化。3.1 城市提取与标准化# 提取城市信息 def extract_city(location): if pd.isna(location): return None # 处理常见分隔符 city re.split(r[·\-—], location)[0] city city.replace( , ) # 城市名称标准化 city_mapping { 北京: 北京, 上海市: 上海, 广州: 广州, 深圳: 深圳, 杭州: 杭州, 成都: 成都 } return city_mapping.get(city, city) df[city] df[地点].apply(extract_city)3.2 区域信息提取# 提取区域信息 def extract_district(location): if pd.isna(location): return None parts re.split(r[·\-—], location) if len(parts) 1: district parts[1].replace(区, ).strip() return district if district else None return None df[district] df[地点].apply(extract_district)3.3 城市-区域分布分析清洗后可以生成城市分布统计city_distribution df[city].value_counts(normalizeTrue) * 100 print(城市分布比例) display(city_distribution.head(10))4. 公司信息的深度解析公司描述字段通常包含融资情况、行业、规模等混合信息需要分别提取。4.1 融资情况分类# 融资阶段分类 def classify_financing(desc): if pd.isna(desc): return 未知 financing_keywords { 天使轮: 天使轮, A轮: A轮, B轮: B轮, C轮: C轮, D轮: D轮, 上市公司: 上市公司, 未融资: 未融资, 国企: 国企 } for key, value in financing_keywords.items(): if key in desc: return value return 其他 df[financing_stage] df[公司描述].apply(classify_financing)4.2 公司规模提取# 提取公司规模 def extract_company_size(desc): if pd.isna(desc): return None size_pattern r(\d-\d人|\d人以上|\d人以下) match re.search(size_pattern, desc) return match.group(0) if match else None df[company_size] df[公司描述].apply(extract_company_size)4.3 行业信息提取# 提取行业信息 def extract_industry(desc): if pd.isna(desc): return None # 常见行业关键词 industries [互联网, 金融, 电子商务, 人工智能, 教育, 医疗, 游戏, 广告, 制造业, 服务业] for industry in industries: if industry in desc: return industry return 其他 df[industry] df[公司描述].apply(extract_industry)5. 数据验证与最终输出完成所有字段清洗后需要进行整体验证# 数据完整性检查 clean_stats pd.DataFrame({ 缺失比例: df.isnull().mean(), 唯一值数量: df.nunique() }) print(清洗后数据质量报告) display(clean_stats) # 保存清洗后的数据 df.to_csv(job_data_clean.csv, indexFalse, encodingutf-8-sig)5.1 数据质量提升技巧薪资异常值处理对于明显错误的薪资数据可以基于行业/职位进行合理替换或删除城市名称统一建立城市别名映射表处理北京市、北京等不同写法文本字段标准化对行业、职位类型等字段建立标准分类体系# 城市名称标准化示例 city_standardization { 北京市: 北京, 上海: 上海, 广州市: 广州, 深圳: 深圳, 杭州市: 杭州 } df[city] df[city].map(city_standardization).fillna(df[city])5.2 数据分析示例清洗后的数据可以支持多种分析# 各城市平均薪资分析 city_salary df.groupby(city).agg({ min_salary: mean, max_salary: mean }).sort_values(max_salary, ascendingFalse) print(各城市薪资水平) display(city_salary.head(10))6. 自动化清洗流程构建对于定期爬取的数据可以构建自动化清洗流程def automated_data_cleaning(raw_file, clean_file): # 加载原始数据 df pd.read_csv(raw_file, encodingutf-8) # 执行所有清洗步骤 df[[min_salary, max_salary]] df[薪资].apply(clean_salary) df[city] df[地点].apply(extract_city) df[district] df[地点].apply(extract_district) df[financing_stage] df[公司描述].apply(classify_financing) df[company_size] df[公司描述].apply(extract_company_size) df[industry] df[公司描述].apply(extract_industry) # 保存清洗结果 df.to_csv(clean_file, indexFalse, encodingutf-8-sig) return df # 使用示例 cleaned_data automated_data_cleaning(job_data_raw.csv, job_data_clean.csv)在实际项目中我发现最耗时的不是编写清洗代码而是处理各种意想不到的数据格式。建议在自动化流程中加入足够的日志记录和异常处理方便追踪问题数据。