文本预处理:正则表达式、分词技术、停用词过滤、词干提取与词形还原

文本预处理,说白了就是给原始文本「洗澡」。你想想看,社交媒体上的数据有多脏?表情符号、@符号、URL链接、错别字、各种缩写...如果不洗干净,后面的分析全是垃圾。

我个人习惯把预处理分成四个核心步骤:正则清洗 → 分词 → 停用词过滤 → 词干/词形还原。每一步都有坑,我一个个说。

3.1 正则表达式:文本清洗的瑞士军刀

正则表达式这东西,刚接触时觉得像天书,用熟了会发现——真香。我在项目中遇到过最典型的场景:从微博文本里提取话题标签(#xxx#)和@用户名。

核心思路:用模式匹配定位并替换掉不需要的内容,或者提取出有价值的信息。

举个实际例子。假设你爬了一堆推特数据,里面全是这种:

"RT @user123: 今天天气真好!☀️ #周末愉快 https://t.co/abc123"

你想保留中文内容,去掉转发标记、用户名、表情符号和链接。用正则一行搞定:

import re

text = "RT @user123: 今天天气真好!☀️ #周末愉快 https://t.co/abc123"
# 去掉RT、@用户、话题标签、链接、表情符号
cleaned = re.sub(r'RT\s|@\w+|#\w+|https?://\S+|[^\u4e00-\u9fa5\w\s]', '', text)
print(cleaned)  # 输出: " 今天天气真好 周末愉快 "

避坑指南:我曾经因为正则写得太宽松,把「#1销量冠军」里的数字1也给干掉了。后来养成了习惯——先在小样本上跑一遍,肉眼检查结果。

常用的正则模式我整理了一张表,你直接拿去用:

目标 正则模式 说明
去掉URL https?://\S+ 匹配http/https链接
去掉@用户 @\w+ 匹配@开头的用户名
去掉话题标签 #\w+ 匹配#开头的标签
保留中英文数字 [^\u4e00-\u9fa5a-zA-Z0-9\s] 只保留汉字、字母、数字、空格
去掉多余空格 \s+ 多个空格替换为一个

3.2 分词技术:中文处理的硬骨头

英文分词简单,按空格切就行。中文?「南京市长江大桥」——是「南京/市长/江大桥」还是「南京市/长江大桥」?这就是歧义问题。

我建议你记住三个主流工具:jieba、HanLP、pkuseg。我个人最常用jieba,因为它轻量、速度快,适合社交媒体这种海量数据场景。

import jieba

text = "我今天在王府井吃了一碗炸酱面,味道真不错!"
# 精确模式(推荐)
words = jieba.lcut(text)
print(words)
# 输出: ['我', '今天', '在', '王府井', '吃了', '一碗', '炸酱面', ',', '味道', '真不错', '!']

# 添加自定义词典(处理品牌名、产品名)
jieba.add_word('喜茶')
jieba.add_word('瑞幸咖啡')
text2 = "今天去喜茶买了一杯多肉葡萄"
print(jieba.lcut(text2))
# 输出: ['今天', '去', '喜茶', '买了', '一杯', '多肉葡萄']

我的经验:做品牌监测时,一定要建自定义词典。比如「星巴克」默认会被切成「星/巴克」,但加上词典后就能正确识别。我曾经因为没加词典,把「小米手机」切成了「小米/手机」,导致品牌词统计少了一半。

三种分词模式的对比:

模式 特点 适用场景
精确模式 最常用,不产生冗余词 一般文本分析
全模式 把所有可能的词都切出来 关键词提取
搜索引擎模式 在精确模式基础上再切长词 搜索引擎索引

3.3 停用词过滤:去掉噪音,留下信号

「的」「了」「在」「是」「一个」——这些词出现频率极高,但几乎不携带任何信息。停用词过滤就是把这些「噪音」去掉。

嗯,这里要注意:停用词表不是通用的。我做电商评论分析时,会把「买」「快递」「好评」这些词从停用词表里移除,因为它们恰恰是分析重点。

# 加载停用词表
stopwords = set()
with open('chinese_stopwords.txt', 'r', encoding='utf-8') as f:
    for line in f:
        stopwords.add(line.strip())

# 过滤停用词
words = ['我', '今天', '在', '王府井', '吃了', '一碗', '炸酱面', '味道', '真不错']
filtered = [w for w in words if w not in stopwords and len(w) > 1]
print(filtered)
# 输出: ['今天', '王府井', '吃了', '炸酱面', '味道', '真不错']

避坑指南:我曾经直接把网上找的停用词表套上去,结果「不」「没」「很」这些否定词和程度副词全被过滤了。情感分析直接废掉——「不好吃」变成了「好吃」。所以,停用词表一定要根据任务定制

3.4 词干提取与词形还原:把变体归一化

英文里,「running」「ran」「runs」都是run的变体。中文虽然没有时态变化,但有「吃」「吃了」「吃过」「吃着」这种形态变化。

词干提取(Stemming)是粗暴地砍掉后缀,词形还原(Lemmatization)是还原到词典原形。我建议中文场景用词形还原,因为中文的词干提取效果很差——「科学家」砍掉「家」变成「科学」,意思完全变了。

# 英文示例(NLTK)
from nltk.stem import PorterStemmer, WordNetLemmatizer

stemmer = PorterStemmer()
lemmatizer = WordNetLemmatizer()

words = ['running', 'ran', 'runs', 'better', 'went']
print([stemmer.stem(w) for w in words])
# 输出: ['run', 'ran', 'run', 'better', 'went']  —— ran没变,better没变

print([lemmatizer.lemmatize(w, pos='v') for w in words])
# 输出: ['run', 'run', 'run', 'better', 'go']  —— 全部还原到动词原形

中文怎么办?中文没有成熟的词形还原工具,我一般用jieba的词性标注+自定义规则。比如「吃了」→「吃」,「看过」→「看」。说白了,就是去掉「了」「过」「着」这些时态助词。

3.5 完整预处理流程

把上面四个步骤串起来,就是一个完整的预处理pipeline。我画了一张流程图,你看一眼就明白了:

文本预处理完整流程 原始文本 正则表达式清洗 分词(jieba/HanLP) 停用词过滤 词干/词形还原 注意事项 • 正则先清洗再分词 • 自定义词典提前加载 • 停用词表按任务定制 • 中文优先词形还原 • 每步都做样本检查 • 社交媒体注意表情符号 • 保留否定词和程度词 • 最终结果去重排序

你看,整个流程就像流水线。原始文本进来,经过四道工序,出来的是干净、结构化的词列表。我建议你每次跑完预处理,都打印几条样本看看——这一步能发现80%的问题。

我的习惯:把预处理封装成一个函数,参数化控制是否做词形还原、使用哪份停用词表。这样不同项目只需要改参数,不用重写代码。

好了,文本预处理就聊到这儿。记住一句话:预处理做得好,后续分析事半功倍;预处理做得糙,模型再强也白搭。


公众号:蓝海资料掘金营,微信deep3321