系列里已经写过很多采集的文章:淘宝SKU采集(第30篇)、拼多多采集(第17篇)、小红书采集(第37篇)。但这些文章的重点都是“怎么采”,对“采完之后怎么处理”讲得不够细。
淘宝的数据有一个特点:采集下来之后,如果不做清洗,几乎是没法直接用的。 价格是区间价或带促销标识、销量是"10万+"这种格式、评价文本混杂各种表情符号和特殊字符。
今天不写采集,专门讲淘宝数据的清洗——从原始脏数据到可分析的结构化表格,一条龙处理方案。
先说清洗目标
| 原始数据(脏) | 清洗后(干净) |
|---|
|
¥5999.00-¥7999.00 | 最低价5999,最高价7999 |
| ¥3999.00(划线价¥5999.00) | 当前价3999,原价5999 |
| 100万+人付款 | 销量1000000 |
| 评价:东西很好,物流很快👍😊 | 评价文本去除emoji |
| 2024-03-15 14:30(多种日期格式) | 统一为2024-03-15 |
第一部分:价格清洗(淘宝特有的四种价格形态)
淘宝的价格在页面上有四种常见形态,每种的处理方式不同。
形态1:单一价格

<span class="price">¥3999.00</span>
# ===== 清洗单一价格 =====

import re
def clean_single_price(text):
"""从"¥3999.00"中提取3999.00"""
if not text:

