第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' |
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 | 匹配空白字符 | 空格、制表符、换行 |
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) # 我的电话是***-****-****,备用号是***-****-****
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的入门第一关。