不知道你有没有遇到过这种场景:手上有几十个甚至几百个账号,每天都要按顺序登录、执行任务、退出、再登录下一个,光重复操作就能耗掉一两个小时。我做某模拟项目X的时候就深受其苦,最后实在忍不了,用易语言配合大漠插件写了这么一套“多账号循环登录任务自动切换”的框架。账号统一从外部文本读取进超级列表框,登录流程走同一套模板化子程序,跑完一个自动清理环境再切下一个,整个过程可以无人值守。这篇文章就把这套框架从数据准备、模板设计到主循环切换、异常自愈的完整思路拆开讲一遍,重点放在循环切换逻辑和稳定运行上,希望能给同样被批量登录折磨的人一点参考。
这套东西说穿了不复杂,但细节特别多。很多人在“循环里执行登录”这一步就卡住了,要么是上一个账号没退干净导致下一个串号,要么是登录状态判断不准导致流程乱掉。我会把每一处容易踩坑的地方都单独拿出来说,并给出我实际在用的处理方式。
1. 先把需求说透:多账号循环登录到底在解决什么问题
做批量登录脚本之前,先想清楚它到底替代了哪些人工操作。我这边的情况是这样的:有大量账号需要每天定时登录某个客户端软件,每个账号登录之后还要做一点固定的任务操作,做完之后退出,换下一个账号继续。最开始是手工操作,点开客户端、输账号密码、点登录、做任务、退出,一天重复上百遍,脑子稍微走神就出错。
这套脚本要解决的核心问题有三个:
- 账号数据的集中管理。账号密码不能散落在记事本里,更不能在代码里写死。用超级列表框做可视化展示,导入、修改、查看状态都方便。
- 登录流程的复用封装。不管哪个账号,登录步骤都是一样的:启动客户端、等待窗口、输入账号密码、点击登录、判断结果。把这一套流程做成“模板子程序”,每个账号只是参数不同。
- 循环切换的自动控制。登录成功之后执行任务,任务结束之后干净退出,确认上一个账号完全离开之后再启动下一个。这是整套脚本能不能稳定跑完的关键。
整个系统可以拆成四个部分:账号数据源、超级列表框界面、登录任务模板、循环控制逻辑。大漠插件在其中承担“眼睛”和“手”的角色,负责找窗口、找图、找字、模拟键鼠操作。
| 模块 | 作用 | 关键技术点 |
|---|---|---|
| 账号数据源 | 提供账号密码原始数据 | 文本文件格式、编码处理 |
| 超级列表框 | 展示账号和运行状态 | 表项索引、列标题操作 |
| 登录任务模板 | 执行单账号登录流程 | 大漠找图、键鼠、状态判断 |
| 循环控制逻辑 | 调度全部账号 | 环境清理、失败重试、断点续跑 |
这四个部分之间是单向依赖的:循环控制从超级列表框里取一个账号,传给登录任务模板去执行,执行完再把状态回写到超级列表框,然后进入下一个表项。
我在做之前也考虑过到底要不要用数据库来存账号,后来还是坚持用文本文件。原因很简单:账号文件可以直接用任何文本编辑器查看修改,出了问题一眼就能看到,不需要额外的数据库管理工具。等账号量大到十万级再考虑数据库,几百几千个账号用文本文件完全够了。
2. 账号数据落地:从文本到超级列表框的每一处细节
账号数据是整个脚本的原料。原料出了问题,后面做得再花哨也没用。我的账号文件固定叫accounts.txt,放在脚本运行目录下,每行一个账号,账号和密码之间用制表符(Tab键)分隔。选择Tab而不是逗号或空格,是因为密码里偶尔会出现逗号和空格,用Tab基本可以避免误拆。
文件格式长这样:
13800000001 password_123 13800000002 abc@123456 # 这一行是注释,导入时会自动跳过以#开头的行为注释行,方便临时禁用一些账号又不想删掉。导入的时候我会去掉每行首尾的空格和换行符,避免因为一个看不见的空格导致登录失败。
下面是易语言导入的核心逻辑。我用的组件是扩展界面支持库里的超级列表框,表项索引从0开始,这一点和易语言的数组下标习惯一致。
子程序 导入账号文件 .局部变量 文件内容, 文本型 .局部变量 行组, 文本型, , "0" .局部变量 字段, 文本型, , "0" .局部变量 i, 整数型 .局部变量 行文本, 文本型 .局部变量 表项索引, 整数型 文件内容 = 到文本 (读入文件 (取运行目录 () + “\accounts.txt”)) 如果真 (文件内容 = “”) 返回 () 如果真结束 ' 清空超级列表框 超级列表框1.全部删除 () 行组 = 分割文本 (文件内容, #换行符, ) 计次循环首 (取数组成员数 (行组), i) 行文本 = 行组 [i] ' 去掉首尾空白 行文本 = 删首尾空 (行文本) 如果真 (行文本 = “”) 到循环尾 () 如果真结束 ' 跳过注释行 如果真 (取文本左边 (行文本, 1) = “#”) 到循环尾 () 如果真结束 字段 = 分割文本 (行文本, #TAB键, ) 如果真 (取数组成员数 (字段) < 2) 到循环尾 () 如果真结束 ' 插入一行到超级列表框 表项索引 = 超级列表框1.插入表项 (, , , , , ) 超级列表框1.置标题 (表项索引, 0, 字段 [1]) 超级列表框1.置标题 (表项索引, 1, 字段 [2]) 超级列表框1.置标题 (表项索引, 2, “等待运行”) 计次循环尾 () 状态条1.置文本 (0, “共导入 ” + 到文本 (超级列表框1.取表项数 ()) + “ 条账号”)超级列表框的列结构我会在创建窗口时定义好,一共三列:账号、密码、状态。状态列是后面循环运行的核心反馈区,用户盯着界面就能看到当前跑到哪一个账号了。
导入之后还有一个容易被忽略的问题:编码格式。我用记事本保存accounts.txt时默认是ANSI,易语言读入文件再到文本()也按ANSI处理,这没问题。但如果有人用其他编辑器把文件存成了UTF-8,读出来就会变成乱码,账号密码直接对不上。解决方式有两个:一是文件统一用ANSI编码保存;二是在导入时用多字节到文本转换命令处理UTF-8内容。我个人图省事,直接在项目说明里标注“账号文件必须保存为ANSI编码”。
3. 登录任务模板化:把“登录”变成可以复用的参数化子程序
所谓“模板”,不是复制粘贴一堆代码,而是把登录过程抽象成一个输入账号和密码、返回成功或失败的子程序。这个子程序内部执行固定的操作序列,账号和密码作为参数传入。这样主循环里就只需要干一件事:取出账号、调用模板、得到结果。
我把单账号登录模板拆成了七个步骤:
- 启动客户端程序
- 等待主窗口出现
- 定位账号输入框并点击
- 清空输入框并输入账号
- 定位密码输入框并输入密码
- 点击登录按钮
- 等待登录成功标志出现
每一步在大漠插件里都能找到对应的命令。启动客户端用运行或打开文件命令;等待窗口出现用FindWindow循环查找;输入框定位用FindPic精确找图,然后MoveTo加LeftClick点击;输入文本用KeyPressStr;登录成功判断用FindPic找特定的成功标志图,或者用FindStr识别窗口上的特定文字。
登录模板的框架大概是这样:
子程序 登录模板, 整数型 .参数 账号, 文本型 .参数 密码, 文本型 .局部变量 窗口句柄, 整数型 .局部变量 是否成功, 逻辑型 .局部变量 超时计数, 整数型 ' 第一步:启动客户端 运行 (取运行目录 () + “\client.exe”, 假, ) ' 第二步:等待主窗口出现,最长等30秒 窗口句柄 = 0 超时计数 = 0 判断循环首 (窗口句柄 = 0 且 超时计数 < 300) 窗口句柄 = 大漠.FindWindow (“”, “客户端主窗口标题”, “”, “”) 如果真 (窗口句柄 ≠ 0) 跳出循环 () 如果真结束 程序_延时 (100) 超时计数 = 超时计数 + 1 判断循环尾 () 如果真 (窗口句柄 = 0) 返回 (0) ' 没等到窗口,直接判定失败 如果真结束 ' 绑定窗口 大漠.BindWindow (窗口句柄, “dx”, “windows3”, “windows”, 0) ' 第三步到第六步:找图点击输入框、输入账号密码、点击登录 ' 这里的图片资源路径由全局变量 图片路径 指定 如果真 (点击按钮 (图片路径 + “account_input.bmp”)) 大漠.KeyPressStr (账号, 100) 如果真结束 如果真 (点击按钮 (图片路径 + “password_input.bmp”)) 大漠.KeyPressStr (密码, 100) 如果真结束 如果真 (点击按钮 (图片路径 + “login_btn.bmp”)) ' 登录按钮点击成功 如果真结束 ' 第七步:判断是否登录成功,最等15秒 超时计数 = 0 判断循环首 (超时计数 < 150) 是否成功 = 大漠.FindPic (0, 0, 2000, 2000, 图片路径 + “login_ok.bmp”, “000000”, 0.9, 0, 0) 如果真 (是否成功) 大漠.UnBindWindow () 返回 (1) 如果真结束 程序_延时 (100) 超时计数 = 超时计数 + 1 判断循环尾 () 大漠.UnBindWindow () 返回 (0)这里的点击按钮是我自己封装的子程序,内部逻辑是找图、移动到图中心、单击。封装的原因是大漠的找图命令参数比较多,每个地方都写一遍容易出错,统一封装更容易维护。
模板化的核心价值在于:账号只是数据,流程才是逻辑。流程写一次,数据可以换任何一批。如果后续登录交互方式变了,比如登录按钮位置变了,我只需要替换对应的图片素材,主循环一行都不用改。
4. 主循环切换逻辑:账号切换与任务执行的正确时序
这一节是整个项目的骨架,也是最容易写乱的地方。主循环要解决的是:如何从超级列表框第一行跑到最后一行,并且每一步都踏实稳妥。
我的核心循环逻辑用的是计次循环,循环次数等于超级列表框的表项数。为什么不直接用无限循环加判断结束?因为计次循环天然自带“跑完即止”的特性,不需要额外维护结束条件,逻辑更清晰。
子程序 开始批量运行 .局部变量 总行数, 整数型 .局部变量 当前行, 整数型 .局部变量 账号, 文本型 .局部变量 密码, 文本型 .局部变量 登录结果, 整数型 .局部变量 连续失败数, 整数型 总行数 = 超级列表框1.取表项数 () 连续失败数 = 0 计次循环首 (总行数, 当前行) 如果真 (全局_停止运行) 跳出循环 () 如果真结束 ' 取账号和密码,注意当前行和表项索引的关系 ' 计次循环变量从1开始,超级列表框索引从0开始,取标题时要减1 账号 = 超级列表框1.取标题 (当前行 - 1, 0) 密码 = 超级列表框1.取标题 (当前行 - 1, 1) 超级列表框1.置标题 (当前行 - 1, 2, “正在登录”) 超级列表框1.确保显示 (当前行 - 1) ' 调用登录模板 登录结果 = 登录模板 (账号, 密码) 如果 (登录结果 = 1) 连续失败数 = 0 超级列表框1.置标题 (当前行 - 1, 2, “任务执行中”) 执行任务模板 () ' 登录成功后执行日常任务 退出客户端进程 () ' 执行完毕,干净退出 超级列表框1.置标题 (当前行 - 1, 2, “完成”) 否则 连续失败数 = 连续失败数 + 1 超级列表框1.置标题 (当前行 - 1, 2, “登录失败”) 退出客户端进程 () ' 登录失败也要把客户端关掉 如果结束 ' 连续失败达到阈值,可能是网络或客户端出了问题,直接停下 如果真 (连续失败数 ≥ 3) 状态条1.置文本 (0, “连续失败超过3次,停止运行”) 跳出循环 () 如果真结束 ' 当前账号处理完毕,清理环境后进入下一个 清理运行环境 () 程序_延时 (随机延时, 1500, 4000) 计次循环尾 () 状态条1.置文本 (0, “批量运行结束”)这段代码里最关键的是退出客户端进程和清理运行环境这两步,它们决定了会不会串号。
串号是最恶性的故障。举个真实例子:A账号登录后任务没做完,客户端没退出,脚本已经切到B账号启动了新客户端进程。结果两个窗口同时存在,后续找图找字全乱套,可能把B账号密码输进了A账号的窗口。要防止这个问题,必须做到“当前账号完全退出并确认之后,才允许启动下一个账号”。
我这里的退出逻辑分两步:先模拟正常退出方式,比如点击客户端的关闭按钮;等待几秒后检查进程列表,如果进程还在,直接结束进程树。确认进程全部消失之后再做环境清理。
子程序 退出客户端进程 .局部变量 进程ID, 整数型 .局部变量 超时计数, 整数型 ' 模拟点击关闭按钮 点击按钮 (图片路径 + “close_btn.bmp”) 程序_延时 (500, 100, ) ' 等待进程自然退出,最多等10秒 超时计数 = 0 判断循环首 (超时计数 < 100) 如果真 (取进程ID数量 (“client.exe”) = 0) 返回 () 如果真结束 程序_延时 (100) 超时计数 = 超时计数 + 1 判断循环尾 () ' 还没退出,强制结束进程树 结束进程树 (“client.exe”) 程序_延时 (800, 100, )为什么要先模拟正常关闭再强制结束?因为有些客户端退出时要写缓存、要上报在线状态,直接强制结束可能导致账号“卡在线”状态,下次登录被服务端拒绝。先给一个正常退出机会,实在退不出去再强杀,是兼顾稳定性和干净度的做法。
5. 大漠操作中的窗口绑定与状态判断可靠性
大漠插件在这套框架里主要干三件事:找窗口、找图片、模拟键鼠。其中窗口绑定是最容易影响稳定性的环节,绑定模式选错,找图和键鼠操作就可能失效。
我经常用的绑定模式是:
大漠.BindWindow (窗口句柄, “dx”, “windows3”, “windows”, 0)第二个参数dx是图色模式,速度快、对大部分游戏客户端兼容好;第三个参数windows3是鼠标模式,模拟鼠标消息;第四个参数windows是键盘模式。这组配置从名字上也能看出来是后台操作,即脚本窗口被遮挡也能正常工作,前提是目标客户端不能有保护机制屏蔽后台消息。
如果绑定失败,找图坐标会偏或者键鼠命令无效。我的处理方式是:绑定命令返回0就重试,重试三次还失败就判定登录失败,进入失败记录逻辑。窗口句柄失效的问题则出现在客户端重启之后:旧句柄已经无效,必须重新查找窗口重新绑定。
找图判断登录状态也是一门学问。我截取“登录成功”标志图的时候,会刻意选一个颜色鲜明、周围干扰少的区域,同时设置合理的相似度参数。大漠FindPic的相似度我一般取0.9,太高了换分辨率或颜色轻微变化就找不到,太低了又容易误报。如果你发现找图时灵时不灵,先用大漠自带的取图工具保存几张现场截图仔细对比。
还有一个必须强调的点:循环执行期间,同一时间只能有一个脚本线程在操作目标窗口。很多资料推荐多线程批量登录,觉得快,但我实际跑下来的结论是得不偿失。多线程同时启动多个客户端进程,对内存和CPU的占用飙升,窗口绑定、找图资源、输入焦点互相打架,出问题的概率成倍增加。这套框架用单线程顺序执行,一个账号跑完再跑下一个,虽然慢一点,但胜在稳定,可以整夜挂机跑完几百个账号。
如果你实在想加快速度,我建议分开在多台机器上跑,而不是在一台机器上开多线程,这才是既提升吞吐量又不牺牲稳定性的方式。
6. 异常处理与断点续跑:让脚本能长时间无人值守
批量登录最怕的是什么?不是登录失败,而是登录流程卡住不动,脚本停在那里,后面的账号全部被堵住。所以我花了很大力气在超时控制和断点续跑上。
先说超时控制。模板里的每个“等待”都必须是有限等待,不能无限等。等待窗口出现我限制30秒,等待登录成功标志限制15秒,超时就判定失败并进入下一个账号。有人觉得等久一点更保险,但实际经验是,如果网络环境正常,登录成功标志最多几秒就该出现,超过15秒说明要么账号密码错了,要么客户端异常,再等下去也是浪费时间。
为了让用户能清楚看到每个账号的运行情况,我在超级列表框的状态列做了实时更新,同时写一份文本日志。日志内容包括时间戳、当前账号、执行步骤、结果,格式如下:
[2025-01-12 10:23:45] 开始运行,共导入 128 条账号 [2025-01-12 10:23:46] 第 1 个账号开始登录 [2025-01-12 10:24:02] 第 1 个账号登录成功 [2025-01-12 10:24:35] 第 1 个账号任务完成 [2025-01-12 10:24:40] 第 2 个账号开始登录日志用启动时间作为文件名,比如log_20250112_102345.txt,不会覆盖历史记录。这个日志在排查问题的时候价值极大,尤其是长期无人值守场景,第二天早上看到哪一行停住了,打开日志就能知道昨晚到底发生了什么。
断点续跑的思路也很简单:每次开始处理一个账号之前,把当前行号写入一个progress.ini配置文件。当脚本因为断电、系统重启、人为强停等原因中断后,再次运行会先读取这个文件,从上次停下的行号继续,而不是从头再来。实现上就是在计次循环开头加几行代码:
.如果真 (读配置项 (取运行目录 () + “\progress.ini”, “运行状态”, “当前行”, “1”) ≠ 到文本 (当前行)) 到循环尾 () .如果真结束 写配置项 (取运行目录 () + “\progress.ini”, “运行状态”, “当前行”, 到文本 (当前行))这算是一个轻量的“断点保护”。几百个账号跑一个多小时,万一中途断了要全部重新跑一遍,那是非常崩溃的。有了断点续跑,至少不用从头再来。
另外我还会在脚本里加一个“心跳自检”:每隔一段时间检查一个全局运行标志,如果主窗口被手动关闭或者全局停止标志被置位,脚本会在当前账号处理完后的切换间隙退出循环,保证任何情况下都能脱身。
7. 一些实际运行中的细节经验
这部分没有太多高深的大道理,全是踩出来的小坑。我把它们列在这里,每一条都是真金白银的教训。
延时必须随机化。脚本里两次操作之间的延时不要写死,比如登录成功之后固定等5000毫秒,这种节奏太机械,容易被服务端的风控系统识别为自动化痕迹。我用的是随机延时,在两秒到四秒之间随机取一个值。大漠自带的延时命令可以加随机范围,或者我用取随机数自己算。
图片素材路径统一管理。所有登录相关的图片资源放在一个pic目录里,脚本启动时读取到大漠路径。大漠的SetPath命令指定图片目录,之后所有FindPic里的图片路径都基于这个目录。这样做的好处是换图片素材时不需要改代码,替换文件同名覆盖即可。
运行前先小规模试跑。批量跑300个账号之前,我会先抽5个账号试跑一遍,确认登录成功标志图片有效、任务执行流程正常、退出进程干净。试跑通过之后才开始全量运行。全量运行的前20分钟我会盯着界面看一遍,确认没有明显卡住或跑飞的情况再去忙别的。
防止误操作和误关闭。脚本主窗口我会设置禁用右上角关闭按钮,改用托盘图标退出,防止手滑把正在运行的脚本点掉。同时注册一个全局热键,比如F9停止、F10继续。热键响应放在消息循环里,不用窗口焦点也能触发。
日志和界面状态要互相配合。超级列表框的状态列是给人看的,文本日志是给人排查的,两者都不能省。状态列能让你一眼看到当前卡在第几个账号,文本日志能让你回溯整个运行过程。我见过有人只写界面状态不写日志,程序崩溃后完全不知道之前发生了什么,这种损失一次就够你长记性了。
8. 扩展思路:模板参数化之后还能做什么
登录任务模板一旦跑通,扩展能力是很强的。现在模板里写死的是“启动客户端、输账号密码、点登录、执行任务”,这些步骤其实可以进一步参数化。比如不同批次的账号可能需要不同的登录入口,这时可以把客户端路径、窗口标题、图片资源路径、任务类型都写进配置文件,做成多模板配置。
我后来在项目里就是这么干的:template.ini里定义每个模板的名称、客户端路径、窗口标题、素材目录、超时时间。账号文件里加一个字段指定该账号使用哪个模板。超级列表框里加一列“模板”,导入时读取。主循环拿到账号后,先根据模板字段加载对应配置,再执行对应模板。这样一套框架就能跑多种登录场景,而不是被一个客户端绑定死。
也可以往后扩展自动更新账号状态机制。登录成功的账号、登录失败的账号、任务执行失败的账号,分开记录到不同的文件里,方便后续只重跑失败账号。我通常会把失败的账号自动写入fail_账号.txt,全量跑完后检查这个文件,针对失败账号做二次重跑,能显著提升整体成功率。
这套框架我从头到尾用下来,最大的体会是:批量自动化最不值钱的环节是“操作”,最值钱的环节是“判断和容错”。每次在循环里多一个状态判断,或者多一层超时保护,都是在给脚本增加稳定性的厚度。写代码的过程没有太多玄学,就是把“如果发生意外怎么办”这个问题一遍遍问自己,然后一个个把答案填进去。
最后提醒一句:自动化脚本在给业务提效的同时,一定要先确认所用场景符合对应软件的服务条款。像这种大量账号跑操作的类型,本来就处在灰色地带,尽量只用于自己完全有权限的系统和测试环境。我个人的做法是跑正式业务操作之前,先确认不会触犯对方的使用规则,避免给自己惹麻烦。