第2章:Python文本处理基础

文本处理,说白了就是跟字符串打交道。我做了这么多年NLP,发现很多新手一上来就想着用高大上的模型,结果连最基本的字符串清洗都搞不定。嗯,这章我们就从最基础的东西说起。

2.1 字符串操作:你每天都在用的基本功

Python的字符串操作,我个人觉得是所有文本处理里最实用的部分。你想想看,不管多复杂的文本分析,第一步肯定是对原始文本做清洗和预处理。

2.1.1 字符串的创建与基本操作

字符串在Python里用单引号或双引号包起来就行。我习惯用单引号,除非字符串里本身就有单引号。

text = 'Hello, 文本挖掘!'
text2 = "I'm learning NLP"  # 这里用双引号避免转义

常用的字符串方法,我列几个你一定会用到的:

方法 作用 示例
strip() 去除首尾空白 ' hello '.strip() → 'hello'
split() 按分隔符切分 'a,b,c'.split(',') → ['a','b','c']
join() 合并列表为字符串 ','.join(['a','b']) → 'a,b'
replace() 替换子串 'abc'.replace('a','x') → 'xbc'
lower()/upper() 大小写转换 'AbC'.lower() → 'abc'
我的小技巧: 处理中文文本时,split()默认按空格切分,但中文句子往往没有空格。我一般先用jieba分词,再用空格连接,最后用split()。这样处理起来特别顺手。

2.1.2 字符串格式化

格式化字符串,我推荐用f-string。Python 3.6以后就有了,简洁又直观。

name = '情感分析'
accuracy = 0.9234
print(f'模型{name}的准确率是{accuracy:.2%}')
# 输出:模型情感分析的准确率是92.34%

为什么会推荐f-string?因为它在处理复杂表达式时特别方便。我在项目中经常需要拼接路径、格式化日志,用f-string一行搞定。

2.2 正则表达式入门:从零开始

正则表达式,很多人一听就头大。其实说白了,它就是一套描述文本模式的规则。你想想看,如果让你从一堆文本里找出所有手机号,手动找肯定累死。正则就是干这个的。

2.2.1 元字符:正则的基石

我刚开始学正则时,觉得元字符特别多。但常用的其实就这几个:

元字符 含义 示例
. 匹配任意单个字符(除换行) 'a.b' 匹配 'acb'、'a1b'
* 匹配前一个字符0次或多次 'ab*' 匹配 'a'、'ab'、'abb'
+ 匹配前一个字符1次或多次 'ab+' 匹配 'ab'、'abb',不匹配 'a'
? 匹配前一个字符0次或1次 'ab?' 匹配 'a'、'ab'
\d 匹配数字 等价于 [0-9]
\w 匹配字母、数字、下划线 等价于 [a-zA-Z0-9_]
\s 匹配空白字符 空格、制表符、换行
重点: 正则里的反斜杠在Python字符串里需要转义。比如要匹配数字,得写成'\\d'。或者用原始字符串r'\d',我强烈建议用后者,少写很多反斜杠。

2.2.2 字符集与分组

字符集用方括号表示,匹配其中任意一个字符。分组用小括号,可以提取匹配到的内容。

import re

# 字符集:匹配a、b、c中的任意一个
pattern = r'[abc]'
result = re.findall(pattern, 'a1b2c3')
print(result)  # ['a', 'b', 'c']

# 分组:提取邮箱的用户名和域名
email = 'user@example.com'
pattern = r'(\w+)@(\w+\.\w+)'
match = re.search(pattern, email)
print(match.group(1))  # user
print(match.group(2))  # example.com

分组这个功能,我在做数据清洗时经常用。比如从日志里提取IP地址、时间戳,用分组一步到位。

2.3 使用re模块:实战演练

Python的re模块,就是正则表达式的执行引擎。我建议你记住四个核心函数:search、match、findall、sub。其他的用到再查。

2.3.1 核心函数详解

函数 作用 返回值
re.search() 在整个字符串中搜索第一个匹配 Match对象或None
re.match() 从字符串开头匹配 Match对象或None
re.findall() 找到所有匹配,返回列表 列表
re.sub() 替换匹配到的内容 替换后的字符串
import re

text = '我的电话是138-1234-5678,备用号是139-8765-4321'

# search:找到第一个
match = re.search(r'\d{3}-\d{4}-\d{4}', text)
print(match.group())  # 138-1234-5678

# findall:找到所有
phones = re.findall(r'\d{3}-\d{4}-\d{4}', text)
print(phones)  # ['138-1234-5678', '139-8765-4321']

# sub:替换(脱敏处理)
masked = re.sub(r'\d{3}-\d{4}-\d{4}', '***-****-****', text)
print(masked)  # 我的电话是***-****-****,备用号是***-****-****
我曾经踩过的坑: 用re.match()时,它只从字符串开头匹配。有一次我写了个爬虫,想提取网页里的价格信息,结果用match死活匹配不到。后来才发现,价格不在字符串开头。换成search()就搞定了。记住:match是「从头开始」,search是「到处找」。

2.3.2 编译正则:提升性能

如果你要多次使用同一个正则,我建议用re.compile()预编译。这样能提升性能,代码也更清晰。

import re

# 预编译
phone_pattern = re.compile(r'\d{3}-\d{4}-\d{4}')

# 多次使用
text1 = '电话:138-1234-5678'
text2 = '手机:139-8765-4321'

match1 = phone_pattern.search(text1)
match2 = phone_pattern.search(text2)

print(match1.group())  # 138-1234-5678
print(match2.group())  # 139-8765-4321

我在做日志分析时,经常要匹配几十万行数据。预编译后,速度能快30%以上。别小看这点优化,数据量大了差距很明显。

2.4 实战案例:清洗用户评论数据

光说不练假把式。我们来看一个真实场景:清洗用户评论数据。原始数据里什么都有:HTML标签、特殊符号、多余空格、表情符号。

import re

def clean_comment(text):
    # 1. 去除HTML标签
    text = re.sub(r'<[^>]+>', '', text)
    
    # 2. 去除特殊符号(保留中文、英文、数字、空格)
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', text)
    
    # 3. 合并多余空格
    text = re.sub(r'\s+', ' ', text)
    
    # 4. 去除首尾空白
    text = text.strip()
    
    return text

# 测试
raw = '<div>这个产品<b>真的</b>很好用!!!😊👍  ¥100元</div>'
cleaned = clean_comment(raw)
print(cleaned)  # 这个产品真的很好用 100元
我的建议: 清洗文本时,不要一步到位。分步骤写,每一步都打印出来看看结果。我曾经一次性写了个超长的正则,结果匹配错了,调试了半小时才发现是某个特殊字符没处理好。分步调试,省时省力。

好了,这一章的内容就到这里。字符串操作和正则表达式,是文本处理的两把瑞士军刀。你想想看,后面我们要做的分词、情感分析、文本分类,哪一步都离不开这些基础。下一章我们聊聊分词,这可是中文NLP的入门第一关。