影刀RPA 正则表达式实战:文本提取与替换

📅 2026/7/19 9:35:48 👁️ 阅读次数 📝 编程学习
影刀RPA 正则表达式实战:文本提取与替换

影刀RPA 正则表达式实战:文本提取与替换

署名:林焱

什么情况用什么

RPA采集网页文本后需要提取手机号、邮箱、金额等结构化数据;日志文件中需要提取错误时间戳和错误码;Excel单元格中混杂了文字和数字需要分离。这些都离不开正则表达式。

场景推荐函数特点
提取第一个匹配re.search返回Match对象
提取所有匹配re.findall返回列表
替换匹配文本re.sub支持回调函数
分割文本re.split按模式分割

怎么做

一:常用正则模式速查

importre# 手机号phone=re.search(r'1[3-9]\d{9}',text)# 邮箱email=re.search(r'[\w.+-]+@[\w-]+\.[\w.-]+',text)# 身份证号id_card=re.search(r'\d{17}[\dXx]',text)# 金额(带千分位)amount=re.search(r'[\d,]+\.?\d*',text)# 日期date=re.search(r'\d{4}[-/年]\d{1,2}[-/月]\d{1,2}',text)# URLurl=re.search(r'https?://[\w./?=&%-]+',text)# IP地址ip=re.search(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}',text)# 中文chinese=re.findall(r'[\u4e00-\u9fa5]+',text)

二:提取数据

店群矩阵自动化突破运营极限!

importre text="订单号:DD202406150001,金额:¥1,299.00,下单时间:2024年6月15日14:30:25"# 提取订单号order_match=re.search(r'订单号[::]\s*(\S+)',text)iforder_match:order_id=order_match.group(1)print(f"订单号:{order_id}")# 提取金额(去掉¥和逗号)amount_match=re.search(r'金额[::][¥¥]\s*([\d,.]+)',text)![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/761123c3058647a3ab433ddc9e8b63e1.png#pic_center)ifamount_match:amount=float(amount_match.group(1).replace(",",""))print(f"金额:{amount}")# 提取日期时间datetime_match=re.search(r'(\d{4}年\d{1,2}月\d{1,2}日)\s*(\d{1,2}:\d{2}:\d{2})',text)ifdatetime_match:date=datetime_match.group(1)time=datetime_match.group(2)print(f"日期:{date}, 时间:{time}")

三:批量提取

importre text=""" 联系方式: 张三 13812345678 zhangsan@email.com 李四 15987654321 lisi@test.com 王五 13711223344 wangwu@example.com """# 提取所有手机号phones=re.findall(r'1[3-9]\d{9}',text)print(f"手机号:{phones}")# 提取所有邮箱emails=re.findall(r'[\w.+-]+@[\w-]+\.[\w.-]+',text)print(f"邮箱:{emails}")# 提取姓名+手机号(分组)contacts=re.findall(r'([\u4e00-\u9fa5]+)\s+(1[3-9]\d{9})\s+([\w.+-]+@[\w.-]+)',text)forname,phone,emailincontacts:print(f"{name}|{phone}|{email}")

四:文本替换

importre text="手机号13812345678,邮箱test@email.com,身份证310101199001011234"# 脱敏替换:手机号中间4位变****masked=re.sub(r'(1[3-9]\d)\d{4}(\d{4})',r'\1****\2',text)print(masked)# 输出: 手机号138****5678,邮箱test@email.com,身份证310101199001011234# 脱敏替换:身份证出生年月日变****masked=re.sub(r'(\d{6})\d{8}(\d{4})',r'\1********\2',masked)print(masked)# 输出: 手机号138****5678,邮箱test@email.com,身份证310101********1234# 脱敏替换:邮箱用户名只保留首字符masked=re.sub(r'([\w])[\w.+-]*(@[\w.-]+)',r'\1***\2',masked)print(masked)# 输出: 手机号138****5678,邮箱t***@email.com,身份证310101********1234

五:复杂场景 - 日志解析

importre log_text=""" [2024-06-15 09:30:15] INFO - 用户登录成功 user_id=1001 [2024-06-15 09:31:22] ERROR - 数据库连接失败 code=DB_TIMEOUT host=192.168.1.100 [2024-06-15 09:32:10] WARN - 请求超时 code=REQ_TIMEOUT url=https://api.example.com/data [2024-06-15 09:35:00] ERROR - 文件不存在 code=FILE_NOT_FOUND path=/data/report.xlsx """# 提取所有错误日志error_pattern=r'\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]\s+ERROR\s+-\s+(.+?)\s+code=(\w+)'errors=re.findall(error_pattern,log_text)fortimestamp,message,codeinerrors:print(f"时间:{timestamp}, 错误:{message}, 代码:{code}")# 提取IP地址ips=re.findall(r'host=(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})',log_text)print(f"IP地址:{ips}")# 提取URLurls=re.findall(r'url=(https?://[\w./?=&%-]+)',log_text)print(f"URL:{urls}")

