4. 文本数据清洗:ESG报告、新闻舆情等非结构化数据的清洗技巧

做绿色金融的量化,最头疼的是什么?

不是因子失效,不是回测过拟合。而是——你明明知道某家公司偷偷排了污水,但你的模型里全是干净的财务数据。

ESG报告、新闻舆情、监管处罚公告……这些非结构化文本里藏着真正的alpha。但前提是,你得先把它们洗干净。

4.1 非结构化数据的「三座大山」

我刚开始处理ESG报告时,差点被逼疯。PDF里是扫描件,新闻里是繁体字,公告里还有表格嵌套。说白了,文本数据清洗就三个难点:

  • 格式混乱:PDF、HTML、扫描图片、Word文档,每种格式的解析方式都不一样
  • 噪声密集:广告、页眉页脚、特殊符号、乱码字符,比信号还多
  • 语义模糊:「绿色」可以是颜色,也可以是「绿色金融」的简称

核心原则:先粗后细,先格式后语义。别一上来就做分词,先把肉眼可见的垃圾清掉。

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

正则表达式这东西,我用了十年。说实话,背语法没用,关键是知道「什么时候该用哪个模式」。

我个人习惯把ESG文本清洗拆成四个步骤:

  1. 去噪:去掉HTML标签、特殊符号、多余空格
  2. 标准化:统一日期格式、数字格式、单位
  3. 提取:抓取关键信息,比如碳排放量、处罚金额
  4. 校验:检查提取结果是否合理

4.2.1 常用正则模式

场景 正则表达式 说明
去HTML标签 <[^>]+> 匹配所有尖括号内容
去特殊符号 [^\w\s\u4e00-\u9fff] 只保留中文、英文、数字和空格
提取数字 \d+(\.\d+)? 整数和小数都匹配
匹配日期 \d{4}[-/年]\d{1,2}[-/月]\d{1,2}[日]? 兼容多种日期格式
匹配金额 \d+(\.\d+)?[万亿]?元 支持「万元」「亿元」

避坑指南:我曾经用 .* 匹配一整段ESG报告,结果把整个PDF的页眉页脚都吞进去了。记住,正则里「贪婪匹配」是魔鬼,能用 .*? 就别用 .*

4.2.2 实战代码:清洗ESG报告段落

import re

def clean_esg_text(raw_text):
    # 1. 去HTML标签
    text = re.sub(r'<[^>]+>', '', raw_text)
    
    # 2. 去特殊符号(保留中文、英文、数字、空格、句号)
    text = re.sub(r'[^\w\s\u4e00-\u9fff。,、;:!?()]', '', text)
    
    # 3. 合并多余空格和换行
    text = re.sub(r'\s+', ' ', text)
    
    # 4. 提取碳排放数据(示例:碳排放量 1234.56 吨)
    carbon_pattern = r'碳排放量[::]?\s*(\d+(\.\d+)?)\s*吨'
    match = re.search(carbon_pattern, text)
    if match:
        carbon_value = float(match.group(1))
        print(f'提取到碳排放量:{carbon_value} 吨')
    
    return text.strip()

# 示例
sample = '<p>公司2023年碳排放量: 1250.5 吨,同比减少3.2%。</p>'
cleaned = clean_esg_text(sample)
print(cleaned)
# 输出:公司2023年碳排放量:1250.5 吨,同比减少3.2%

4.3 停用词处理:该删的删,该留的留

停用词,说白了就是那些「出现频率高但没啥信息量」的词。比如「的」「了」「是」「在」。

但ESG文本里有个坑——有些词在普通文本里是停用词,在ESG语境里却是关键词。

注意:不要把「绿色」「排放」「污染」「治理」这些词加到停用词表里。我见过有人直接把通用中文停用词表套到ESG报告上,结果「绿色债券」变成了「债券」,「碳排放」变成了「排放」——信息丢失严重。

4.3.1 构建ESG专用停用词表

我的做法是:在通用停用词表基础上,手动调整。

# ESG专用停用词表(部分)
esg_stopwords = set([
    '的', '了', '是', '在', '和', '与', '及', '或',
    '一个', '这个', '那个', '这些', '那些',
    '我们', '他们', '它们', '公司', '企业',
    '进行', '通过', '使用', '采用', '实现',
    '相关', '有关', '关于', '对于', '基于',
    '以及', '其中', '此外', '同时', '另外',
    '年', '月', '日', '第', '等', '之', '为'
])

def remove_stopwords(tokens, custom_stopwords=None):
    if custom_stopwords is None:
        custom_stopwords = esg_stopwords
    return [token for token in tokens if token not in custom_stopwords]

# 示例
tokens = ['公司', '2023年', '碳排放', '量', '为', '1250.5', '吨']
filtered = remove_stopwords(tokens)
print(filtered)
# 输出:['公司', '2023年', '碳排放', '量', '1250.5', '吨']

4.4 知识体系:文本清洗全流程

嗯,这里我用一张图把整个流程串起来。你想想看,从原始文本到可用的结构化因子,中间要经过多少道工序?

ESG文本数据清洗全流程 原始文本 PDF/HTML/新闻/公告 格式清洗 去HTML标签/去特殊符号 标准化 统一日期/数字/单位 信息提取 正则匹配/关键词抓取 停用词处理 ESG专用停用词表 结构化因子 碳排放/处罚/评级 每个阶段都可能需要回溯清洗,不是一次性的流水线 迭代优化

4.5 实战避坑:我踩过的三个坑

做文本清洗这么多年,有些坑是拿真金白银换来的教训。

  • 坑一:PDF解析不彻底——我曾经用PyPDF2解析一份ESG报告,结果把「碳排放」解析成了「碳 排 放」。后来改用pdfplumber,配合正则里的 \s* 才搞定。
  • 坑二:繁体简体混用——有些港股公司的ESG报告是繁体字,但新闻是简体。我建议统一转成简体再做后续处理,用 opencc 库就行。
  • 坑三:停用词过度过滤——有次我把「不」字加进了停用词表,结果「不符合标准」变成了「符合标准」——意思完全反了。否定词绝对不能删。

我的经验:文本清洗没有银弹。每拿到一批新数据,先手工看100条样本,摸清噪声的规律,再写清洗规则。这个步骤省不了。

4.6 小结

文本数据清洗,说白了就是「把垃圾变成金子」的过程。正则表达式是工具,停用词处理是艺术,而经验——嗯,那是踩坑踩出来的。

我个人习惯把清洗好的文本存成结构化表格,每行一个文档,每列一个字段(比如「碳排放量」「处罚金额」「评级等级」)。这样后续做因子构造时,直接读表就行,不用再碰原始文本。

记住:干净的数据,是量化模型的基石。ESG文本里藏着的信息,比财务数据更前瞻、更真实。洗得干净,你就能比别人早一步看到风险。


专注资料整理