3. 文本预处理实战:中文分词、去停用词、词性标注、文本清洗全流程

文本预处理,说白了就是给原始文本「洗澡」。

我刚开始做量化舆情分析时,拿到的新闻数据简直惨不忍睹——乱码、特殊符号、英文夹杂、还有一堆「的」「了」「吗」这种没用的词。如果不处理干净,后面建模就是垃圾进垃圾出。

这一章,咱们就把整个预处理流水线走一遍。我会带着你从原始文本一步步走到干净、可用的特征向量。

3.1 文本清洗:先把脏东西筛掉

文本清洗是第一步,也是最容易被忽略的一步。我见过不少新手直接拿原始文本去分词,结果模型效果差得离谱。

清洗的核心目标就三个:

  • 去噪:去掉HTML标签、URL、特殊符号、乱码字符
  • 统一格式:全角转半角、繁体转简体、统一大小写
  • 过滤无效内容:去掉过短文本、重复文本、纯数字文本

避坑指南:我曾经在清洗阶段把「ST股票」中的「ST」当成英文单词给删了,结果模型完全识别不了ST股的风险信号。所以清洗规则一定要结合业务场景来定。

下面是我常用的清洗函数:

import re
import unicodedata

def clean_text(text):
    # 1. 全角转半角
    text = unicodedata.normalize('NFKC', text)
    
    # 2. 去掉HTML标签
    text = re.sub(r'<.*?>', '', text)
    
    # 3. 去掉URL
    text = re.sub(r'http[s]?://\S+', '', text)
    
    # 4. 保留中文、英文、数字、基本标点
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?、;:""''()【】《》\s]', '', text)
    
    # 5. 合并多余空格
    text = re.sub(r'\s+', ' ', text).strip()
    
    return text

小技巧:清洗时别一刀切。比如「+5%」中的百分号对量化分析很重要,我一般会保留「%」「+」「-」这几个符号。

3.2 中文分词:把句子切成词

中文不像英文有空格分隔,所以分词是中文NLP的基石。你想想看,「金融市场」到底是「金融/市场」还是「金融市/场」?分错了意思就全变了。

目前主流的分词工具有三个:

工具 速度 准确率 适用场景
jieba 中等 通用场景、快速原型
pkuseg 中等 金融、医学等专业领域
HanLP 很高 需要词性标注、命名实体识别

我个人习惯用 jieba + 自定义词典 的组合。为什么?因为金融领域有大量专业术语,比如「量化宽松」「逆回购」「北向资金」,这些词默认词典里没有。

import jieba

# 加载自定义金融词典
jieba.load_userdict('finance_dict.txt')

# 示例:金融词典内容
# 量化宽松 5
# 逆回购 5
# 北向资金 5
# ST股票 3

text = "央行今日开展逆回购操作,释放流动性"
words = jieba.lcut(text)
print(words)
# 输出:['央行', '今日', '开展', '逆回购', '操作', ',', '释放', '流动性']

注意:自定义词典的权重不要乱设。我踩过坑——把「涨停」设为高频词,结果「涨停板」被切成了「涨停/板」,反而错了。一般设3-5就够了。

3.3 去停用词:扔掉没用的词

停用词就是那些高频但没实际意义的词,比如「的」「了」「在」「是」「一个」。这些词在情感分析里基本不贡献信息量,留着反而增加噪声。

我常用的停用词表包含三类:

  • 通用停用词:的、了、在、是、我、你、他、这、那
  • 金融领域停用词:报告、显示、表示、指出、据悉
  • 标点符号:,。!?、;:""''()【】《》
def load_stopwords(path='stopwords.txt'):
    with open(path, 'r', encoding='utf-8') as f:
        return set([line.strip() for line in f])

stopwords = load_stopwords()

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

# 示例
words = ['央行', '今日', '开展', '逆回购', '操作', ',', '释放', '流动性']
filtered = remove_stopwords(words)
print(filtered)
# 输出:['央行', '今日', '开展', '逆回购', '操作', '释放', '流动性']

经验之谈:我一般会保留单字词中的「涨」「跌」「多」「空」——这些字在金融文本里信息量极大,不能一刀切去掉。

3.4 词性标注:给每个词打标签

词性标注就是告诉程序:这个词是名词、动词还是形容词。在量化情绪建模里,词性标注有两个重要作用:

  • 提取情感词:形容词和副词往往是情感信号(如「大幅上涨」「严重亏损」)
  • 过滤噪声:去掉虚词、连词、助词

我用的是 jieba 自带的词性标注功能,配合 paddle 模式准确率更高:

import jieba.posseg as pseg

# 使用Paddle模式(需要安装paddlepaddle)
jieba.enable_paddle()

text = "A股三大指数今日集体收涨"
words = pseg.cut(text, use_paddle=True)

for word, flag in words:
    print(f'{word}/{flag}', end=' ')
# 输出:A股/n 三大/m 指数/n 今日/t 集体/n 收涨/v

词性标注的标签含义:

标签 含义 示例
n 名词 股票、指数、市场
v 动词 上涨、下跌、买入
a 形容词 强劲、低迷、稳健
d 副词 大幅、持续、明显
t 时间词 今日、昨日、本周

核心思路:在情感分析中,我通常只保留 a(形容词)、d(副词)、v(动词)这三类,因为它们承载了大部分情感信息。名词和时间词可以视情况保留。

3.5 全流程整合:一条流水线搞定

好了,现在我们把所有步骤串起来。这是我实际项目中用的预处理流水线:

def preprocess_pipeline(text):
    # 1. 清洗
    text = clean_text(text)
    
    # 2. 分词 + 词性标注
    words_with_pos = pseg.cut(text, use_paddle=True)
    
    # 3. 过滤:只保留形容词、副词、动词
    keep_flags = {'a', 'd', 'v'}
    filtered = [(w.word, w.flag) for w in words_with_pos 
                if w.flag in keep_flags and len(w.word) > 1]
    
    # 4. 去停用词
    filtered = [(w, f) for w, f in filtered if w not in stopwords]
    
    # 5. 返回词列表
    return [w for w, f in filtered]

# 测试
text = "今日A股三大指数集体收涨,市场情绪明显回暖"
result = preprocess_pipeline(text)
print(result)
# 输出:['收涨', '明显', '回暖']

你看,原始文本有11个词,经过流水线后只剩下3个核心情感词。这就是预处理的威力——把噪声去掉,让信号更清晰。

3.6 本章知识体系

下面这张图展示了整个文本预处理的流程和关键决策点:

文本预处理全流程 原始文本 步骤1:文本清洗 去HTML标签 | 全角转半角 | 去URL | 保留有效字符 步骤2:中文分词 jieba分词 | 自定义金融词典 | 精确模式 步骤3:词性标注 Paddle模式 | 保留a/d/v三类 | 过滤虚词 步骤4:去停用词 通用停用词 | 金融领域停用词 | 保留单字情感词 干净文本 → 特征向量

嗯,到这里文本预处理的完整流程就讲完了。你可能会问:为什么非要把流程拆这么细?

原因很简单——每个步骤都可能引入错误。清洗阶段漏了特殊符号,分词阶段切错了词,停用词表删了关键信息……任何一个环节出问题,后面的模型都会跟着跑偏。

我建议你在实际项目中,每一步都输出中间结果看看。花10分钟检查,能省下后面调模型的好几天时间。