完整流程:网页数据清洗提取

importre# yd_input: raw_textraw_text=yd_input.get("raw_text","")# 清洗HTML标签clean_text=re.sub(r'<[^>]+>','',raw_text)# 去除多余空白clean_text=re.sub(r'\s+',' ',clean_text).strip()# 提取结构化数据result={"phones":list(set(re.findall(r'1[3-9]\d{9}',clean_text))),"emails":list(set(re.findall(r'[\w.+-]+@[\w-]+\.[\w.-]+',clean_text))),"urls":list(set(re.findall(r'https?://[\w./?=&%-]+',clean_text))),"amounts":[],"dates":[],"id_cards":list(set(re.findall(r'\d{17}[\dXx]',clean_text))),}# 提取金额并转为数字amount_matches=re.findall(r'[¥¥]?\s*([\d,]+\.?\d*)\s*元',clean_text)foramtinamount_matches:try:result["amounts"].append(float(amt.replace(",","")))except:pass# 提取日期date_matches=re.findall(r'\d{4}[-/年]\d{1,2}[-/月]\d{1,2}',clean_text)result["dates"]=date_matches# 清洗后的文本result["clean_text"]=clean_text yd_output=result

有什么坑

坑一:贪婪匹配导致多提取

现象:想提取<div>内容</div>中的"内容",结果提取了从第一个<div>到最后一个</div>之间的所有内容。

原因.*默认贪婪匹配,尽可能多匹配。

解决:用.*?非贪婪匹配:

importre html='<div>内容A</div><span>中间</span><div>内容B</div>'# 错误:贪婪匹配result=re.findall(r'<div>(.*)</div>',html)print(result)# ['内容A</div><span>中间</span><div>内容B'] ❌ 多了# 正确:非贪婪匹配result=re.findall(r'<div>(.*?)</div>',html)print(result)# ['内容A', '内容B'] ✓

坑二:特殊字符未转义

现象:想匹配文本中的价格:100元,但正则写价格:100元匹配不到。

temu店群自动化报活动案例


原因:正则中.匹配任意字符,虽然这里恰好能匹配到,但如果文本是价格?100元?是正则特殊字符。

解决:用re.escape转义:

importre# 要匹配的文本包含特殊字符keyword="价格?(含税)"escaped=re.escape(keyword)# 自动转义特殊字符print(escaped)# 价格\?\(含税\)text="价格?(含税):100元"match=re.search(escaped+r'[::]\s*([\d,.]+)',text)ifmatch:print(match.group(1))# 100

坑三:中文匹配范围不准

现象:用\w匹配中文时有时成功有时失败。

原因\w在默认模式只匹配[a-zA-Z0-9_],不匹配中文。需要加re.UNICODE标志(Python3默认开启)。

解决:用Unicode范围匹配中文:

importre text="hello世界123"# 匹配中文chinese=re.findall(r'[\u4e00-\u9fa5]+',text)print(chinese)# ['世界']# 匹配中文+英文+数字all_words=re.findall(r'[\u4e00-\u9fa5\w]+',text)print(all_words)# ['hello', '世界123']# 匹配中文标点punctuation=re.findall(r'[\u3000-\u303f\uff00-\uffef]+',text)

坑四:re.sub替换时的反向引用

现象:想把2024-06-15替换为2024/06/15,但写re.sub(r'\d+-(\d+)-(\d+)', r'\d+/\1/\2', text)不工作。

原因:替换字符串中\d+是字面量不是正则。需要用分组引用。

解决

importre text="日期:2024-06-15"# 正确:用分组引用result=re.sub(r'(\d{4})-(\d{2})-(\d{2})',r'\1/\2/\3',text)print(result)# 日期:2024/06/15# 常见分组引用:# \1, \2, \3... 引用第N个分组# \g<1>, \g<2> 等价写法(更清晰)![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0aaa30638a684d6eb5cde42dccc368eb.png#pic_center)# 用回调函数做更复杂的替换defformat_date(match):year=match.group(1)month=match.group(2)day=match.group(3)returnf"{year}{int(month)}{int(day)}日"result=re.sub(r'(\d{4})-(\d{2})-(\d{2})',format_date,text)print(result)# 日期:2024年6月15日