数据预处理:4 步把 10 万条脏评论变成可分析的干净语料

老板们最常踩的坑:拿到 10 万条评论直接跑 LDA,跑出来全是无意义主题

问题不在算法,在数据——脏数据里 30% 是 URL、@用户、表情、空行、重复行。垃圾进,垃圾出。

我们的 数据预处理软件 把 4 步流程固化成流水线:字符过滤 → 去重去空 → 智能分词 → 停用词清理。一次配置,永久复用。

下面拆解每一步的真实代码和踩坑点。

一、多语言字符过滤:先砍掉 30% 垃圾

原始评论长这样:

1
2
@小米客服 刚买的https://item.jd.com/123.html 666👍👍
今天天气真好[表情]!!!

要保留的:中英文 + 数字 + 必要的标点(句号、问号、感叹号保留情感)
要砍掉的:URL、@用户、HTML 标签、emoji

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import re

def clean_text(text, lang='zh'):
# 1. 砍 URL
text = re.sub(r'https?://\S+', '', text)
# 2. 砍 @用户
text = re.sub(r'@[\w\u4e00-\u9fa5]+', '', text)
# 3. 砍 HTML 标签
text = re.sub(r'<[^>]+>', '', text)
# 4. 砍 emoji(保留基本标点)
text = re.sub(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF]', '', text)
if lang == 'zh':
# 中文:保留汉字 + 中英文标点 + 数字
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?、;:""''《》()]', ' ', text)
else:
# 英文:保留字母数字 + 基本标点
text = re.sub(r'[^a-zA-Z0-9\s.,!?;:\'"()-]', ' ', text)
# 5. 合并多余空白
text = re.sub(r'\s+', ' ', text).strip()
return text

print(clean_text('@小米客服 刚买的https://item.jd.com/123.html 666👍👍'))
# 输出: 刚买的 666

踩坑 1:emoji 范围要写全,只写 \U0001F600-\U0001F64F 会漏掉「👍」这种 Symbols and Pictographs 区间。

踩坑 2:中文要保留 ,。!? 这类全角标点,砍掉后情感判别就废了(比如”差评!”和”差评”情感强度差很多)。

二、去重去空:肉眼看不见的”重复”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pandas as pd

df = pd.read_csv('comments.csv')
before = len(df)

# 1. 去空行(整条 NaN 或只有空白)
df = df.dropna(subset=['content'])
df = df[df['content'].str.strip() != '']

# 2. 合并重复(按内容完全相同)
df = df.drop_duplicates(subset=['content'], keep='first')

# 3. 进阶:合并"近似重复"(去掉空格、标点后相同)
# 比如 "666 !" 和 "666!" 其实是同一句
df['fingerprint'] = df['content'].str.replace(r'[\s\W]', '', regex=True)
df = df.drop_duplicates(subset=['fingerprint'], keep='first')
df = df.drop(columns=['fingerprint'])

print(f'去重前 {before} 条, 去重后 {len(df)} 条, 砍掉 {before - len(df)} 条垃圾')

实测 10 万条电商评论:纯空行占 8%,全等重复占 12%,近似重复(清洗后重复)占 15%——这 15% 不处理会严重污染主题分析。

三、智能分词:jieba 加自定义词库

中文分词默认 jieba.cut() 在电商领域惨不忍睹——“小米手机”被切成”小米/手机”,”华为 Mate60”切成”华为/Mate/60”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import jieba

# 1. 加载自定义词库(行业黑话 + 品牌型号)
custom_words = [
'小米手机', '华为Mate60', 'iPhone15Pro', '遥遥领先',
'国货之光', '智商税', '种草', '拔草', '踩雷',
'退退退', '绝绝子', 'yyds', '栓Q'
]
for word in custom_words:
jieba.add_word(word, freq=10000) # freq 越大越不容易被切散

# 2. 分词
def tokenize(text):
words = jieba.lcut(text)
return [w for w in words if len(w.strip()) > 0]

# 测试
print(tokenize('华为Mate60遥遥领先yyds'))
# ['华为Mate60', '遥遥领先', 'yyds'] ✓ 不再被切散

踩坑:自定义词必须加 freq=10000,否则优先级太低,被切散成”华为 / Mate60”。

四、停用词清理:最后一道过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 加载停用词库(中文常见 2000+ 停用词)
stopwords = set()
with open('cn_stopwords.txt', encoding='utf-8') as f:
for line in f:
stopwords.add(line.strip())

# 进阶:领域停用词(评论里高频但没意义)
domain_stopwords = {'这个', '那个', '就是', '感觉', '觉得', '可能', '应该', '还是', '不过', '然后'}
stopwords |= domain_stopwords

def remove_stopwords(words):
return [w for w in words if w not in stopwords and len(w) > 1]

words = tokenize('我觉得这个手机还是不错的')
print(remove_stopwords(words))
# ['手机', '不错'] ✓ "觉得"/"这个"/"还是" 都被砍掉

踩坑:单字词一定要砍(”的”、”了”、”是”),但要保留”不”、”没”、”很”——这些是情感分析的关键否定词和程度词。

五、流水线:一次配置 4 步串起来

1
2
3
4
5
6
7
8
9
10
11
12
def pipeline(text, lang='zh'):
text = clean_text(text, lang)
if not text:
return ''
words = tokenize(text)
words = remove_stopwords(words)
return ' '.join(words)

# 批量处理
df['clean'] = df['content'].apply(pipeline)
df = df[df['clean'] != '']
df.to_csv('clean_corpus.csv', index=False)

六、效果对比

处理 10 万条淘宝评论:

指标 原始 处理后
总条数 100,000 73,200
平均长度 28 字 12 字
含 URL 18% 0%
重复行 27% 0%
跑 LDA 主题数 3-5 个混乱主题 清晰的 8-12 个主题

数据决定上限,算法逼近上限。数据预处理做不好,再先进的算法也救不回。


我们的 数据预处理软件 把上面 4 步封装成可视化界面:导入文件 → 选清洗规则 → 一键导出。支持自定义正则、自定义词库、自定义停用词库,配一次永久复用。