影刀RPA 流程调试技巧全攻略:从日志到断点
作者:林焱
写RPA流程,开发只占30%的时间,剩下70%都在调试——流程跑出错了不知道哪一步出错、数据不对但不知道在哪一步被改错了、网页元素突然定位不到但不知道为什么。
这篇文章把影刀RPA里所有调试手段整理成一篇攻略,从最基础的日志输出到高级的断点调试,帮你快速定位问题。
一、什么情况用什么
| 问题类型 | 调试方法 | 说明 |
|---|---|---|
| 流程报错了看不懂 | 查看错误日志 | 影刀自带错误信息 |
| 不知道哪一步出问题 | 单步执行 | 一步一步跑,看在哪步出错 |
| 变量值不对 | 日志输出 | 在关键位置打印变量值 |
| 网页元素定位失败 | 截图+选择器检查 | 看实际页面和选择器是否匹配 |
| 流程逻辑不对 | 断点调试 | 在指定位置暂停,检查状态 |
| 性能太慢 | 耗时统计 | 每步加计时器 |
| 数据处理结果不对 | 中间结果检查 | 每步后检查输出 |
| 间歇性出错 | 详细日志+重放 | 记录所有操作,重现问题 |
二、怎么做
2.1 日志输出(最基础的调试)
影刀的日志指令:
影刀有【输出调试信息】指令,可以在流程执行时输出信息到日志面板。
【输出调试信息】→ 内容:"当前页码: " + page_num.ToString() 【输出调试信息】→ 内容:"采集到 " + data_count.ToString() + " 条数据" 用Python输出更详细的日志:
importdatetimedeflog(message,level="INFO"):"""带时间戳和级别的日志"""timestamp=datetime.datetime.now().strftime("%H:%M:%S")print(f"[{timestamp}] [{level}]{message}")# 使用log("流程开始执行")log(f"当前页码:{page_num}")log(f"采集到{len(data)}条数据","INFO")log(f"元素未找到,重试第{retry_count}次","WARNING")log(f"网络请求失败:{error_message}","ERROR")结构化日志记录:
importjsonimportdatetimeimportosclassFlowLogger:"""流程日志记录器"""def__init__(self,flow_name,log_dir=r"D:\logs"):self.flow_name=flow_name self.log_dir=log_dir os.makedirs(log_dir,exist_ok=True)# 当天日志文件today=datetime.date.today().strftime("%Y%m%d")self.log_file=os.path.join(log_dir,f"{flow_name}_{today}.log")self.steps=[]# 记录执行步骤deflog(self,step_name,message,data=None,level="INFO"):"""记录日志"""entry={"timestamp":datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),"level":level,"step":step_name,"message":message,"data":data}self.steps.append(entry)# 输出到控制台print(f"[{entry['timestamp']}] [{level}]{step_name}:{message}")# 写入文件withopen(self.log_file,'a',encoding='utf-8')asf:f.write(json.dumps(entry,ensure_ascii=False)+'\n')deferror(self,step_name,message,error=None):"""记录错误"""self.log(step_name,message,level="ERROR",data=str(error)iferrorelseNone)defsummary(self):"""输出执行摘要"""total=len(self.steps)errors=sum(1forsinself.stepsifs['level']=='ERROR')warnings=sum(1forsinself.stepsifs['level']=='WARNING')print(f"\n{'='*50}")print(f"执行摘要:{self.flow_name}")print(f"总步骤:{total}, 错误:{errors}, 警告:{warnings}")print(f"日志文件:{self.log_file}")print(f"{'='*50}")# 使用# logger = FlowLogger("商品采集")# logger.log("启动", "流程开始")# logger.log("登录", "登录成功", {"user": "admin"})# logger.error("采集", "第3页超时", "TimeoutError")# logger.summary()2.2 单步执行
拼多多店群自动化上架方案
影刀支持单步执行模式,一步一步运行流程:
操作方式:
- 在编辑器中点击「单步执行」按钮(或按快捷键)
- 流程执行一步就暂停
- 查看当前变量值和状态
- 点击「继续」执行下一步
适用场景:
- 流程逻辑复杂,不确定执行顺序
- 某一步出错了但不知道是哪步
- 变量值在某步之后变了,要找到变化点
2.3 变量监控
在调试时实时监控变量值的变化:
# 在关键位置插入变量检查点defcheck_variable(var_name,var_value,expected=None):"""检查变量值"""print(f"变量{var_name}={var_value}(类型:{type(var_value).__name__})")ifexpectedisnotNone:ifvar_value==expected:print(f" ✅ 符合预期:{expected}")else:print(f" ❌ 不符合预期!期望:{expected}, 实际:{var_value}")# 检查常见问题ifvar_valueisNone:print(f" ⚠️ 值为None")elifisinstance(var_value,str)andvar_value=="":print(f" ⚠️ 空字符串")elifisinstance(var_value,(list,dict))andlen(var_value)==0:print(f" ⚠️ 空集合")returnvar_value# 使用# page_num = 3# check_variable("page_num", page_num, expected=3)## data = []# check_variable("data", data) # 会提示空集合2.4 断点调试
在关键位置设置断点,流程执行到断点时暂停:
影刀的断点功能:
- 在指令上右键 → 设置断点
- 流程执行到该指令时暂停
- 检查当前所有变量值
- 可以手动修改变量值后继续执行
用Python实现条件断点:
# 条件断点:满足条件才暂停page_num=3total_pages=10# 只在第3页时输出详细信息ifpage_num==3:print(f"=== 断点:第{page_num}页 ===")print(f"已采集数据:{len(all_data)}条")print(f"当前URL:{current_url}")print(f"页面标题:{page_title}")# 可以在这里加input()暂停(但在影刀中不推荐)2.5 截图调试
网页自动化出错时,截图是最直接的调试方式:
importpyautoguiimportdatetimeimportosdefdebug_screenshot(name="debug",save_dir=r"D:\logs\screenshots"):"""调试截图"""os.makedirs(save_dir,exist_ok=True)timestamp=datetime.datetime.now().strftime("%Y%m%d_%H%M%S")filename=f"{name}_{timestamp}.png"filepath=os.path.join(save_dir,filename)pyautogui.screenshot(filepath)print(f"截图已保存:{filepath}")returnfilepath# 在关键步骤前后截图# debug_screenshot("before_click")# 【点击元素】# debug_screenshot("after_click")网页元素截图(用影刀的【截图】指令):
【截图】→ 截图方式:指定元素截图 目标元素:xxx 保存路径:D:\logs\screenshots\2.6 选择器调试
网页元素定位失败时,检查选择器是否正确:
# 用JavaScript检查元素是否存在js_check=""" // 检查元素是否存在 var element = document.querySelector('CSS选择器'); if (element) { return JSON.stringify({ found: true, tag: element.tagName, text: element.innerText.substring(0, 100), visible: element.offsetParent !== null, rect: element.getBoundingClientRect() }); } return JSON.stringify({found: false}); """# 在影刀的【执行JavaScript】节点里运行上面的代码# 根据返回结果判断元素状态选择器调试清单:
元素定位失败时检查: 1. 页面是否完全加载?(加等待) 2. 元素是否在iframe里?(切换iframe) 3. 元素是否需要滚动才能看到?(滚动到元素) 4. 元素是否动态加载?(等待元素出现) 5. 选择器是否因为页面改版而失效?(重新捕获) 6. 是否有多个匹配元素?(用更精确的选择器)2.7 数据流追踪
追踪数据在流程中的变化:
importjsonclassDataTracker:"""数据流追踪器"""def__init__(self):self.history=[]deftrack(self,step_name,data):"""记录数据在某步骤的状态"""# 深拷贝防止引用问题importcopy snapshot=copy.deepcopy(data)self.history.append({'step':step_name,'data':snapshot,'data_type':type(data).__name__,'count':len(data)ifhasattr(data,'__len__')elseNone})print(f"[追踪]{step_name}:{type(data).__name__}",end="")ifhasattr(data,'__len__'):print(f" (长度:{len(data)})")else:print(f" ={data}")defdiff(self,step1,step2):"""比较两个步骤的数据差异"""d1=Noned2=Noneforhinself.history:ifh['step']==step1:d1=h['data']ifh['step']==step2:d2=h['data']ifd1isNoneord2isNone:print("找不到指定步骤")returnifisinstance(d1,list)andisinstance(d2,list):added=[xforxind2ifxnotind1]removed=[xforxind1ifxnotind2]print(f"{step1}→{step2}:")print(f" 新增:{len(added)}项")print(f" 删除:{len(removed)}项")defreport(self):"""输出追踪报告"""print(f"\n===== 数据流追踪报告 =====")forhinself.history:count_str=f" (数量:{h['count']})"ifh['count']isnotNoneelse""print(f"{h['step']}:{h['data_type']}{count_str}")# 使用# tracker = DataTracker()# tracker.track("初始数据", products)# tracker.track("过滤后", filtered)# tracker.track("排序后", sorted_data)# tracker.track("去重后", unique_data)# tracker.report()2.8 性能分析
importtimefromdatetimeimportdatetimeclassPerformanceAnalyzer:"""性能分析器"""def__init__(self):self.checkpoints=[]defmark(self,name):"""标记一个性能检查点"""self.checkpoints.append({'name':name,'time':time.time(),'memory':self._get_memory()})def_get_memory(self):"""获取当前内存使用"""try:importpsutilimportos process=psutil.Process(os.getpid())returnprocess.memory_info().rss/1024/1024# MBexcept:return0defreport(self):"""输出性能报告"""iflen(self.checkpoints)<2:returnprint(f"\n===== 性能分析报告 =====")print(f"{'步骤':<20}{'耗时':<12}{'累计':<12}{'内存(MB)':<12}")print("-"*56)total_time=0foriinrange(len(self.checkpoints)):cp=self.checkpoints[i]ifi==0:elapsed=0else:elapsed=cp['time']-self.checkpoints[i-1]['time']total_time+=elapsed mem=cp['memory']print(f"{cp['name']:<20}{elapsed:>8.2f}s{total_time:>8.2f}s{mem:>8.1f}")print("-"*56)print(f"{'总耗时':<20}{'':<12}{total_time:>8.2f}s")# 找出最慢的步骤max_elapsed=0slowest_step=""foriinrange(1,len(self.checkpoints)):elapsed=self.checkpoints[i]['time']-self.checkpoints[i-1]['time']ifelapsed>max_elapsed:max_elapsed=elapsed slowest_step=self.checkpoints[i]['name']print(f"最慢步骤:{slowest_step}({max_elapsed:.2f}s)")# 使用# pa = PerformanceAnalyzer()# pa.mark("流程开始")# # ... 采集数据 ...# pa.mark("采集完成")# # ... 处理数据 ...# pa.mark("处理完成")# # ... 写入Excel ...# pa.mark("写入完成")# pa.report()三、有什么坑
坑1:日志太多找不到关键信息
流程里每一步都打日志,跑完后日志几千行,找不到出错的那行。
解决:分级日志——正常步骤用INFO,异常用WARNING,出错用ERROR。排查时先搜ERROR:
# 只看错误日志deffilter_error_logs(log_file):withopen(log_file,'r',encoding='utf-8')asf:forlineinf:[video(video-cKxRFxn7-1784482941834)(type-csdn)(url-https://live.csdn.net/v/embed/524993)(image-https://v-blog.csdnimg.cn/asset/a547123d88ad712dccba346c9217e237/cover/Cover0.jpg)(title-TEMU店群如何管理运营?)]if'"level": "ERROR"'inlineor'[ERROR]'inline:print(line.strip())坑2:调试代码忘记删
调试时加的print和临时变量,流程上线后还在运行,影响性能。
解决:用一个全局开关控制调试输出:
DEBUG=True# 调试时设为True,上线前改为Falsedefdebug_print(message):ifDEBUG:print(f"[DEBUG]{message}")# 上线前只需要改一行DEBUG=False坑3:单步执行时网页状态变化
单步执行时每步之间间隔几秒,网页可能因为超时或动态刷新而变化,导致后续步骤失败。
解决:单步调试用于检查逻辑和数据,网页操作建议用完整执行+日志的方式调试。
坑4:截图太多占满磁盘
每次出错都截图,一天下来截图占几个GB。
解决:限制截图数量,定期清理:
importosimporttimedefclean_old_screenshots(screenshot_dir,max_age_hours=24):"""清理旧截图"""ifnotos.path.exists(screenshot_dir):returncurrent_time=time.time()cleaned=0forfilenameinos.listdir(screenshot_dir):filepath=os.path.join(screenshot_dir,filename)ifos.path.isfile(filepath):file_age=current_time-os.path.getmtime(filepath)iffile_age>max_age_hours*3600:os.remove(filepath)cleaned+=1print(f"清理了{cleaned}个旧截图")坑5:间歇性错误无法复现
有些错误时有时无,调试时正常,上线后偶尔出错。
解决:加详细日志记录所有操作,出错后重放:
# 记录每次操作的完整上下文deflog_operation(op_name,**context):"""记录操作上下文"""log_entry={'operation':op_name,'timestamp':datetime.datetime.now().isoformat(),'context':context}# 写入日志文件withopen(r"D:\logs\operations.jsonl",'a',encoding='utf-8')asf:f.write(json.dumps(log_entry,ensure_ascii=False)+'\n')# 每个关键操作都记录# log_operation("click_element", selector="btn_submit", page_url=url, page_title=title)总结
调试的核心方法论:
先看错误信息——影刀的错误提示通常能直接定位问题
加日志定位——在出错位置前后加日志,缩小排查范围
截图留证据——网页操作出错时截图是最好证据
单步查逻辑——逻辑错误用单步执行逐步排查
数据流追踪——数据不对时追踪数据在每步的变化
性能分析找瓶颈——太慢就加耗时统计,找出最慢的步骤
调试代码要清理——上线前删掉或关闭调试输出