Python正则表达式入门从模式匹配到数据提取实战文章目录Python正则表达式入门从模式匹配到数据提取实战前言一、re 模块快速上手1.1 第一个正则示例1.2 原生字符串 r...二、核心匹配函数2.1 search()搜索第一个匹配2.2 match()从字符串开头匹配2.3 findall()查找所有匹配2.4 finditer()返回迭代器推荐大文本使用2.5 sub()替换匹配内容三、常用正则模式速查3.1 字符类3.2 量词3.3 边界与锚点3.4 贪婪与非贪婪四、捕获组与高级用法4.1 分组捕获4.2 命名分组4.3 前瞻与后顾4.4 常用模式速查表五、编译正则提升性能六、实战案例简易网页爬虫辅助工具总结✅ 亮点总结适用场景扩展方向前言正则表达式Regular Expression是处理文本的瑞士军刀。无论是验证用户输入、提取网页数据、清洗日志文件还是批量替换文本正则表达式都能用几行代码完成看似复杂的任务。Python内置的re模块提供了完整的正则表达式支持。学习正则表达式的现实意义在面试中正则表达式是常见考点在爬虫开发中它是数据提取的核心工具在日志分析中它是错误排查的利器。然而正则表达式也有一个名声——它看起来像天书容易把简单问题复杂化。本文的目标是让你掌握正则的核心套路学会用合适的工具做合适的事避免陷入过度依赖正则的陷阱。本文将从零带你掌握正则表达式的核心用法。一、re模块快速上手1.1 第一个正则示例正则表达式最简单的入门方式就是从一个具体的例子开始。下面这个例子演示了如何从一段文本中提取手机号码——这是正则表达式最经典的应用场景之一。importre text我的手机号是13812345678请尽快联系我# 匹配中国大陆手机号patternr1[3-9]\d{9}matchre.search(pattern,text)ifmatch:print(f找到手机号:{match.group()})# 找到手机号: 13812345678print(f起始位置:{match.start()})# 7print(f结束位置:{match.end()})# 18关键解析re.search()在整个字符串中搜索第一个匹配返回一个Match对象。如果没找到匹配它返回None——所以在生产代码中务必先检查返回值是否为None否则直接调用.group()会抛出AttributeError。1.2 原生字符串r...正则表达式中大量使用反斜杠使用原生字符串r...可以避免转义混乱# 繁琐的普通字符串pattern1\\d\\.\\d# 简洁的原生字符串推荐pattern2r\d\.\dprint(pattern1pattern2)# True二、核心匹配函数re模块提供了多个匹配函数各自有不同的语义和使用场景。理解它们的区别是正则入门的关键。常见面试题“match()和search()有什么区别”——答案很简单match()只从字符串开头匹配search()搜索整个字符串。2.1search()搜索第一个匹配text价格¥19.9折扣价¥9.9patternr¥(\d\.\d)matchre.search(pattern,text)print(match.group())# ¥19.9print(match.group(1))# 19.9 第一个捕获组2.2match()从字符串开头匹配# match() 只匹配开头print(re.match(r\d,123abc))# re.Match objectprint(re.match(r\d,abc123))# None (不在开头)# search() 搜索任意位置print(re.search(r\d,abc123))# re.Match object2.3findall()查找所有匹配text我有苹果5个橘子12个香蕉8个patternr(\w)(\d)个# 有捕获组时返回元组列表resultre.findall(pattern,text)print(result)# [(苹果, 5), (橘子, 12), (香蕉, 8)]forfruit,countinresult:print(f{fruit}:{count}个)2.4finditer()返回迭代器推荐大文本使用textPython 3.9, Python 3.10, Python 3.11patternrPython (\d\.\d)formatchinre.finditer(pattern,text):print(f版本{match.group(1)}位于位置{match.start()})2.5sub()替换匹配内容# 脱敏手机号text联系人: 张三 13812345678, 李四 13987654321maskedre.sub(r(\d{3})\d{4}(\d{4}),r\1****\2,text)print(masked)# 联系人: 张三 138****5678, 李四 139****4321# 使用函数进行替换defcensor(match):wordmatch.group()returnword[0]**(len(word)-1)text这个项目简直太棒了效率非常高resultre.sub(r[了得],censor,text)print(result)# 这个项目简直太棒*效率非常高三、常用正则模式速查正则表达式的元字符是构建匹配模式的积木。这些符号初看可能很抽象但每个都有明确的使用场景。学习技巧不要试图一次记住所有元字符——先熟练\d、\w、.、*、、?这几个最常用的剩下的需要时再查即可。3.1 字符类patterns{r\d:匹配任意数字 [0-9],r\D:匹配任意非数字,r\w:匹配字母数字下划线 [a-zA-Z0-9_],r\W:匹配非字母数字下划线,r\s:匹配空白字符空格、制表符、换行等,r\S:匹配非空白字符,r.:匹配任意字符除换行符,}testA 1 _ 中 print(re.findall(r\d,test))# [1]print(re.findall(r\w,test))# [A, 1, _, 中]print(re.findall(r\s,test))# [ , , , ]3.2 量词test_cases{ra*:a出现0次或多次,ra:a出现1次或多次,ra?:a出现0次或1次,ra{3}:a恰好出现3次,ra{2,4}:a出现2到4次,ra{2,}:a出现至少2次,}print(re.findall(ra*,baaac))# [, aaa, , ]print(re.findall(ra,baaac))# [aaa]print(re.findall(ra{2,3},baaaaac))# [aaa]print(re.findall(r\d{3,4},电话: 010-1234567))# [010, 1234]3.3 边界与锚点边界匹配是正则表达式中的重要概念它不消耗字符只匹配位置。^匹配行首$匹配行尾\b匹配单词边界。理解边界匹配对于精确匹配非常重要——比如你想匹配独立的单词 “cat” 而不是 “category” 中的 “cat”就需要用到\bcat\b。re.MULTILINE标志让^和$匹配每行的开头和结尾而非整个字符串的开头和结尾。test第一行 第二行 第三行# ^ 匹配行首$ 匹配行尾print(re.findall(r^第.,test,re.MULTILINE))# [第一, 第二, 第三]# \b 匹配单词边界textcat category scatter catprint(re.findall(r\bcat\b,text))# [cat, cat]3.4 贪婪与非贪婪这是正则表达式中最经典的坑之一。贪婪匹配是正则的默认行为——量词*,,?,{}会尽可能多地匹配字符。这在很多场景下会导致意料之外的结果比如在HTML中使用.*匹配时可能跨过多个标签。非贪婪匹配通过在量词后加?实现如*?,?,??它会尽可能少地匹配。一个实用的经验法则在HTML/XML解析、引号内容提取等场景中几乎总是应该使用非贪婪匹配。htmldiv内容1/divdiv内容2/div# 贪婪匹配默认匹配尽可能多的内容greedyre.findall(rdiv.*/div,html)print(greedy)# [div内容1/divdiv内容2/div]# 非贪婪匹配加 ? 匹配尽可能少的内容lazyre.findall(rdiv.*?/div,html)print(lazy)# [div内容1/div, div内容2/div]四、捕获组与高级用法4.1 分组捕获# 提取日期text今天是2024-01-15明天是2024-01-16patternr(\d{4})-(\d{2})-(\d{2})formatchinre.finditer(pattern,text):year,month,daymatch.groups()print(f{year}年{month}月{day}日)4.2 命名分组# 使用 (?Pname...) 给分组命名patternr(?Pyear\d{4})-(?Pmonth\d{2})-(?Pday\d{2})text出生日期: 1995-08-22matchre.search(pattern,text)ifmatch:print(match.group(year))# 1995print(match.group(month))# 08print(match.group(day))# 22print(match.groupdict())# {year: 1995, month: 08, day: 22}4.3 前瞻与后顾# 正向肯定前瞻 (?...)后面必须跟着...text100元 200美元 300元 400欧元# 匹配后面跟着元的数字resultre.findall(r\d(?元),text)print(result)# [100, 300]# 正向肯定后顾 (?...)前面必须是...# 匹配前面是$的数字text商品: $199, $299, ¥599resultre.findall(r(?\$)\d,text)print(result)# [199, 299]# 负向前瞻 (?!...)后面不能跟着...textPython3 Python2 Pythonresultre.findall(rPython(?!\d),text)print(result)# [Python] (不匹配后面有数字的)4.4 常用模式速查表模式含义示例\d{3,4}3-4位数字区号[\u4e00-\u9fa5]中文字符匹配中文[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}Email地址基本邮箱匹配1[3-9]\d{9}中国大陆手机号13812345678\d{17}[\dXx]18位身份证身份证号https?://[^\s]URL网址五、编译正则提升性能频繁使用的正则表达式应该预编译。re.compile()将正则模式编译为一个正则对象后续可以反复使用避免每次调用re.match()时重复解析和编译。在大批量文本处理中预编译能带来显著的性能提升——在循环内重复使用同一个正则表达式时编译版本比未编译版本快30%-50%。最佳实践将编译后的正则对象定义为模块级别的常量在整个程序中复用。importtime# 预编译email_patternre.compile(r^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}$)emails[userexample.com,invalid-email,testcompany.co.uk,nodomain,goodemail.org,]foremailinemails:is_validbool(email_pattern.match(email))status✓ifis_validelse✗print(f{status}{email})# 性能测试test_texthelloworld.com *10000starttime.time()for_inrange(1000):re.match(r^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}$,testexample.com)print(f未编译耗时:{time.time()-start:.3f}s)starttime.time()for_inrange(1000):email_pattern.match(testexample.com)print(f编译后耗时:{time.time()-start:.3f}s)六、实战案例简易网页爬虫辅助工具下面的实战案例将前面学到的知识整合成几个实用的函数——邮箱提取、URL提取、HTML表格解析、敏感信息脱敏。这些函数都是实际爬虫和数据清洗项目中可以直接复用的工具代码。importredefextract_emails(text):从文本中提取所有邮箱地址patternre.compile(r\b[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}\b)returnpattern.findall(text)defextract_urls(text):从文本中提取所有URLpatternre.compile(rhttps?://[^\s\]|www\.[^\s\])returnpattern.findall(text)defparse_html_table(html):解析简易HTML表格# 提取所有行rowsre.findall(rtr(.*?)/tr,html,re.DOTALL)table_data[]forrowinrows:# 提取每行中的单元格cellsre.findall(rt[dh](.*?)/t[dh],row,re.DOTALL)# 清理HTML标签clean_cells[re.sub(r.*?,,cell).strip()forcellincells]table_data.append(clean_cells)returntable_datadefmask_sensitive_info(text):脱敏处理隐藏身份证号、手机号、银行卡号textre.sub(r(\d{3})\d{4}(\d{4}),r\1****\2,text)textre.sub(r(\d{6})\d{8}(\d{4}),r\1********\2,text)textre.sub(r(\d{4})\s?(\d{4})\s?(\d{4})\s?(\d{4}),r\1 **** **** \4,text)returntext# 测试sample 联系人: 张三, 邮箱: zhangtest.com 官网: https://www.example.com 手机: 13812345678 身份证: 320102199001011234 table trth姓名/thth分数/th/tr trtd张三/tdtd95/td/tr trtd李四/tdtd88/td/tr /table print(提取邮箱:,extract_emails(sample))print(提取URL:,extract_urls(sample))print(解析表格:,parse_html_table(sample))print(脱敏后:,mask_sensitive_info(sample))总结正则表达式是文本处理的利器Pythonre模块的核心APIsearch()查找第一个匹配findall()查找所有匹配sub()替换匹配内容split()按模式分割字符串使用r...原生字符串避免转义困扰频繁使用的正则用re.compile()预编译提升性能非贪婪匹配*?和?在HTML解析等场景中非常实用正则表达式初看可能有些晦涩但一旦掌握处理文本的效率和优雅程度都会显著提升。建议收藏本文用作速查参考。下一篇我们将进入多线程与并发编程的世界。✅ 亮点总结系统讲解re模块五大核心函数search/findall/sub/split/match附带速查表元字符、量词、分组、零宽断言循序渐进从基础模式到复杂匹配预编译re.compile()提升性能非贪婪匹配解决 HTML 解析中的常见坑实战案例邮箱/URL 提取、敏感信息脱敏、HTML 表格解析即学即用适用场景数据清洗从日志/爬虫结果中提取结构化字段日期、IP、金额等表单验证校验用户输入的手机号、邮箱、身份证号等格式合法性代码重构批量替换项目中的旧 API 调用、统一代码风格扩展方向学习正则表达式的性能优化了解回溯陷阱和原子组结合re的 flags 参数掌握多行匹配、忽略大小写等高级模式探索regex第三方库支持更丰富的正则特性如递归匹配、模糊匹配