影刀RPA 正则表达式实战进阶:分组断言替换的高级用法

发布时间:2026/7/19 9:49:23
影刀RPA 正则表达式实战进阶:分组断言替换的高级用法 影刀RPA 正则表达式实战进阶分组断言替换的高级用法作者林焱什么情况用这个会用\d匹配数字、.*匹配任意内容你的正则入门已经过了。但实际采集数据时经常遇到这样的需求从订单号ORD20250315001金额199元中只提取订单号和金额不要前后的文字从HTML中提取所有链接的href但不包含其他属性把张三13800138000拆成姓名和手机号。这些场景靠\d和.*解决不了。你需要正则的进阶武器——分组、断言、替换。这三样学好你能应对90%以上的文本提取和清洗需求。怎么做第一步捕获分组——精确提取你想要的分组用()包裹提取时只拿括号内的内容importre text订单号ORD20250315001金额199.00元# 不用分组只能匹配整个模式resultre.search(r订单号[A-Z]\d金额[\d.]元,text)print(result.group())# 订单号ORD20250315001金额199.00元 ← 整串# 用分组分别提取订单号和金额resultre.search(r订单号([A-Z]\d)金额([\d.])元,text)ifresult:order_idresult.group(1)# ORD20250315001amountresult.group(2)# 199.00print(f订单号:{order_id}, 金额:{amount})命名分组——数字编号分不清时用名字# 命名分组 (?Pname...)resultre.search(r订单号(?Porder_id[A-Z]\d)金额(?Pamount[\d.])元,text)ifresult:print(result.group(order_id))# ORD20250315001print(result.group(amount))# 199.00# 还可以用字典形式print(result.groupdict())# {order_id: ORD..., amount: 199.00}第二步非捕获分组——只做条件不做提取拼多多店群自动化报活动上架有时你需要用括号做条件限制但不想要这个分组被捕获# (?:...) — 非捕获分组# 匹配19.9元或199元但不捕获元text价格19.9元优惠价15.5元# 普通分组捕获了单位resultre.findall(r([\d.])(元),text)![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/63c1c8014ac34a28b8ea19233ded872e.png#pic_center)print(result)# [(19.9, 元), (15.5, 元)] ← 单位也被捕获了# 非捕获分组只捕获价格resultre.findall(r([\d.])(?:元),text)print(result)# [19.9, 15.5] ← 只有价格第三步贪婪匹配 vs 非贪婪匹配这是新手最容易写错的地方htmldiv第一个div/divdiv第二个div/div# 贪婪匹配默认尽可能多地匹配resultre.findall(rdiv(.*)/div,html)print(result)# [第一个div/divdiv第二个div] ← 不是你想要的# 非贪婪匹配加?尽可能少地匹配resultre.findall(rdiv(.*?)/div,html)print(result)# [第一个div, 第二个div] ← 对了# 规则.* → 贪婪多吃.*? → 非贪婪少吃实战例子——提取所有链接的URLhtml a href/page1 classlink页面1/a a href/page2 classlink页面2/a a href/page3页面3/a # 非贪婪匹配hrefurlsre.findall(rhref(.*?),html)print(urls)# [/page1, /page2, /page3]第四步断言——只看不吃的匹配断言lookaround让你的正则检查某个条件是否满足但不消耗字符# 正向前瞻 (?...) — 后面跟着什么# 匹配元前面的数字text价格199元运费10元pricesre.findall(r[\d.](?元),text)print(prices)# [199, 10]# 正向后顾 (?...) — 前面是什么# 匹配¥后面的数字text¥199.00¥10.00pricesre.findall(r(?¥)[\d.],text)print(prices)# [199.00, 10.00]# 负向前瞻 (?!...) — 后面不是什么# 匹配不是元结尾的数字text价格199元数量50件重量30kgnumsre.findall(r\d(?!元),text)print(nums)# [19, 50, 30] ← 199的9也被匹配了# 负向后顾 (?!...) — 前面不是什么# 匹配不在¥后面的数字text原价¥199现价99pricesre.findall(r(?!¥)\b\d\b,text)print(prices)# [99]实战提取中文姓名后的手机号不提取固话号text张三13800138000李四010-12345678王五13900139000# 后顾断言前面是中文名2-4个汉字phonesre.findall(r(?[\u4e00-\u9fa5]{2,4})\d{11},text)print(phones)# [13800138000, 13900139000]# 固话010-12345678不符合11位被排除了第五步re.sub的高级替换# 基础替换text价格199元resultre.sub(r元,¥,text)# 价格199¥# 替换时引用分组——用 \1, \2 引用捕获的内容textORD-2025-0315resultre.sub(r(\w)-(\d{4})-(\d{4}),r\1_\2\3,text)print(result)# ORD_20250315# 替换中使用函数——复杂的转换逻辑defformat_price(match):格式化价格去掉多余的0pricefloat(match.group(1))ifpriceint(price):returnf¥{int(price)}returnf¥{price}text价格1199.00元价格2299.50元价格3100.00元resultre.sub(r价格\d([\d.])元,format_price,text)print(result)# ¥199¥299.5¥100# 实战HTML标签清洗defstrip_html(text):去除HTML标签保留文本# 去除所有标签textre.sub(r[^],,text)# 替换HTML实体textre.sub(rnbsp;, ,text)textre.sub(ramp;,,text)textre.sub(rlt;,,text)textre.sub(rgt;,,text)# 压缩多个空格textre.sub(r\s, ,text)returntext.strip()第六步常见的实战模式# 1. 提取邮箱emailsre.findall(r[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,},text)# 2. 提取URLurlsre.findall(rhttps?://[^\s\],text)# 3. 提取身份证号18位id_cardsre.findall(r\d{17}[\dXx],text)# 4. 提取日期多种格式datesre.findall(r\d{4}[-/年]\d{1,2}[-/月]\d{1,2}日?,text)# 5. 提取金额含货币符号amountsre.findall(r[¥$]\s*[\d,]\.?\d*,text)# 6. 分词——提取所有中文字符chinesere.findall(r[\u4e00-\u9fa5],text)# 7. 提取键值对kv_patternr(\w)\s*[:]\s*([^,\s])pairsre.findall(kv_pattern,name:张三, age:25, city:深圳)# [(name, 张三), (age, 25), (city, 深圳)]有什么坑坑一.不匹配换行符现象.*匹配不到跨行的内容。解决加re.DOTALL标志text第一行\n第二行\n第三行# 默认 . 不匹配 \nprint(re.findall(r(.*),text))# 只匹配到每行# re.DOTALL 让 . 匹配包括 \n 的所有字符print(re.findall(r第一行(.*)第三行,text,re.DOTALL))# 匹配到了坑二\w不匹配中文# \w 默认只匹配 [a-zA-Z0-9_]print(re.findall(r\w,用户名user123))# [user123] ← 中文丢了# 解决用Unicode属性或明确指定范围[video(video-n3ps2nMg-1784395810971)(type-csdn)(url-https://live.csdn.net/v/embed/526817)(image-https://v-![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/226229b63d1a4e8c9e884e0cb4e8a47c.png#pic_center)blog.csdnimg.cn/asset/1d3c3709da119dd8c13ab01e9b282520/cover/Cover0.jpg)(title-TEMU店群矩阵自动化运营核价报活动)]print(re.findall(r[\w\u4e00-\u9fa5],用户名user123))# [用户名user123]坑三后顾断言不支持不定长现象(?订单号\w*)这种写法报错。原因Python的re模块中后顾断言(?...)必须是定长的。解决用regex模块代替re支持不定长后顾或改用捕获分组importregex# pip install regex# regex模块支持不定长后顾resultregex.findall(r(?订单号\w{3,})\d,text)坑四正则太复杂没法维护现象写了一个100个字符的正则表达式一星期后自己看不懂了。解决用re.VERBOSE模式写注释patternre.compile(r (?Pprotocolhttps?):// # 协议 (?Pdomain[a-zA-Z0-9.-]) # 域名 (?Pport:\d)? # 端口可选 (?Ppath/[^\s]*) # 路径 ,re.VERBOSE)坑五正则表达式匹配太慢现象在大文本上用复杂正则流程卡住不动。原因正则引擎的回溯可能导致指数级时间复杂度。解决避免在大量文本上用.*再跟一个精确模式尽量用更精确的前缀缩小匹配范围。总结正则从入门到进阶的分水岭就三个分组精确提取、非贪婪避免过度匹配、断言只检查不消费。把这三个组合起来90%的文本处理任务一行正则搞定。