return None
nums = re.findall(r'[\d.]+', str(text))
return float(nums[0]) if nums else None
形态2:价格区间
<span class="price">¥5999.00-¥7999.00</span>
# ===== 拆分为最低价和最高价 =====
def split_price_range(text):
"""从"¥5999.00-¥7999.00"拆出[5999.00, 7999.00]"""
if not text:
return [None, None]
nums = re.findall(r'[\d.]+', str(text))
if len(nums) == 1:
return [float(nums[0]), float(nums[0])]
return [float(nums[0]), float(nums[-1])] # 取最小和最大
# 在DataFrame中应用
df[['最低价', '最高价']] = df['价格_原始'].apply(
lambda x: pd.Series(split_price_range(x))
)
形态3:促销价 + 原价(划线价)
<span class="price">¥3999.00</span>
<span class="old-price">¥5999.00</span>
# ===== 提取促销价和原价 =====
def extract_promotion_price(price_text, old_price_text):
"""提取促销价和原价"""
促销价 = clean_single_price(price_text)
原价 = clean_single_price(old_price_text)
# 如果原价小于促销价,说明标反了,交换
if 原价 and 促销价 and 原价 < 促销价:
原价, 促销价 = 促销价, 原价
return [促销价, 原价]
形态4:折扣价(带"券后价")
<span class="price">¥3699.00</span>
<span class="coupon-price">券后价¥3599.00</span>
# ===== 提取券后价 =====
def extract_coupon_price(text):
"""从"券后价¥3599.00"中提取3599.00"""
if not text or "券后" not in text:
return None
nums = re.findall(r'[\d.]+', text)
return float(nums[0]) if nums else None
综合价格清洗函数
# ===== 综合价格清洗(处理所有形态)=====
import pandas as pd
import re
def clean_taobao_price(row):
"""
输入row包含:price_text, old_price_text, coupon_text
输出:当前价、原价、最低价、最高价
"""
price = clean_single_price(row.get('price_text'))
old_price = clean_single_price(row.get('old_price_text'))
coupon_price = extract_coupon_price(row.get('coupon_text'))
# 判断是否是区间价
if '-' in str(row.get('price_text', '')):
min_p, max_p = split_price_range(row['price_text'])
return {
'当前价': min_p,
'原价': max_p,
'最低价': min_p,
'最高价': max_p
}
# 有券后价,用券后价
if coupon_price:
return {
'当前价': coupon_price,
'原价': price if price else old_price,
'最低价': coupon_price,
'最高价': price if price else old_price
}
# 有划线价,促销价是当前价
if old_price and price:
return {
'当前价': price,
'原价': old_price,
'最低价': price,
'最高价': old_price
}
# 单一价格
return {
'当前价': price,
'原价': None,
'最低价': price,
'最高价': price
}
第二部分:销量清洗("万"单位转换)
淘宝的销量常见格式:
| 原始格式 | 清洗结果 | 说明 |
|---|---|---|
1000人付款 | 1000 | 直接提取数字 |
10万+人付款 | 100000 | "万"转成0000 |
1.2万人付款 | 12000 | 小数乘以10000 |
已售10万+ | 100000 | 不同位置的文本 |
1000+人付款 | 1000 | 去掉"+"号 |
# ===== 销量清洗 =====
def clean_sales(text):
"""
清洗淘宝销量格式:
- "10万+" → 100000
- "1.2万" → 12000
- "1000人付款" → 1000
"""
if pd.isna(text):
return 0
text = str(text).strip()
# 提取数字和"万"字符
match = re.search(r'([\d.]+)\s*万', text)
if match:
# 有"万"单位
num = float(match.group(1))
return int(num * 10000)
# 没有"万",直接提取数字
nums = re.findall(r'[\d.]+', text)
if nums:
return int(float(nums[0]))
return 0
# 应用
df['销量_清洗'] = df['销量_原始'].apply(clean_sales)
第三部分:评价文本清洗
淘宝评价中混杂了大量噪音:表情符号、特殊字符、重复标点、广告内容。
去除Emoji和特殊字符
# ===== 清洗评价文本 =====
import re
def clean_comment(text):
"""清洗淘宝评价文本"""
if pd.isna(text):
return ""
text = str(text)
# 1. 去除emoji(Unicode表情符号范围)
emoji_pattern = re.compile(
"[\U0001F600-\U0001F64F" # 表情符号
"\U0001F300-\U0001F5FF" # 符号和象形文字
"\U0001F680-\U0001F6FF" # 交通和地图
"\U0001F700-\U0001F77F" # 炼金术符号
"\U0001F780-\U0001F7FF" # 几何形状扩展
"\U0001F800-\U0001F8FF" # 补充箭头-C
"\U0001F900-\U0001F9FF" # 补充符号和象形文字
"\U0001FA00-\U0001FA6F" # 象棋符号
"\U0001FA70-\U0001FAFF" # 扩展-A
"\u2600-\u26FF" # 杂项符号
"\u2700-\u27BF" # 装饰符号
"]+",
flags=re.UNICODE
)
text = emoji_pattern.sub('', text)
# 2. 去除多余空格
text = re.sub(r'\s+', ' ', text).strip()
# 3. 去除重复标点("太好了!!!" → "太好了!")
text = re.sub(r'([!!?.?])\1+', r'\1', text)
# 4. 去除"此用户未填写评价"等无意义文本
if text in ["此用户未填写评价", "用户未填写评价", ""]:
return ""
return text
# 应用
df['评价_清洗'] = df['评价_原始'].apply(clean_comment)
评价分类(好评/中评/差评)
# ===== 评价分类 =====
def classify_rating(rating_text):
"""根据星级或文本判断评价类型"""
if pd.isna(rating_text):
return "未知"
text = str(rating_text)
if "好评" in text or "满意" in text or "五星" in text:
return "好评"
elif "差评" in text or "不满意" in text or "一星" in text:
return "差评"
elif "中评" in text or "一般" in text:
return "中评"
# 根据星级数字判断
stars = re.findall(r'(\d)星', text)
if stars:
star_num = int(stars[0])
if star_num >= 4:
return "好评"
elif star_num == 3:
return "中评"
else:
return "差评"
return "未知"
第四部分:评价关键词提取(简易NLP)
# ===== 提取评价关键词 =====
def extract_keywords(text, top_k=5):
"""
从评价文本中提取关键词(简易实现)
实际项目可用jieba分词+TF-IDF
"""
if not text or len(text) < 3:
return []
# 去停用词
stopwords = {'的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这', '那', '它', '他', '她'}
# 简单分词(按标点和空格)
import re
words = re.findall(r'[\u4e00-\u9fa5]{2,}', text)
# 过滤停用词
filtered = [w for w in words if w not in stopwords]
# 词频统计
from collections import Counter
counter = Counter(filtered)
# 返回最高频的几个词
return [word for word, _ in counter.most_common(top_k)]
# 应用
df['关键词'] = df['评价_清洗'].apply(extract_keywords)
第五部分:完整清洗流水线(整合所有步骤)
# ============================================
# 淘宝数据完整清洗流水线(Python)
# ============================================
import pandas as pd
import re
from collections import Counter
# ---- 输入:原始数据(从影刀传入)----
# raw_data: [[商品名, 价格文本, 划线价文本, 销量文本, 评价文本, ...], ...]
def clean_taobao_data(raw_data):
# 1. 转为DataFrame
columns = ['商品名', '价格_原始', '划线价', '销量_原始', '评价_原始', '采集时间']
df = pd.DataFrame(raw_data, columns=columns)
# 2. 价格清洗
price_info = df.apply(clean_taobao_price, axis=1, result_type='expand')
df = pd.concat([df, price_info], axis=1)
# 3. 销量清洗
df['销量_清洗'] = df['销量_原始'].apply(clean_sales)
# 4. 评价清洗
df['评价_清洗'] = df['评价_原始'].apply(clean_comment)
df['评价类型'] = df['评价_原始'].apply(classify_rating)
df['关键词'] = df['评价_清洗'].apply(extract_keywords)
# 5. 计算派生字段
# 折扣率 = 当前价 / 原价(如果有)
def calc_discount(row):
if row['原价'] and row['原价'] > 0:
return round(row['当前价'] / row['原价'] * 100, 1)
return None
df['折扣率_%'] = df.apply(calc_discount, axis=1)
# 6. 异常标记
df['异常标记'] = '正常'
df.loc[df['当前价'] <= 0, '异常标记'] = '价格异常'
df.loc[df['销量_清洗'] == 0, '异常标记'] = '零销量'
df.loc[df['评价_清洗'] == '', '异常标记'] = '无评价'
# 7. 选择输出列
output_cols = [
'商品名', '当前价', '原价', '最低价', '最高价',
'销量_清洗', '评价_清洗', '评价类型', '关键词',
'折扣率_%', '异常标记', '采集时间'
]
clean_df = df[output_cols]
# 8. 去重
clean_df = clean_df.drop_duplicates(subset=['商品名'], keep='first')
# 9. 排序
clean_df = clean_df.sort_values('销量_清洗', ascending=False)
return clean_df
# ---- 在影刀中调用 ----
# 执行Python代码,输入raw_data,输出clean_data
result_df = clean_taobao_data(raw_data)
cleaned_data = result_df.to_dict('records')
第六部分:清洗前后的数据质量对比
| 指标 | 清洗前 | 清洗后 |
|---|---|---|
| 价格字段 | 带"¥"、区间、划线价 | 统一为数字,拆出4个价格字段 |
| 销量字段 | "10万+"、带单位 | 统一为数字 |
| 评价文本 | 含emoji、乱码 | 纯文本、去除噪音 |
| 评价分类 | 无 | 好评/中评/差评 |
| 关键字段 | 无 | 提取5个高频关键词 |
| 异常标记 | 无 | 价格/销量/评价异常自动标记 |
| 可分析性 | 低 | 高(可直接做统计、图表) |
常见问题速查
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 价格提取失败 | 价格格式变了(如"¥3,999") | 正则加逗号匹配 |
| 销量"万"转换错误 | 小数位处理 | int(num * 10000) |
| 评价清洗后全为空 | 所有评价都是默认评价 | 保留"默认评价"标记,不删除 |
| 去重后数据太少 | 不同商品名称相同 | 用商品ID去重,不是商品名 |
| 折扣率超过100% | 划线价低于促销价 | 判断并交换 |
推荐资源
- Python
re模块文档(正则表达式) - Jieba分词库(中文NLP基础工具)
- 本系列第40篇《数据采集之后的清洗、去重与格式转换》
- 本系列第30篇《淘宝商品SKU采集与评价抓取》
#影刀RPA #RPA自动化 #淘宝 #数据清洗 #价格清洗 #评价分析
作者:林焱
本文为《影刀RPA学习手册》系列文章之一,内容源于实操经验的整理与分享。
