影刀RPA新手教程:文本清洗与正则提取完全指南——手机号、邮箱、金额、日期提取实战
影刀RPA新手教程:文本清洗与正则提取完全指南——手机号、邮箱、金额、日期提取实战
本文作者:林焱 | 转载请注明出处
开篇案例:从3000条用户留言里提取手机号
去年帮一个教育机构做RPA,需求很简单:每天从后台导出用户留言,提取里面的手机号,自动加微信。
我写好流程,跑第一批3000条留言,提取出2800个手机号。
交给客户后,客户说:“怎么有800多个是空号?”
我仔细一看,提取出来的"手机号"里,有写成了138-1234-5678的,有写成了138 1234 5678的,还有写成了(021)13812345678的。
更离谱的是,有人把手机号写在了句子中间,比如"我的号码是13812345678,请加我"。
正则写得不够严谨,把座机号、快递单号也当成了手机号提取出来。
这次教训让我明白:文本清洗和正则提取,表面简单,实际坑很多。
本文所有案例,围绕"用户留言信息提取与清洗"这条真实业务线展开。
模块一:安装与准备工作
文本处理和正则提取,在影刀RPA里主要用"Python代码"指令实现。
影刀自带的正则相关指令(“匹配正则表达式”、“替换正则表达式”)也能用,但功能有限。
当需要处理复杂逻辑时,还是Python更顺手。
确保你的环境里Python版本在3.6以上(正则的某些新特性需要3.6+)。
关于Python环境的安装配置,home.linyan.cloud 上有详细的图文教程,照着做10分钟搞定。
新建流程,命名为"文本清洗与提取Demo"。
模块二:元素定位(从网页提取文本)
文本清洗的前提是:先把文本从网页或软件里提取出来。
XPath提取网页文本内容
//div[@class='user-comment']/text()这个XPath提取class为user-comment的div标签里的文本节点。
注意:XPath的text()只提取直接子文本节点,不提取子标签里的文本。
如果想提取div内所有文本(包括子标签里的),用:
string(//div[@class='user-comment'])CSS选择器提取文本
在影刀里用"获取元素属性"指令,选中元素后,用以下方式获取文本:
# 在影刀的"执行JavaScript"指令里js_code=""" return document.querySelector('.user-comment').innerText; """从Excel批量读取待处理的文本
很多时候文本存在Excel里,用影刀的"读取Excel"指令批量读取,存到一个列表变量里,再逐条处理。
模块三:变量与数据类型(正则表达式的返回类型)
用Python的re模块做正则提取,要搞清楚每个函数返回什么类型:
importre text="我的手机号是13812345678,备用号13987654321"# re.search:返回第一个匹配的Match对象,没找到返回Nonematch=re.search(r"1[3-9]\d{9}",text)ifmatch:print(match.group())# 13812345678(字符串)print(match.span())# (6, 17)(元组,起始和结束位置)# re.findall:返回所有匹配的字符串列表,没找到返回空列表phones=re.findall(r"1[3-9]\d{9}",text)[video(video-UbANf05A-1782836906410)(type-csdn)(url-https://live.csdn.net/v/embed/525010)(image-https://v-blog.csdnimg.cn/asset/f4faa587144cb7070f19e8b36813806b/cover/Cover0.jpg)(title-店群矩阵自动化突破运营极限!)]print(phones)# ['13812345678', '13987654321'](列表)# re.finditer:返回迭代器,每个元素是一个Match对象(适合大文本)forminre.finditer(r"1[3-9]\d{9}",text):print(m.group(),m.start())# re.sub:返回替换后的字符串(字符串)cleaned=re.sub(r"\s+","",text)print(cleaned)# 我的手机号是13812345678,备用号13987654321我当时踩过这个坑:把re.search()的返回值直接当字符串用。
当没找到匹配时,re.search()返回None,对None调用.group()会报AttributeError。
正确写法:
match=re.search(pattern,text)phone=match.group()ifmatchelse""模块四:流程控制(批量文本处理)
处理大量文本时,用循环配合条件判断。
场景:从留言列表里提取手机号,提取不到的单独记录
importre comments=["我的手机号是13812345678,请加我","QQ号123456789,不加微信","电话号码021-12345678","手机138-1234-5678,谢谢",]pattern=r"1[3-9]\d{9}"results=[]no_match=[]fori,commentinenumerate(comments):# 先清洗:去掉所有空格、横杠、括号cleaned=re.sub(r"[\s\-()()]","",comment)match=re.search(pattern,cleaned)ifmatch:results.append({"index":i,"comment":comment,"phone":match.group()})else:no_match.append({"index":i,"comment":comment})print(f"成功提取{len(results)}条,未提取到{len(no_match)}条")在影刀里,这个逻辑用"循环"指令遍历列表,用"如果/否则"指令判断提取结果,用"赋值"指令把结果存到新的列表变量里。
模块五:网页自动化(结合文本提取)
很多时候文本不在本地文件里,而在网页上,需要先用网页自动化把文本抓下来,再做清洗。
完整流程:抓取网页留言并提取手机号
- 用"启动浏览器"指令打开后台页面
- 用"循环"指令翻页,直到没有下一页
- 用"获取元素文本"指令提取每条留言的内容
- 把提取到的文本存到一个列表变量
- 用"Python代码"指令批量清洗和提取
- 把结果写入Excel
等待页面加载完成的技巧
网页留言往往是异步加载的,抓文本时要等内容出现。
importtime# 在影刀的"Python代码"指令里# 配合影刀的"等待元素出现"指令使用# 如果内容加载慢,可以主动等待:defwait_text_appear(element_xpath,timeout=10):""" 等待元素文本出现(内容不为空) """foriinrange(timeout):# 用影刀的"获取元素文本"指令获取文本# 这里用伪代码表示逻辑text=get_element_text(element_xpath)iftextandtext.strip():returntext time.sleep(1)returnNone模块六:数据处理——手机号提取
手机号的规则:1开头,第二位是3-9,后面9位数字,共11位。
但要注意,用户填写时会有各种格式。
严格手机号提取(只匹配纯数字11位)
importredefextract_phone(text):""" 从文本中提取手机号 先清洗格式字符,再做匹配 """# 第一步:去掉所有非数字字符,看看是否有11位连续数字以1开头digits_only=re.sub(r"\D","",text)match=re.search(r"1[3-9]\d{9}",digits_only)ifmatch:returnmatch.group()return""# 测试test_cases=["13812345678",# 标准格式"138-1234-5678",# 带横杠"138 1234 5678",# 带空格"(021)13812345678",# 带区号括号"手机:13812345678",# 带中文前缀"1381234567",# 只有10位,不是手机号"23812345678",# 2开头,不是手机号]forcaseintest_cases:result=extract_phone(case)print(f"输入:{case}-> 提取结果:{resultifresultelse'未提取到'}")排除座机号
座机号以0开头,或者是400/800开头。
如果文本里同时有手机号和座机号,要区分开:
importredefextract_phone_only(text):""" 只提取手机号,排除座机号 """# 去掉所有非数字digits_only=re.sub(r"\D","",text)# 如果以0开头,是座机号,跳过ifdigits_only.startswith("0"):return""# 如果以400或800开头,是客服号,跳过ifdigits_only.startswith("400")ordigits_only.startswith("800"):return""# 匹配手机号match=re.search(r"1[3-9]\d{9}",digits_only)returnmatch.group()ifmatchelse""模块七:数据处理——邮箱提取
邮箱的正则表达式比手机号复杂,因为邮箱格式多样。
标准邮箱提取
importredefextract_email(text):""" 从文本中提取邮箱地址 """pattern=r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"match=re.search(pattern,text)returnmatch.group()ifmatchelse""# 测试emails=["我的邮箱是zhangsan@example.com","联系我:lisi123@163.com","work.email+tag@gmail.com",# +号在邮箱里是合法的"admin@sub.domain.co.jp",# 多层级域名]foreinemails:print(extract_email(e))坑点:邮箱后面的标点符号
用户写邮箱时,经常在后面加标点符号:
我的邮箱是zhangsan@example.com。请发邮件。 联系我:lisi123@163.com,谢谢!如果直接匹配,会把句号或逗号也匹配进去。
解决方法:在正则里排除结尾的标点符号,或者在匹配后做清洗:
defextract_email_clean(text):pattern=r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"match=re.search(pattern,text)ifnotmatch:return""email=match.group()# 去掉结尾的可能附着的标点符号email=email.rstrip(".,;:!。,;:!")returnemail我当时踩过这个坑:提取出来的邮箱带了一个中文句号,发邮件时一直报"邮箱地址无效",排查了很久才发现问题。
模块八:数据处理——金额提取
金额提取的难点:格式多样,有"100元"、“¥100”、“100.00”、"一百元"等。
常见金额格式提取
importredefextract_amount(text):""" 从文本中提取金额(数字部分) 支持:100元、¥100、100.00、100.00元、1,000元 """# 匹配各种金额格式patterns=[r"¥?\s*(\d+(?:,\d{3})*(?:\.\d{2})?)\s*元?",# ¥100、100元、100.00元r"(\d+(?:,\d{3})*(?:\.\d{2})?)\s*元",# 100元r"金额[::]\s*(\d+(?:,\d{3})*(?:\.\d{2})?)",# 金额:100r"合计[::]\s*(\d+(?:,\d{3})*(?:\.\d{2})?)",# 合计:100]forpatterninpatterns:match=re.search(pattern,text)ifmatch:amount_str=match.group(1).replace(",","")returnfloat(amount_str)return0.0# 测试test_cases=["总价100元","¥199.00","金额:1,299.00","合计 299元","没有金额信息",]forcaseintest_cases:amount=extract_amount(case)print(f"输入:{case}-> 金额:{amount}")中文金额转数字(进阶)
如果遇到了"一百元"、"贰佰叁拾元"这种中文金额,需要专门处理:
defchinese_amount_to_number(text):""" 把中文金额转成数字(简化版,支持佰以内的金额) """cn_num={"一":1,"二":2,"三":3,"四":4,"五":5,"六":6,"七":7,"八":8,"九":9,"十":10}# 匹配"X百Y十Z"格式pattern=r"([一二三四五六七八九])?百"?([一二三四五六七八九])?十"?([一二三四五六七八九])?元"# 这个正则比较复杂,实际项目中建议用现成的库:pip install cn2antry:importcn2an # 提取中文金额字符串match=re.search(r"[\u4e00-\u9fff]+元",text)ifmatch:cn_amount=match.group().rstrip("元")returncn2an.cn2an(cn_amount)exceptImportError:print("请先安装cn2an库:pip install cn2an")return0模块九:数据处理——日期提取
日期格式比金额还多:2024-06-01、2024/06/01、06/01/2024、2024年6月1日……
通用日期提取
importrefromdatetimeimportdatetimedefextract_date(text):""" 从文本中提取日期,转成统一格式YYYY-MM-DD """# 定义多种日期格式的正则patterns=[(r"(\d{4})[年\-/](\d{1,2})[月\-/](\d{1,2})","%Y%m%d"),# 2024-06-01 或 2024年06月01日(r"(\d{1,2})[月\-/](\d{1,2})[日\-/](\d{4})?","%m%d%Y"),# 06/01/2024(r"(\d{4})年(\d{1,2})月(\d{1,2})日","%Y%m%d"),# 2024年6月1日]forpattern,fmtinpatterns:match=re.search(pattern,text)ifmatch:groups=match.groups()# 根据格式组装iffmt=="%Y%m%d":year,month,day=groupstry:dt=datetime(int(year),int(month),int(day))returndt.strftime("%Y-%m-%d")exceptValueError:passreturn""# 测试dates=["日期:2024-06-01","2024年6月1日","06/01/2024","订单日期20240601",# 连续数字格式]fordindates:result=extract_date(d)print(f"输入:{d}->{resultifresultelse'未识别'}")模块十:进阶技能
技能一:用正则分组同时提取多个字段
一条留言里可能同时包含姓名、手机号、地址。
用正则分组一次提取:
importre text="姓名:张三,手机13812345678,地址:广东省深圳市南山区"pattern=r"姓名[::]\s*(\S+).*?手机\s*(\d{11}).*?地址[::]\s*(\S+)"match=re.search(pattern,text)ifmatch:name=match.group(1)phone=match.group(2)address=match.group(3)print(f"姓名:{name},手机:{phone},地址:{address}")注意正则里的.*?,问号表示"非贪婪匹配",即匹配尽可能少的字符。
如果不加问号,.*会匹配到最后一个符合条件的位置,可能导致提取错误。
技能二:批量清洗特殊字符
用户留言里经常有特殊字符:emoji、不可见字符、全角半角混用。
importredefclean_text(text):""" 清洗文本:去掉emoji、不可见字符,统一全角半角 """# 去掉emoji(Unicode范围)text=re.sub(r"[\U00010000-\U0010ffff]","",text)# 去掉不可见字符(除了换行和制表符)text=re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]","",text)# 全角数字转半角forfull,halfin[("0","0"),("1","1"),("2","2"),("3","3"),("4","4"),("5","5"),("6","6"),("7","7"),("8","8"),("9","9")]:text=text.replace(full,half)# 全角字母转半角(简易版)importstring# 这个范围处理用unicode码点转换result=""forcharintext:if"\uff01"<=char<="\uff5e":# 全角字符转半角result+=chr(ord(char)-0xfee0)else:result+=charreturnresult技能三:用看看AI能不能帮上忙
如果文本太乱,正则写不出来,可以考虑调用OCR或者NLP API。
但大多数情况下,把正则写严谨就够了。
正则写不好时,先在小批量数据上测试,用re.findall()看看匹配结果,再逐步调整正则。
模块十一:平台实战
把文本提取流程部署到影刀控制台时,注意以下几点。
要点一:留言内容可能包含敏感词
有些用户留言里包含脏话或者敏感词,直接存到Excel可能在打开时触发系统的内容审查。
解决方法:在清洗流程里加一个敏感词过滤步骤,把敏感词替换成***。
temu店群自动化报活动案例
要点二:提取结果要去重
同一个手机号可能出现在多条留言里(同一个人留了多次)。
提取完后用Python的set去重:
phones=["13812345678","13987654321","13812345678"]unique_phones=list(set(phones))print(f"去重前{len(phones)}条,去重后{len(unique_phones)}条")注意:set不保证顺序。
如果需要保留第一次出现的顺序,用以下方式:
unique_phones=[]seen=set()forphoneinphones:ifphonenotinseen:unique_phones.append(phone)seen.add(phone)模块十二:系统联动与工程化规范
工程化规范一:正则表达式存到配置文件
不要把正则写在代码里,维护起来很麻烦。
创建一个regex_patterns.json配置文件:
{"phone":"1[3-9]\\d{9}","email":"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}","amount":"\\d+(?:,\\d{3})*(?:\\.\\d{2})?","date":"\\d{4}[年\\-]\\d{1,2}[月\\-]\\d{1,2}"}流程启动时读取这个文件,正则需要修改时只改配置文件。
工程化规范二:提取结果的质量报告
每次跑完提取流程,自动生成一个质量报告:
defgenerate_quality_report(total,success,details):""" 生成提取质量报告 """report=f""" 提取质量报告 ============ 总条数:{total}成功提取:{success}提取失败:{total-success}提取成功率:{success/total*100:.1f}% 失败详情: """foritemindetails:report+=f" - 第{item['index']}行:{item['text'][:30]}...\n"print(report)# 保存报告到文件withopen("extract_report.txt","w",encoding="utf-8")asf:f.write(report)速查表:正则提取常用表达式
| 目标 | 正则表达式 | 说明 |
|---|---|---|
| 手机号 | 1[3-9]\d{9} | 匹配11位手机号 |
| 邮箱 | [a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} | 标准邮箱格式 |
| 金额(数字) | \d+(?:\.\d{2})? | 匹配整数或两位小数 |
| 日期YYYY-MM-DD | \d{4}-\d{1,2}-\d{1,2} | 简单匹配,不验证合法性 |
| 中文姓名 | [\u4e00-\u9fa5]{2,4} | 2-4个汉字 |
| 身份证号 | \d{17}[\dXx] | 18位身份证 |
| 邮政编码 | \d{6} | 6位数字 |
| 去除所有空格 | \s+ | 用于re.sub替换 |
报错排查指南
报错:re.error: unterminated character set
原因:正则里有未闭合的方括号[]。
解决:检查正则里所有的[是否有对应的]。
报错:提取结果比预期少
原因:正则默认是贪婪匹配,或者用了错误的分组。
解决:在*或+后面加?改成非贪婪匹配。
总结
文本清洗和正则提取,核心是"先清洗格式、再提取内容、最后验证结果"。
三步都做到位,提取准确率就能到95%以上。
剩下的5%特殊情况,可以人工核查,不必追求100%自动化。
更多正则写法和文本处理案例,访问 home.linyan.cloud 获取。
#影刀RPA #RPA教程 #正则提取 #文本清洗 #数据提取
作者:林焱
