3、文本清洗基础:去除HTML标签,去除URL与特殊字符,统一大小写,去除多余空白

好,咱们正式开始聊文本清洗。这一步说白了,就是把原始文本里那些「乱七八糟」的东西给清理掉。你想想看,从网上爬下来的数据,什么HTML标签、URL链接、各种表情符号、大小写不统一、空格满天飞……这些如果不处理,模型根本没法好好学。

我个人习惯把文本清洗分成四个基础动作:去标签、去URL、统一大小写、清空白。别小看这四步,我见过太多项目,就因为清洗没做到位,模型效果直接打五折。

3.1 去除HTML标签

从网页抓数据,HTML标签是最常见的「噪音」。比如 <p><div><a href="..."> 这些,对我们做NLP来说毫无意义。

怎么去?最直接的办法是用正则表达式。我常用的模式是 r'<[^>]+>',它能匹配所有尖括号包裹的内容。

import re

def remove_html_tags(text):
    clean = re.sub(r'<[^>]+>', '', text)
    return clean

# 示例
raw_text = '<p>今天天气<b>真好</b>!</p>'
print(remove_html_tags(raw_text))
# 输出:今天天气真好!
小技巧:如果你用的是BeautifulSoup,也可以用 BeautifulSoup(text, "html.parser").get_text()。不过我个人更偏爱正则,因为速度快,而且不依赖第三方库。

嗯,这里要注意:有些HTML里还藏着JavaScript代码或CSS样式,比如 <script>...</script>。我建议先单独把它们干掉,再处理普通标签。

def clean_html_completely(text):
    # 先移除 script 和 style 块
    text = re.sub(r'<script[^>]*>.*?</script>', '', text, flags=re.DOTALL)
    text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL)
    # 再移除所有标签
    text = re.sub(r'<[^>]+>', '', text)
    return text

3.2 去除URL与特殊字符

URL这东西,在文本里经常出现。比如「详情请访问 https://example.com/news/123」。模型不需要知道这个链接,它只会把URL当成一个奇怪的词。

我曾经在一个舆情分析项目里,发现模型把「http」和「www」当成了高频词,结果分类全偏了。排查了半天,才发现是URL没清干净。

URL的正则模式:r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'

def remove_urls(text):
    url_pattern = r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
    return re.sub(url_pattern, '', text)

特殊字符呢?比如 @#$%^&* 这些。要不要全删?不一定。得看你的任务。

  • 情感分析:保留 !? 可能有用,因为它们表达情绪。
  • 文本分类:一般只保留中文、英文、数字和基本标点。
  • 机器翻译:标点符号最好保留,不然句子结构会乱。

我一般这样处理:

def remove_special_chars(text, keep_punctuation=True):
    if keep_punctuation:
        # 保留中文、英文、数字、空格和常见标点
        pattern = r'[^\u4e00-\u9fa5a-zA-Z0-9\s,。!?、;:""''()【】《》\-\.\,\!\?]'
    else:
        # 只保留中文、英文、数字和空格
        pattern = r'[^\u4e00-\u9fa5a-zA-Z0-9\s]'
    return re.sub(pattern, '', text)
注意:别一股脑把所有特殊字符都删了。比如「C++」里的「++」如果删了,就变成「C」了,意思全变了。我建议先看看你的数据里有哪些特殊字符是有意义的。

3.3 统一大小写

英文文本里,「Apple」和「apple」在模型看来是两个不同的词。但语义上它们是一样的。所以统一大小写很有必要。

做法很简单:text.lower()text.upper()。我习惯用 lower(),因为小写更常见。

def normalize_case(text):
    return text.lower()

但有个坑:专有名词。比如「iPhone」、「McDonald's」、「USA」。全转小写后,「iPhone」变成「iphone」,模型可能就认不出来了。

我曾经做过一个产品名称识别项目,把「iPhone 14」转成「iphone 14」后,召回率掉了8个点。后来我加了一个白名单机制:

def smart_lower(text, preserve_list=None):
    if preserve_list is None:
        preserve_list = ['iPhone', 'McDonald', 'USA', 'AI', 'NLP']
    # 先把白名单里的词替换成占位符
    placeholders = {}
    for i, word in enumerate(preserve_list):
        placeholder = f'__PLACEHOLDER_{i}__'
        placeholders[placeholder] = word
        text = text.replace(word, placeholder)
    # 统一转小写
    text = text.lower()
    # 恢复白名单词
    for placeholder, word in placeholders.items():
        text = text.replace(placeholder, word)
    return text

当然,如果你的任务不涉及专有名词,直接用 lower() 就够用了。

3.4 去除多余空白

最后一步,清理空白。文本里经常有多余的空格、换行、制表符。比如「今天 天气 真好」中间有多个空格,或者每行末尾有空格。

我常用的方法:

def clean_whitespace(text):
    # 将多个空白字符(空格、换行、制表符)替换为单个空格
    text = re.sub(r'\s+', ' ', text)
    # 去掉首尾空白
    text = text.strip()
    return text

为什么要做这步?你想想看,如果文本里有多余空格,分词的时候就会分出很多空词,或者把「今天」和「天气」之间的空格当成一个特征,这完全没意义。

完整清洗流程:我一般把这四步串成一个函数,一步到位。
def full_clean_pipeline(text):
    # 1. 去HTML标签
    text = re.sub(r'<[^>]+>', '', text)
    # 2. 去URL
    text = re.sub(r'http[s]?://\S+', '', text)
    # 3. 去特殊字符(保留中文、英文、数字、空格和常见标点)
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s,。!?、;:""''()【】《》\-\.\,\!\?]', '', text)
    # 4. 统一小写
    text = text.lower()
    # 5. 清空白
    text = re.sub(r'\s+', ' ', text).strip()
    return text

# 测试
sample = '<div>欢迎访问 https://example.com !今天天气真好!!!</div>'
print(full_clean_pipeline(sample))
# 输出:欢迎访问 !今天天气真好!!!

你看,清洗完之后,文本干净多了。模型拿到这样的数据,学习效率会高很多。

最后提醒一句:清洗的顺序很重要。我建议先做去标签和去URL,再做特殊字符处理,然后统一大小写,最后清空白。顺序乱了,可能会把有用的信息误删掉。

嗯,文本清洗基础就这些。下一章咱们聊聊更高级的清洗技巧,比如处理表情符号、纠正拼写错误这些。到时候再细聊。