Python正则表达式完整实战指南:从基础到进阶(超详细代码注释)
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(rPython, text) print(match结果, result_match) # None因为开头不是Python result_match2 re.match(rHello, text) print(match结果2, result_match2) # 匹配成功 print(匹配内容, result_match2.group()) # Hello # search() - 扫描整个字符串 result_search re.search(rPython, 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 rhref[\]([^\])[\] 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, count0, flags0) # 公主号船长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 [用户A13812345678, 用户B18987654321, 用户C15500001234] 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)}) # 用户A138****5678 # 用户B189****4321 # 用户C155****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八、高级技巧零宽断言# # 零宽断言 - 正则进阶必学 # 断言只匹配位置不消耗字符 # (?...) 正向先行断言后面是... # (?!...) 负向先行断言后面不是... # (?...) 正向后行断言前面是... # (?九、re.compile() 预编译性能优化# # re.compile() - 预编译正则批量处理必用 # 同一正则重复使用时预编译可以显著提升性能 # 公主号船长Talk # import time # 模拟10万条数据 import random import string def random_text(): 生成随机文本混入手机号 chars .join(random.choices(abcdefghijklmnopqrstuvwxyz0123456789中文测试, k20)) phone f1{.join(random.choices(3456789, k1))}{.join(random.choices(string.digits, k9))} return chars phone chars data [random_text() for _ in range(10000)] phone_pattern_str r1[3-9]\d{9} # 方法1不预编译 start time.time() results1 [re.findall(phone_pattern_str, text) for text in data] t1 time.time() - start # 方法2预编译 phone_re re.compile(phone_pattern_str) start time.time() results2 [phone_re.findall(text) for text in data] t2 time.time() - start print(f不预编译{t1:.4f}秒) print(f预编译 {t2:.4f}秒) print(f性能提升{t1/t2:.1f}倍) # 结论数据量越大预编译优势越明显 # 建议循环内反复使用的正则一律用 re.compile() 提前编译十、实战案例数据清洗Pipeline# # 综合实战用正则清洗一批用户数据 # 场景清洗从网页爬取的用户联系信息 # 公主号船长Talk # raw_data [ {name: 张 三 , phone: 138-8888-9999, email: ZHANGSANGMAIL.COM, age: 25岁}, {name: 李晓明VIP, phone: (021)66667777, email: li.xiaomingcompany.com.cn, age: 30}, {name: 王芳, phone: 18987654321, email: wangfang#qq.com, age: 二十八岁}, {name: 赵六_内部, phone: 400-800-8888, email: zhao6163.com, age: 35岁}, {name: 陈七, phone: 13500001234, email: chen7工作.com, age: }, ] # 预编译所有正则 RE_NAME_CLEAN re.compile(r[\s\(\)_VIP内部]) # 清除姓名中的噪声字符 RE_PHONE_DIGITS re.compile(r\D) # 提取手机号纯数字 RE_EMAIL_VALID re.compile(r^[\w\.\-][\w\.\-]\.[a-zA-Z]{2,}$) # 验证邮箱 RE_AGE_DIGITS re.compile(r\d) # 提取年龄数字 def clean_record(record): 清洗单条用户记录 result {} # 1. 清洗姓名去除空格、括号说明等噪声 name RE_NAME_CLEAN.sub(, record[name]).strip() result[name] name if name else None # 2. 清洗手机号只保留数字验证11位手机号 phone_digits RE_PHONE_DIGITS.sub(, record[phone]) # 验证是否为合法手机号 if re.match(r^1[3-9]\d{9}$, phone_digits): result[phone] phone_digits result[phone_valid] True elif re.match(r^\d{8,}$, phone_digits): result[phone] phone_digits # 固话等 result[phone_valid] False else: result[phone] None result[phone_valid] False # 3. 邮箱转小写 验证格式 email record[email].lower() result[email] email if RE_EMAIL_VALID.match(email) else None result[email_valid] result[email] is not None # 4. 年龄提取数字 age_match RE_AGE_DIGITS.search(record[age]) if age_match: age int(age_match.group()) result[age] age if 0 age 150 else None # 异常值过滤 else: result[age] None return result # 批量清洗 print( * 60) print(用户数据清洗结果) print( * 60) for raw in raw_data: cleaned clean_record(raw) print(f\n原始{raw}) print(f清洗{cleaned}) # 统计清洗质量 cleaned_data [clean_record(r) for r in raw_data] total len(cleaned_data) valid_phones sum(1 for r in cleaned_data if r.get(phone_valid)) valid_emails sum(1 for r in cleaned_data if r.get(email_valid)) valid_ages sum(1 for r in cleaned_data if r.get(age)) print(f\n数据质量报告) print(f 总记录数{total}) print(f 有效手机{valid_phones}/{total} ({valid_phones/total*100:.0f}%)) print(f 有效邮箱{valid_emails}/{total} ({valid_emails/total*100:.0f}%)) print(f 有效年龄{valid_ages}/{total} ({valid_ages/total*100:.0f}%))十一、常用正则模板速查# # 收藏备用常用正则表达式模板 # 公主号船长Talk更多干货关注公主号 # PATTERNS { # 通讯类 手机号: r^1[3-9]\d{9}$, 固话号: r^\(?0\d{2,3}\)?[-\s]?\d{7,8}$, 邮箱: r^[\w\.\-][\w\.\-]\.[a-zA-Z]{2,}$, URL: rhttps?://[^\s]|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, 手机号), (zhangsangmail.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) # [你好世界] # ❌ 坑5re.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等速查表✅避坑指南贪婪/转义/中文/多行常见坑正则表达式刚学时觉得难熟练后会发现它是处理文本数据最高效的工具。建议收藏本文边用边查遇到具体场景多练几次就能掌握。数据分析更多干货持续更新中。