Python爬虫实战⑰|Pandas数据清洗,处理缺失值与异常值
·
author: 专注Python实战,分享爬虫与数据分析干货
title: Python爬虫实战⑰|Pandas数据清洗,处理缺失值与异常值
update: 2026-04-26
tags: Python,Pandas,数据清洗,缺失值,异常值,数据预处理,去重
作者:专注Python实战,分享爬虫与数据分析干货
更新时间:2026年4月
适合人群:有Pandas基础、想掌握数据清洗技术的开发者
前言:爬下来的数据,能直接用吗?
爬虫数据常见问题:
- 某字段是空的(网站没有该数据)
- 价格字段里有"¥"和","符号,无法计算
- 日期格式不统一(2026-01-01 和 2026/1/1)
- 评分字段有"暂无评分"
- 异常大的订单金额
数据清洗 = 数据分析的70%工作。 Garbage in, garbage out。
一、缺失值处理
1.1 缺失值检测
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", None, "王五", "赵六"],
"年龄": [25, None, 30, 28, None],
"薪资": ["15000", "20000", None, "25000", "18000"],
"部门": ["技术", "技术", "运营", None, "运营"],
})
# 检测缺失值
print(df.isnull())
print("\n每列缺失数量:")
print(df.isnull().sum())
print("\n每列缺失比例:")
print((df.isnull().sum() / len(df) * 100).round(1), "%")
1.2 删除缺失值
# 删除所有含缺失值的行
df_clean = df.dropna()
# 只删除某列有缺失值的行
df_clean = df.dropna(subset=["姓名", "年龄"])
# 只删除全部为空的行
df_clean = df.dropna(how="all")
# 删除后重置索引
df_clean = df.dropna().reset_index(drop=True)
1.3 填充缺失值
# 用固定值填充
df["部门"] = df["部门"].fillna("未知部门")
# 用均值填充(数值列)
df["年龄"] = df["年龄"].fillna(df["年龄"].mean())
# 用中位数填充(不怕极端值影响)
df["薪资"] = pd.to_numeric(df["薪资"], errors="coerce")
df["薪资"] = df["薪资"].fillna(df["薪资"].median())
# 用众数填充(类别列)
df["部门"] = df["部门"].fillna(df["部门"].mode()[0])
# 前向填充
df["部门"] = df["部门"].fillna(method="ffill")
# 后向填充
df["部门"] = df["部门"].fillna(method="bfill")
# 线性插值
df["年龄"] = df["年龄"].interpolate()
# 按条件填充
df.loc[df["部门"] == "技术", "薪资"] = df.loc[
df["部门"] == "技术", "薪资"
].fillna(20000)
二、重复值处理
# 检测重复
print(df.duplicated()) # 每行是否重复
print(df.duplicated().sum()) # 重复行数量
print(df[df.duplicated()]) # 显示重复的行
# 删除重复(保留第一个)
df_unique = df.drop_duplicates()
# 删除重复(保留最后一个)
df_unique = df.drop_duplicates(keep="last")
# 按列删除重复
df_unique = df.drop_duplicates(subset=["姓名"])
# 按多列组合去重
df_unique = df.drop_duplicates(subset=["姓名", "部门"])
# 去重后重置索引
df_unique = df.drop_duplicates().reset_index(drop=True)
三、数据类型转换
3.1 数值转换
# 查看数据类型
print(df.dtypes)
# 转数值类型
df["薪资"] = pd.to_numeric(df["薪资"], errors="coerce")
# errors="coerce":无法转换的变成NaN
# 转日期类型
df["入职日期"] = pd.to_datetime(df["入职日期"], errors="coerce")
# 转字符串
df["工号"] = df["工号"].astype(str)
# 转分类(节省内存)
df["部门"] = df["部门"].astype("category")
3.2 字符串清洗
# 清理特殊字符
df["薪资"] = df["薪资"].str.replace("¥", "", regex=False)
df["薪资"] = df["薪资"].str.replace(",", "", regex=False)
df["薪资"] = df["薪资"].str.strip() # 去首尾空格
# 转数值
df["薪资"] = pd.to_numeric(df["薪资"], errors="coerce")
# 提取数字
df["手机号"] = df["手机号"].str.extract(r"(\d{11})")
# 统一日期格式
df["日期"] = pd.to_datetime(df["日期"], errors="coerce").dt.strftime("%Y-%m-%d")
# 替换特定文本
df["评分"] = df["评分"].replace("暂无评分", np.nan)
df["评分"] = df["评分"].replace({"暂无": np.nan, "N/A": np.nan, "-": np.nan})
四、异常值处理
4.1 识别异常值
import pandas as pd
import numpy as np
df = pd.DataFrame({
"订单金额": [100, 200, 150, 999999, 180, 200, 170, -50, 160],
"订单数量": [1, 2, 1, 9999, 2, 1, 1, -1, 2],
})
# 方法1:3σ原则
mean = df["订单金额"].mean()
std = df["订单金额"].std()
lower = mean - 3 * std
upper = mean + 3 * std
outliers = df[(df["订单金额"] < lower) | (df["订单金额"] > upper)]
print("3σ异常值:")
print(outliers)
# 方法2:IQR四分位法(更稳健,推荐)
Q1 = df["订单金额"].quantile(0.25)
Q3 = df["订单金额"].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df["订单金额"] < lower) | (df["订单金额"] > upper)]
print(f"\nIQR异常值(<{lower:.0f} 或 >{upper:.0f}):")
print(outliers)
# 方法3:Z-score
from scipy import stats
z_scores = np.abs(stats.zscore(df[["订单金额", "订单数量"]]))
outliers = df[(z_scores > 3).any(axis=1)]
print("\nZ-score异常值:")
print(outliers)
4.2 处理异常值
# 方法1:删除
df_clean = df[(df["订单金额"] >= 0) & (df["订单金额"] < 10000)]
df_clean = df[(df["订单金额"] > lower) & (df["订单金额"] < upper)]
# 方法2:替换为边界值(clip)
df["订单金额"] = df["订单金额"].clip(lower=0, upper=10000)
# 方法3:替换为中位数
median = df["订单金额"].median()
df.loc[df["订单金额"] > upper, "订单金额"] = median
df.loc[df["订单金额"] < lower, "订单金额"] = median
# 方法4:标记异常(保留原始数据)
df["是否异常"] = (df["订单金额"] > upper) | (df["订单金额"] < lower)
五、完整数据清洗流程
import pandas as pd
import numpy as np
def clean_crawler_data(df):
"""爬虫数据清洗完整流程"""
print(f"原始数据: {df.shape[0]}行 × {df.shape[1]}列")
print(f"缺失值:\n{df.isnull().sum()}")
# Step 1: 去重
before = len(df)
df = df.drop_duplicates(subset=["url"]).reset_index(drop=True)
print(f"\n去重: {before} → {len(df)}(删除{before - len(df)}条)")
# Step 2: 列名标准化
df.columns = df.columns.str.strip().str.lower().str.replace(" ", "_")
# Step 3: 类型转换
if "价格" in df.columns:
df["价格"] = df["价格"].astype(str).str.replace("[¥¥,]", "", regex=True)
df["价格"] = pd.to_numeric(df["价格"], errors="coerce")
if "评分" in df.columns:
df["评分"] = pd.to_numeric(df["评分"], errors="coerce")
if "日期" in df.columns:
df["日期"] = pd.to_datetime(df["日期"], errors="coerce")
# Step 4: 处理缺失值
# 数值列用中位数填充
num_cols = df.select_dtypes(include=[np.number]).columns
for col in num_cols:
df[col] = df[col].fillna(df[col].median())
# 文本列用"未知"填充
text_cols = df.select_dtypes(include=["object"]).columns
for col in text_cols:
df[col] = df[col].fillna("未知")
# Step 5: 异常值处理
if "价格" in df.columns:
Q1 = df["价格"].quantile(0.25)
Q3 = df["价格"].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outlier_count = ((df["价格"] < lower) | (df["价格"] > upper)).sum()
df["价格"] = df["价格"].clip(lower=max(0, lower), upper=upper)
print(f"异常值处理: {outlier_count}条价格异常")
print(f"\n清洗后: {df.shape[0]}行 × {df.shape[1]}列")
print(f"剩余缺失值: {df.isnull().sum().sum()}")
return df
# 使用
df = pd.read_csv("raw_products.csv", encoding="utf-8-sig")
df_clean = clean_crawler_data(df)
df_clean.to_csv("clean_products.csv", index=False, encoding="utf-8-sig")
六、知识卡
| 方法 | 说明 |
|---|---|
| df.isnull().sum() | 每列缺失值数量 |
| df.dropna() | 删除缺失行 |
| df.fillna() | 填充缺失值 |
| df.interpolate() | 线性插值 |
| df.duplicated() | 检测重复行 |
| df.drop_duplicates() | 删除重复行 |
| pd.to_numeric() | 转数值 |
| pd.to_datetime() | 转日期 |
| str.replace() | 字符串替换 |
| str.extract() | 正则提取 |
| 3σ原则 | 正态分布异常值检测 |
| IQR方法 | 四分位异常值检测 |
| df.clip() | 截断到指定范围 |
七、课后作业
必做题:
- 读取一个含缺失值的CSV,统计缺失比例
- 用三种方法填充缺失值,对比效果
- 用IQR方法检测异常值
选做题:
- 编写完整数据清洗函数
- 处理真实的爬虫数据(含乱码、格式混乱等)
有问题欢迎评论区留言,大家一起讨论!
标签:Python | Pandas | 数据清洗 | 缺失值 | 异常值 | 数据预处理
更多推荐


所有评论(0)