影刀RPA实战案例:淘宝商品数据清洗——价格、销量与评价文本的全链路处理

系列里已经写过很多采集的文章:淘宝SKU采集(第30篇)、拼多多采集(第17篇)、小红书采集(第37篇)。但这些文章的重点都是“怎么采”,对“采完之后怎么处理”讲得不够细。

淘宝的数据有一个特点:采集下来之后,如果不做清洗,几乎是没法直接用的。 价格是区间价或带促销标识、销量是"10万+"这种格式、评价文本混杂各种表情符号和特殊字符。

picture.image

今天不写采集,专门讲淘宝数据的清洗——从原始脏数据到可分析的结构化表格,一条龙处理方案。


picture.image

先说清洗目标

原始数据(脏)清洗后(干净)

picture.image | ¥5999.00-¥7999.00 | 最低价5999,最高价7999 | | ¥3999.00(划线价¥5999.00) | 当前价3999,原价5999 | | 100万+人付款 | 销量1000000 | | 评价:东西很好,物流很快👍😊 | 评价文本去除emoji | | 2024-03-15 14:30(多种日期格式) | 统一为2024-03-15 |

picture.image


第一部分:价格清洗(淘宝特有的四种价格形态)

picture.image 淘宝的价格在页面上有四种常见形态,每种的处理方式不同。

形态1:单一价格


![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/bc82da4f2e054a1ab2773a5710f5b16f~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=Q5ORlF%2B%2FV5UJFmK%2FfpBudn9JemY%3D)
<span class="price">¥3999.00</span>
# ===== 清洗单一价格 =====

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/6f2a6e91bf2145eba6aae90c55275d32~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=4S5v1mJ%2F%2F6MtxsTHInciNztqqfo%3D)
import re

def clean_single_price(text):
    """从"¥3999.00"中提取3999.00"""
    if not text:
    
![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/7669d6640fe74df88e70bd89bdbe85fb~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783541674&x-signature=1jAyPNbu3bgdYJESSyBACp6kpgI%3D)
        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))
)

picture.image

形态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>

picture.image

# ===== 提取券后价 =====
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学习手册》系列文章之一,内容源于实操经验的整理与分享。

0
0
0
0
评论
未登录
暂无评论