影刀RPA 正则表达式入门文本提取的利器作者林焱做RPA绕不开文本处理而正则表达式是文本处理里最强大的工具。提取手机号、清洗网页文本、验证邮箱格式、解析URL参数……这些用字符串方法写几十行代码的事正则一行搞定。但正则也是很多人的噩梦——一堆符号看不懂、写了匹配不上、匹配上了但多匹配了。这篇文章用RPA实战场景讲正则不讲理论只讲用法。一、什么情况用什么场景正则表达式说明提取手机号\d{11}11位数字提取邮箱[\w.][\w.]简单邮箱匹配提取URLhttps?://[\w./?-]HTTP/HTTPS链接提取金额¥\d\.?\d*人民币金额提取日期\d{4}-\d{2}-\d{2}YYYY-MM-DD验证是否纯数字^\d$全数字替换HTML标签[^]匹配所有HTML标签分割复杂文本[,;\s]逗号/分号/空格分割提取括号内容\(([^)])\)提取圆括号里的内容二、怎么做2.1 正则基础速查先记住这些最常用的符号\d → 匹配数字 (0-9) \w → 匹配字母数字下划线 (a-zA-Z0-9_) \s → 匹配空白字符 (空格/制表符/换行) . → 匹配任意字符除换行 → 前面的出现1次或多次 * → 前面的出现0次或多次 ? → 前面的出现0次或1次 {n} → 前面的出现恰好n次 {n,m} → 前面的出现n到m次 [video(video-CwdASn1q-1784395628028)(type-csdn)(url-https://live.csdn.net/v/embed/525010)(image-https://v-blog.csdnimg.cn/asset/f4faa587144cb7070f19e8b36813806b/cover/Cover0.jpg)(title-店群矩阵自动化突破运营极限)] ^ → 开头 $ → 结尾 [] → 字符集合匹配其中任意一个 () → 分组提取匹配的部分 | → 或 \ → 转义特殊字符2.2 在影刀中使用正则用Python的re模块在Python代码节点里importre# 提取 # 提取所有手机号text联系方式13812345678备用15987654321客服400-123-4567phonesre.findall(r1[3-9]\d{9},text)print(f手机号:{phones})# [13812345678, 15987654321]# 提取邮箱text邮箱testexample.com 和 admincompany.cnemailsre.findall(r[\w.][\w.]\.\w,text)print(f邮箱:{emails})# [testexample.com, admincompany.cn]# 提取金额text商品A ¥99.5商品B ¥1,299.00商品C ¥50pricesre.findall(r¥([\d,]\.?\d*),text)print(f金额:{prices})# [99.5, 1,299.00, 50]# 提取日期text发布日期2026-06-15更新日期2026-06-20datesre.findall(r\d{4}-\d{2}-\d{2},text)print(f日期:{dates})# [2026-06-15, 2026-06-20]# 提取URLtext访问 https://www.example.com/path?id123 或 http://test.cn 获取详情urlsre.findall(rhttps?://[\w./?-],text)print(fURL:{urls})2.3 提取带分组的数据用括号()可以提取匹配结果中的特定部分importre# 提取键值对textname张三 age25 city北京patternr(\w)(\S)matchesre.findall(pattern,text)resultdict(matches)print(result)# {name: 张三, age: 25, city: 北京}# 提取链接和文本htmla hrefhttps://example.com示例链接/aa hrefhttps://test.cn测试/apatternra href([^])([^])/alinksre.findall(pattern,html)forurl,textinlinks:print(f链接:{url}, 文本:{text})# 提取括号内的内容text商品名称红色和 商品名称蓝色patternr([^])contentsre.findall(pattern,text)print(f括号内容:{contents})# [红色, 蓝色]2.4 替换文本importre# 替换敏感信息text手机号13812345678身份证110101199001011234# 手机号中间4位替换为*maskedre.sub(r(1[3-9]\d)\d{4}(\d{4}),r\1****\2,text)print(masked)# 手机号138****5678身份证110101199001011234# 身份证中间替换为*masked_idre.sub(r(\d{6})\d{8}(\d{4}),r\1********\2,text)print(masked_id)# 手机号13812345678身份证110101********1234# 去除HTML标签htmlp这是b加粗/b的i文本/i/pclean_textre.sub(r[^],,html)print(clean_text)# 这是加粗的文本# 去除多余空白text 多个 空格 和\n\n换行 cleanedre.sub(r\s, ,text).strip()print(cleaned)# 多个 空格 和 换行2.5 验证格式importredefvalidate_phone(phone):验证手机号格式patternr^1[3-9]\d{9}$returnbool(re.match(pattern,phone))defvalidate_email(email):验证邮箱格式patternr^[\w.][\w.]\.\w$returnbool(re.match(pattern,email))defvalidate_id_card(id_card):验证身份证号格式18位patternr^\d{17}[\dXx]$returnbool(re.match(pattern,id_card))defvalidate_url(url):验证URL格式patternr^https?://[\w./?%-]$returnbool(re.match(pattern,url))# 测试print(validate_phone(13812345678))# Trueprint(validate_phone(12345678901))# False不是1开头print(validate_email(testexample.com))# Trueprint(validate_email(testexample))# False没有完整域名2.6 实战网页文本清洗采集网页数据时文本里经常有各种杂乱字符importredefclean_web_text(text):清洗网页文本ifnottext:return# 去除HTML标签textre.sub(r[^],,text)# 去除HTML实体texttext.replace(nbsp;, )texttext.replace(amp;,)texttext.replace(lt;,)texttext.replace(gt;,)texttext.replace(quot;,)textre.sub(r#(\d);,lambdam:chr(int(m.group(1))),text)# 去除多余空白textre.sub(r\s, ,text)# 去除首尾空白texttext.strip()returntext# 测试raw p商品nbsp;名称/p \n b价格/b: #165;99 cleanclean_web_text(raw)print(f清洗前: {raw})print(f清洗后: {clean})# 商品 名称 价格: ¥992.7 实战解析结构化文本importredefparse_log_line(log):解析日志行# 格式: [2026-06-15 09:30:45] [INFO] [flow_name] 消息内容patternr\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(\w)\] \[([^\]])\] (.)matchre.match(pattern,log)ifmatch:return{timestamp:match.group(1),level:match.group(2),source:match.group(3),message:match.group(4)}returnNonelog[2026-06-15 09:30:45] [INFO] [商品采集] 开始采集第1页parsedparse_log_line(log)print(parsed)# {timestamp: 2026-06-15 09:30:45, level: INFO, source: 商品采集, message: 开始采集第1页}defparse_url_params(url):解析URL参数# 提取查询字符串matchre.search(r\?(.)$,url)ifnotmatch:return{}query_stringmatch.group(1)params{}# 分割参数对forpairinquery_string.split():ifinpair:key,valuepair.split(,1)params[key]valuereturnparams urlhttps://example.com/search?keywordrpapage3sortprice_descparamsparse_url_params(url)print(params)# {keyword: rpa, page: 3, sort: price_desc}三、有什么坑坑1贪婪匹配导致多提取正则默认是贪婪的——会尽可能多地匹配importre textdiv内容A/divdiv内容B/div# 贪婪匹配错误resultre.findall(rdiv(.)/div,text)print(result)# [内容A/divdiv内容B] ← 多匹配了# 非贪婪匹配正确resultre.findall(rdiv(.?)/div,text)print(result)# [内容A, 内容B] ← 正确解决在或*后面加?变成非贪婪模式?*?坑2特殊字符没转义正则里有特殊含义的字符. * ? ^ $ { } [ ] ( ) | \ /如果要在文本中匹配这些字符本身需要转义importre# 错误直接匹配文件路径中的.textfile.txtresultre.findall(rfile.txt,text)print(result)# [file.txt] 但也会匹配 fileXtxt# 正确转义.[video(video-wE0Q4zpn-1784395634709)(type-csdn)(url-https://live.csdn.net/v/embed/524992)(image-https://v-blog.csdnimg.cn/asset/b59aed2f01d4fe8583467562aaf4dcfd/cover/Cover0.jpg)(title-temu店群自动化报活动案例)]resultre.findall(rfile\.txt,text)print(result)# [file.txt]# 更简单的方法用re.escapepatternre.escape(file.txt)resultre.findall(pattern,text)坑3中文匹配问题\w默认不匹配中文。要匹配中文需要特殊处理importre# \w不匹配中文text商品A价格99resultre.findall(r[\w],text)print(result)# [A, 99] ← 中文没匹配到# 方法一显式指定中文范围resultre.findall(r[\u4e00-\u9fff],text)print(result)# [商品, 价格]# 方法二使用re.UNICODE标志resultre.findall(r\w,text,re.UNICODE)print(result)# [商品A价格99]Python3中\w默认包含中文# 匹配中文英文数字resultre.findall(r[\u4e00-\u9fff\w],text)print(result)坑4匹配速度慢某些正则表达式在长文本上匹配极慢正则灾难importreimporttime# 灾难性正则回溯爆炸bad_patternr(a)btexta*30# 30个astarttime.time()re.match(bad_pattern,text)elapsedtime.time()-start print(f耗时:{elapsed:.3f}秒)# 可能要好几秒# 优化后的正则good_patternrabstarttime.time()re.match(good_pattern,text)elapsedtime.time()-startprint(f耗时:{elapsed:.3f}秒)# 几乎瞬间解决避免嵌套量词如(a)用更简单的写法替代。坑5跨行匹配失败默认情况下.不匹配换行符多行文本匹配会失败importre text第一行 第二行 第三行# 默认不跨行resultre.findall(r第一行.第二行,text)print(result)# [] ← 匹配失败# 方法一使用re.DOTALL标志resultre.findall(r第一行.第二行,text,re.DOTALL)print(result)# [第一行\n第二行]# 方法二用\s匹配换行resultre.findall(r第一行\s第二行,text)print(result)# [第一行\n第二行]总结正则表达式的核心要点先测再用于生产——用在线工具如regex101.com测试正则用非贪婪模式——提取内容时加?避免多匹配特殊字符要转义——用re.escape()处理用户输入中文用\u4e00-\u9fff——\w对中文的支持不一定可靠避免灾难性正则——别用嵌套量词能简化就简化多行用re.DOTALL——跨行匹配时别忘了加标志