Python正则表达式完整实战指南:从基础到进阶(超详细代码注释)
正则表达式是数据分析、爬虫、文本处理中最强大的工具之一。掌握正则表达式,处理字符串效率提升10倍不夸张。本文系统整理了Python正则表达式的全套用法,从基础语法到高级技巧,每个知识点配有完整代码示例和详细注释,适合初中级开发者和数据分析师收藏。
一、环境准备与基本概念
# ============================================================
# Python正则表达式完整实战指南 - 环境准备
# 公主号:船长Talk(更多Python数据分析干货,关注公主号)
# ============================================================
import re # Python内置正则库,无需安装
import time
# re模块核心函数速查
# re.match() - 从字符串开头匹配
# re.search() - 搜索整个字符串,返回第一个匹配
# re.findall() - 返回所有匹配的列表
# re.finditer() - 返回所有匹配的迭代器
# re.sub() - 替换匹配内容
# re.split() - 按正则分割字符串
# re.compile() - 预编译正则(提升性能)
print("re模块版本:", re.__version__ if hasattr(re, '__version__') else "内置模块")
print("环境准备完成 ✅")
二、基础语法速查表
正则表达式由元字符和普通字符组成,先把核心元字符记住:
# ============================================================
# 基础元字符示例
# 公主号:船长Talk
# ============================================================
# 1. 字符类元字符
# . 匹配任意字符(除换行符)
# \d 匹配数字 [0-9]
# \D 匹配非数字
# \w 匹配字母、数字、下划线 [a-zA-Z0-9_]
# \W 匹配非\w字符
# \s 匹配空白字符(空格、制表符、换行)
# \S 匹配非空白字符
# 2. 量词(控制重复次数)
# * 匹配0次或多次
# + 匹配1次或多次
# ? 匹配0次或1次
# {n} 精确匹配n次
# {n,m} 匹配n到m次
# {n,} 匹配至少n次
# 3. 位置元字符
# ^ 字符串开头
# $ 字符串结尾
# \b 单词边界
# 4. 特殊字符
# [] 字符集合
# [^] 字符集合取反
# | 或(A|B)
# () 分组
# (?:...) 非捕获分组
# 演示:最简单的匹配
text = "2024年销售额为3,500,000元,同比增长15.6%"
# 提取所有数字(含小数点和逗号)
numbers = re.findall(r'[\d,\.]+', text)
print("提取数字:", numbers)
# 输出:['2024', '3,500,000', '15.6']
三、re.match() vs re.search() 区别详解
# ============================================================
# match vs search 核心区别
# match:只从字符串【开头】匹配
# search:扫描整个字符串,找第一个匹配
# ============================================================
text = "Hello World, Python is awesome"
# match() - 从开头匹配
result_match = re.match(r'Python', text)
print("match结果:", result_match) # None,因为开头不是Python
result_match2 = re.match(r'Hello', text)
print("match结果2:", result_match2) # 匹配成功
print("匹配内容:", result_match2.group()) # Hello
# search() - 扫描整个字符串
result_search = re.search(r'Python', text)
print("\nsearch结果:", result_search) # 找到了
print("匹配位置:", result_search.span()) # (13, 19)
print("匹配内容:", result_search.group()) # Python
# 实战建议:
# 验证格式(手机号、邮箱)→ 用 match + ^ $
# 提取内容(从长文本找关键字)→ 用 search 或 findall
四、re.findall() 批量提取数据(最常用)
# ============================================================
# findall - 数据提取最常用函数
# 公主号:船长Talk
# ============================================================
# 场景1:从日志文件提取IP地址
log_data = """
2024-01-15 10:23:45 INFO 192.168.1.100 - GET /api/data 200
2024-01-15 10:24:12 ERROR 10.0.0.55 - POST /api/login 500
2024-01-15 10:25:01 INFO 172.16.0.200 - GET /api/user 200
"""
# IP地址正则:4段数字,每段1-3位
ip_pattern = r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
ips = re.findall(ip_pattern, log_data)
print("提取到的IP:", ips)
# ['192.168.1.100', '10.0.0.55', '172.16.0.200']
# 场景2:从HTML提取所有链接
html = """
Python官网
示例页面1
相对路径
"""
# 提取href属性中的URL
url_pattern = r'href=["\']([^"\']+)["\']'
urls = re.findall(url_pattern, html)
print("\n提取到的URL:", urls)
# ['https://www.python.org', 'http://example.com/page1', '/relative/path']
# 场景3:提取中文姓名(2-4个汉字)
text = "联系人:张三(13812345678),李晓明(18987654321),王芳芳(15500001234)"
names = re.findall(r'[\u4e00-\u9fa5]{2,4}', text)
print("\n提取姓名:", names)
# ['联系人', '张三', '李晓明', '王芳芳']
# 注意:这里"联系人"也被提取了,需要结合上下文过滤
# 更精确的方式是用断言(后面会讲)
五、分组提取(超重要!)
# ============================================================
# 分组 () 的核心用法
# 分组是正则表达式最强大的功能之一
# 公主号:船长Talk
# ============================================================
# 场景:解析日志行,分别提取时间、级别、IP、状态码
log_line = "2024-01-15 10:23:45 INFO 192.168.1.100 - GET /api/data 200"
# 用分组捕获各个字段
pattern = r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) (\w+) ([\d\.]+)'
match = re.search(pattern, log_line)
if match:
print("完整匹配:", match.group(0)) # 整个匹配
print("日期: ", match.group(1)) # 第1组
print("时间: ", match.group(2)) # 第2组
print("级别: ", match.group(3)) # 第3组
print("IP: ", match.group(4)) # 第4组
# 也可以用 groups() 一次获取所有组
date, time_str, level, ip = match.groups()
print(f"\n解析结果:{date} {time_str} | {level} | {ip}")
# ---- 命名分组(更易读)----
# 用 (?P...) 给分组命名
pattern_named = r'(?P\d{4}-\d{2}-\d{2}) (?P\d{2}:\d{2}:\d{2}) (?P\w+) (?P[\d\.]+)'
match2 = re.search(pattern_named, log_line)
if match2:
print("\n命名分组提取:")
print("日期:", match2.group('date'))
print("时间:", match2.group('time'))
print("级别:", match2.group('level'))
print("IP: ", match2.group('ip'))
# 转换为字典
result_dict = match2.groupdict()
print("\n转字典:", result_dict)
六、re.sub() 文本替换(清洗神器)
# ============================================================
# re.sub() - 正则替换,数据清洗最常用
# 语法:re.sub(pattern, repl, string, count=0, flags=0)
# 公主号:船长Talk
# ============================================================
# 场景1:清洗用户输入的手机号(去掉格式符号)
phone_raw = "联系电话:138-8888-9999 或 (021) 6666-7777"
phone_clean = re.sub(r'[\s\-\(\)]', '', phone_raw)
print("清洗前:", phone_raw)
print("清洗后:", phone_clean)
# 清洗后:联系电话:13888889999或02166667777
# 场景2:去除HTML标签,提取纯文本
html_text = "这是重要内容,点击查看"
plain_text = re.sub(r']+>', '', html_text)
print("\nHTML原文:", html_text)
print("纯文本: ", plain_text)
# 纯文本:这是重要内容,点击查看
# 场景3:脱敏处理(隐藏手机号中间4位)
phones = ["用户A:13812345678", "用户B:18987654321", "用户C:15500001234"]
def mask_phone(text):
# \1 和 \2 引用第1、2个分组(保留前3位和后4位)
return re.sub(r'(\d{3})\d{4}(\d{4})', r'\1****\2', text)
for p in phones:
print(f"{p} → {mask_phone(p)}")
# 用户A:138****5678
# 用户B:189****4321
# 用户C:155****1234
# 场景4:将多种日期格式统一为 YYYY-MM-DD
dates_raw = "日期:2024/01/15、20240116、2024.01.17"
dates_clean = re.sub(r'(\d{4})[/\.]?(\d{2})[/\.]?(\d{2})', r'\1-\2-\3', dates_raw)
print("\n日期规范化:", dates_clean)
# 日期:2024-01-15、2024-01-16、2024-01-17
七、re.split() 智能分割
# ============================================================
# re.split() - 按正则分割字符串
# 比 str.split() 强大,支持多种分隔符同时分割
# 公主号:船长Talk
# ============================================================
# 场景1:按多种标点分割句子
text = "Python很好用;正则表达式很强大,掌握它,效率翻倍!数据分析必备。"
sentences = re.split(r'[;,。!?;,\.!?]', text)
# 过滤空字符串
sentences = [s.strip() for s in sentences if s.strip()]
print("分割结果:")
for i, s in enumerate(sentences, 1):
print(f" {i}. {s}")
# 场景2:解析CSV-like数据(分隔符不规范)
data_line = "张三,25, 北京 , 数据分析师 , 15000"
# 按逗号分割,同时去除前后空格
fields = [f.strip() for f in re.split(r',', data_line)]
print("\n解析字段:", fields)
# ['张三', '25', '北京', '数据分析师', '15000']
# 场景3:驼峰命名转下划线(代码工具常用)
camel_cases = ["getUserInfo", "calculateTotalRevenue", "parseHTMLContent"]
def camel_to_snake(name):
# 在大写字母前插入下划线,然后转小写
s1 = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', name)
return re.sub(r'([a-z\d])([A-Z])', r'\1_\2', s1).lower()
for name in camel_cases:
print(f"{name:30s} → {camel_to_snake(name)}")
# getUserInfo → get_user_info
# calculateTotalRevenue → calculate_total_revenue
# parseHTMLContent → parse_html_content
八、高级技巧:零宽断言
# ============================================================
# 零宽断言 - 正则进阶必学
# 断言只匹配位置,不消耗字符
# (?=...) 正向先行断言(后面是...)
# (?!...) 负向先行断言(后面不是...)
# (?"]+|www\.[^\s<>"]+',
# 身份类
"身份证": r'^\d{17}[\dXx]$',
"中文姓名": r'^[\u4e00-\u9fa5]{2,4}$',
# 时间类
"日期YYYY-MM-DD": r'^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$',
"时间HH:MM:SS": r'^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$',
"时间戳": r'^\d{10}(\.\d+)?$',
# 数值类
"整数": r'^-?\d+$',
"正整数": r'^\d+$',
"浮点数": r'^-?\d+(\.\d+)?$',
"百分比": r'^\d+(\.\d+)?%$',
"人民币": r'^¥?\d{1,3}(,\d{3})*(\.\d{1,2})?$',
# 代码类
"Python变量名": r'^[a-zA-Z_][a-zA-Z0-9_]*$',
"IPv4地址": r'^\d{1,3}(\.\d{1,3}){3}$',
"MD5哈希": r'^[a-fA-F0-9]{32}$',
"UUID": r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$',
}
# 使用方式
def validate(value, pattern_name):
pattern = PATTERNS.get(pattern_name)
if not pattern:
return False, f"未知模板:{pattern_name}"
ok = bool(re.match(pattern, str(value)))
return ok, "✅ 合法" if ok else "❌ 不合法"
# 批量测试
test_cases = [
("13812345678", "手机号"),
("12345678901", "手机号"),
("zhangsan@gmail.com", "邮箱"),
("bad#email@", "邮箱"),
("2024-01-15", "日期YYYY-MM-DD"),
("2024-13-01", "日期YYYY-MM-DD"),
]
print("正则模板验证测试:")
for value, pattern_name in test_cases:
ok, msg = validate(value, pattern_name)
print(f" {pattern_name:15s} | {str(value):25s} | {msg}")
十二、避坑指南
# ============================================================
# 正则使用常见坑点总结
# 公主号:船长Talk
# ============================================================
# ❌ 坑1:贪婪匹配 vs 非贪婪匹配
html = "加粗文字和斜体文字"
greedy = re.findall(r'', html) # 贪婪:尽可能多匹配
lazy = re.findall(r'', html) # 非贪婪:尽可能少匹配
print("贪婪匹配:", greedy)
# ['加粗文字和斜体文字'] ← 整个匹配了!
print("非贪婪: ", lazy)
# ['', '', '', ''] ← 正确
# 规律:量词 + ? 变为非贪婪(如 *? +? ??)
# ❌ 坑2:忘记转义特殊字符
# 这些字符在正则中有特殊含义,用在字面量时必须转义:
# . * + ? ^ $ { } [ ] | ( ) \
price = "商品价格:$29.99"
# 错误:re.findall(r'$\d+.\d+', price) → 匹配不到
# 正确:
found = re.findall(r'\$\d+\.\d+', price)
print("\n价格提取:", found) # ['$29.99']
# ❌ 坑3:忘记使用原始字符串 r''
# \n 在Python字符串中是换行,在正则中应该用 \\n 或 r'\n'
# 建议:正则表达式统一用 r'' 原始字符串
# ❌ 坑4:中文字符范围
# 常用汉字范围:\u4e00-\u9fa5
# 但部分生僻字和扩展字符不在此范围
# 更全范围:\u4e00-\u9fff\u3400-\u4dbf
chinese = re.findall(r'[\u4e00-\u9fa5]+', "Hello你好世界World123")
print("\n中文提取:", chinese) # ['你好世界']
# ❌ 坑5:re.DOTALL 标志(. 默认不匹配换行)
multi_line = """第一行
第二行
第三行"""
# 默认:. 不匹配换行符
r1 = re.findall(r'第.行', multi_line)
print("\n默认模式:", r1) # ['第一行', '第二行', '第三行']
# 实际上这里 . 匹配的是"一"、"二"、"三"字,没问题
# 如果要跨行匹配,需要 re.DOTALL
r2 = re.search(r'第一行.+第三行', multi_line, re.DOTALL)
print("DOTALL跨行匹配:", r2.group() if r2 else "未匹配")
print("\n✅ 正则表达式实战完成!记住:多练才能熟练")
总结
本文系统介绍了Python正则表达式的完整用法:
-
✅ 基础语法:元字符、量词、位置锚点
-
✅ 核心函数:match/search/findall/sub/split/compile
-
✅ 分组与命名分组:精准提取结构化数据
-
✅ 零宽断言:正向/负向先行、后行断言
-
✅ 性能优化:re.compile() 预编译
-
✅ 实战Pipeline:数据清洗综合应用
-
✅ 常用模板:手机/邮箱/日期/IP等速查表
-
✅ 避坑指南:贪婪/转义/中文/多行常见坑
正则表达式刚学时觉得难,熟练后会发现它是处理文本数据最高效的工具。建议收藏本文,边用边查,遇到具体场景多练几次就能掌握。
数据分析更多干货,持续更新中。