一个人写了一套店群自动化系统:从“人肉切号”到“全自动躺平”的完整复盘
一个人写了一套店群自动化系统从“人肉切号”到“全自动躺平”的完整复盘写在最前面别跟我说“你这就是个Chrome启动器”。我玻璃心看到这种评论就把死锁写进下一版大家一起挂。本文含大量真实翻车记录比如“为什么我200个店铺同时登录会被平台当成DDoS攻击”。 3. 店群这行要么你学会写代码要么你学会花钱请人写代码。我选了第三条路——自己写然后卖给别人。拼多多店群自动化上架方案2023年秋天我的店群生意差点被“切号”这件事拖垮。当时手里有80多个TEMU和拼多多店铺每天光登录、对账、上架就要花掉一个全职运营的8小时。运营小妹辞职那天跟我说林哥我宁愿去达美乐拍饼至少不用记这么多密码。我试过市面上几乎所有方案影刀RPA脚本跑着跑着就串号、指纹浏览器按月收费100个店一个月好几千、定制开发报价5万起。要么太贵要么太蠢。最后我决定自己写。三个月后Alien 1.0跑起来了。又过了半年它从一堆杂乱脚本变成了带UI、带调度、带环境隔离的商业软件。现在我一个人管着120个店铺每天实际工作时间不到20分钟——打开Alien看一眼报表处理一下异常然后关掉。这篇文章就是Alien的完整复盘。没有营销废话只有工程细节和血泪坑。一、店群的本质重复劳动 环境焦虑店群运营的核心工作就两件切环境和做动作。做动作很简单上架、领券、回复消息、同步订单。影刀RPA录一遍就能跑。真正要命的是切环境。每个店铺需要独立的代理IP、独立的浏览器缓存、独立的登录态。如果两个店铺共用一套环境轻则串号重则封店。人工切号的流程是这样的关闭当前浏览器清空缓存或者用隐私模式切换系统代理或浏览器代理插件重新打开浏览器输入账号密码登录做完操作后退出重复一个店铺3分钟100个店铺就是5小时。而且人不是机器会漏、会忘、会手滑。有一次运营忘记切代理用A店的IP登录了B店两个店同时收到异地登录警告第二天一起被封。所以我的核心思路只有一个把“切环境”这件事彻底自动化并且让“做动作”批量、并发地跑起来。TEMU店群如何管理运营二、Alien环境管理中心每个店铺一套独立“公寓”2.1 界面长什么样文字拆解Alien的主界面分三块左侧分组树类似文件管理器。根节点是平台拼多多/TEMU/TikTok下面可以建多级文件夹。比如“TEMU → 美国站 → 女装类目”。右键分组可以批量分配代理、批量执行流程。右侧店铺表格每一行是一个店铺环境。列包括店铺ID可编辑别名、代理IP带在线/离线状态灯、健康度绿/黄/红基于最近24小时失败率、最后登录时间、状态空闲/执行中/异常。每行末尾有三个按钮打开环境、运行任务、编辑。底部日志面板实时输出每个任务的执行日志支持按店铺ID筛选。错误日志标红警告标黄。顶部工具栏导入CSV、新建分组、批量打开选中、导出报表。导入CSV模板只有三列shop_id, platform, proxy。其他指纹参数分辨率、时区、语言系统自动生成。100个店铺的初始化从原来手工配置一整天变成一分钟。“批量打开选中”是运营最爱。选中一排店铺点击按钮系统为每个店铺启动一个独立的Chrome窗口窗口标题显示店铺ID位置自动平铺。运营可以像操作普通电脑一样进去处理异常订单不用记任何密码。2.2 技术实现user-data-dir 指纹定制环境隔离的技术基础是Chromium的--user-data-dir参数。每个店铺指定一个独立的目录所有Cookies、LocalStorage、缓存都写在这个目录里。只要目录不共用店铺就不会串数据。但平台还会采集屏幕分辨率、时区、语言、WebGL、Canvas指纹等特征。如果两个店铺这些特征完全一样即使换了代理和缓存仍然可能被关联。所以我给每个店铺生成一套固定的、但彼此不同的指纹配置。固定是为了避免频繁变化触发风控不同是为了让平台看不出关联。下面是我的环境管理核心类生产代码简化版importhashlibimportrandomimportjsonfrompathlibimportPathclassAlienEnv:ROOTPath(./AlienData)def__init__(self,shop_id:str,platform:str):self.shop_idshop_id self.platformplatform self.pathself.ROOT/platform/shop_id self.user_dirself.path/user_dataself.fp_fileself.path/fingerprint.jsondefcreate(self,proxy:str,group:str)-str:self.path.mkdir(parentsTrue,exist_okTrue)self.user_dir.mkdir(exist_okTrue)fpself._build_fingerprint(proxy,group)withopen(self.fp_file,w)asf:json.dump(fp,f,indent2)# 预建缓存目录避免首次启动报错(self.user_dir/Cache).mkdir(exist_okTrue)(self.user_dir/Local Storage).mkdir(exist_okTrue)returnstr(self.user_dir)def_build_fingerprint(self,proxy:str,group:str)-dict:# 以shop_id为种子保证同一店铺每次生成相同指纹seed_strf{self.platform}:{self.shop_id}:{group}seedint(hashlib.md5(seed_str.encode()).hexdigest()[:8],16)rngrandom.Random(seed)resolutions[(1920,1080),(1366,768),(1440,900),(1536,864)]tz_map{pdd:[Asia/Shanghai],temu:[America/New_York,America/Los_Angeles],tiktok:[America/New_York,Europe/London]}lang_map{pdd:[zh-CN],temu:[en-US,en-GB],tiktok:[en-US,en-GB]}return{proxy:proxy,group:group,screen_w:rng.choice(resolutions)[0],screen_h:rng.choice(resolutions)[1],timezone:rng.choice(tz_map.get(self.platform,[UTC])),language:rng.choice(lang_map.get(self.platform,[en-US])),platform_os:rng.choice([Win32,MacIntel]),webgl_vendor:rng.choice([Google Inc.,Intel Inc.]),cpu_cores:rng.choice([2,4,8])}defload_fingerprint(self):withopen(self.fp_file)asf:returnjson.load(f) 启动浏览器时读取指纹配置通过命令行参数注入 pythondeflaunch_browser(env:AlienEnv):fpenv.load_fingerprint()cmd[chrome.exe,f--user-data-dir{env.user_dir},f--proxy-server{fp[proxy]},f--window-size{fp[screen_w]},{fp[screen_h]},f--lang{fp[language]},--remote-debugging-port0,--disable-blink-featuresAutomationControlled]procsubprocess.Popen(cmd,...)# 捕获调试端口... 这个方案上线后我的店铺关联封店率从每月5-10个降到了0。已经连续10个月没有因为环境关联被封过。---## 三、调度编排让22个窗口像军队一样整齐环境建好了下一步是让任务跑起来。 Alien的调度器是我写过最复杂的模块之一。它要同时满足-最多22个浏览器窗口并发再高会卡--同一个店铺同一时间只能执行一个任务--不同店铺的任务可以任意并发--任务结束后浏览器不关复用给下一个任务--空闲超过30分钟的浏览器自动关闭并清理缓存 下面这段是调度器的主干代码真实工程提炼 pythonimportthreadingimportqueueimporttimeclassAlienScheduler:def__init__(self,max_concurrent22):self.max_concurrentmax_concurrent self.semthreading.Semaphore(max_concurrent)self.task_qqueue.Queue()self.shop_locks{}self.lockthreading.Lock()def_get_shop_lock(self,shop_id):withself.lock:ifshop_idnotinself.shop_locks:self.shop_locks[shop_id]threading.Lock()returnself.shop_locks[shop_id]defadd_task(self,shop_id,flow_file,paramsNone):self.task_q.put((shop_id,flow_file,params))def_worker(self):whileTrue:shop_id,flow_file,paramsself.task_q.get()shop_lockself._get_shop_lock(shop_id)tthreading.Thread(targetself._run,args(shop_id,flow_file,params,shop_lock))t.start()def_run(self,shop_id,flow_file,params,shop_lock):# 顺序先拿店铺锁串行化再拿信号量限流withshop_lock:self.sem.acquire()try:self._execute(shop_id,flow_file,params)finally:self.sem.release()self.task_q.task_done()def_execute(self,shop_id,flow_file,params):# 获取或启动浏览器实例debug_portself._ensure_browser(shop_id)# 调用影刀self._call_yingdao(flow_file,shop_id,debug_port,params)def_ensure_browser(self,shop_id):# 检查本地是否有存活实例没有则启动passdef_call_yingdao(self,flow_file,shop_id,port,params):importsubprocess,json cmd[影刀RPA.exe,-run,flow_file,-param,fshop_id{shop_id},-param,fdebug_port{port},-param,fparams{json.dumps(params)}]subprocess.run(cmd,timeout600,checkTrue)defstart(self,worker_count22):for_inrange(worker_count):threading.Thread(targetself._worker,daemonTrue).start() 这个调度器上线后我的22个窗口从早到晚满负荷运行CPU占用稳定在70%左右内存稳定在45%左右连续跑了一个月没重启过。**智能平铺**每次启动新浏览器时调度器会计算当前已打开的窗口数量然后通过Windows API把窗口排列成网格。运营不用在任务栏里翻来翻去。**资源回收线程**一个独立的后台线程每隔5分钟扫描所有浏览器实例如果某个实例空闲超过30分钟就关闭进程并删除其Cache和Code Cache目录保留Cookies和LocalStorage。这个机制让磁盘占用始终控制在20GB以内。**拖拽式流程编排**我基于QGraphicsView写了一个可视化流程编辑器。左侧是动作库登录、上架、领券、发货等右侧是画布。运营把动作拖到画布上用箭头连线就是一个自动化流程。保存后可以分配给单个店铺或整个分组。这个功能让不懂代码的运营也能自己配置业务流程。---## 四、底层封装从脚本到商业软件的最后一公里如果我把上面这些代码以Python脚本的形式发给客户他们会疯掉。 所以我做了三件事### 4.1 PyQt6 专业界面我花了一个月学PyQt6然后写了一套完整的GUI。-仪表盘实时曲线图显示并发数、任务吞吐量、内存占用--环境管理树形分组表格支持拖拽、批量操作--流程编排基于QGraphicsScene的画布节点可拖拽、连线可编辑--日志面板彩色输出支持按店铺ID筛选 深色主题圆角卡片微动效。客户第一次打开时说“这软件看着就不便宜。”### 4.2 PyInstaller 打包成单exe客户不需要装Python、Chrome、影刀客户端。 我用PyInstaller把所有东西打成一个Alien.exe包含Python解释器、所有依赖库、一个便携版Chromium约120MB。首次启动时解压到%APPDATA%\Alien。 客户只需要下载 → 解压 → 双击exe → 等待10秒 → 看到界面。### 4.3 一机一码安全验证为了防止破解我做了硬件绑定授权。 程序启动时读取硬盘序列号和MAC地址生成机器码。用户把机器码发给我我用RSA私钥签名生成license文件。每次启动验证签名和硬件是否匹配。 虽然不能100%防破解但已经挡住了99%的“复制粘贴即用”。---## 五、那些让我半夜爬起来的坑**坑1内存泄漏排查了整整一周。**第一次部署后跑了48小时内存从2GB涨到11GB然后系统卡死。我用tracemalloc定位发现是影刀调用时subprocess.Popen的stdout管道没有被及时读取缓冲区积压。加上stdoutsubprocess.DEVNULL后解决。**坑2同一店铺两个任务同时写同一个文件。**有一个任务会把订单数据导出到orders_{shop_id}.csv。两个任务并行执行时会互相覆盖导致数据不完整。解决方案每个任务生成带时间戳的临时文件orders_{shop_id}_{timestamp}.tmp执行完后再合并到主文件。**坑3代理IP在任务中途失效。**有些廉价代理不稳定跑着跑着就断了。影刀脚本会卡在“加载中”页面直到超时。我在_execute里加了页面加载超时检测30秒超时后重启浏览器并换一个代理然后重试任务。**坑4Windows更新半夜重启。**有一次Windows自动更新半夜重启了机器所有正在执行的任务中断部分店铺的登录态丢失。后来我把调度器改成了“断点续传”模式每个任务开始前在数据库里记录状态重启后自动恢复未完成的任务。---## 写在最后从一个人写Alien到现在已经过去一年半。 它从几十行脚本变成了上万行代码的完整系统。 有人问我你为什么不直接用现成的指纹浏览器加影刀 我的回答是**自己造轮子不是为了证明自己厉害而是为了在每一个细节上拥有控制权。**代理池要换改代码。并发数要调整改配置。平台更新了风控策略加一层指纹伪装。 所有事情都在自己手里不用等第三方更新不用看供应商脸色。 如果你也在做店群自动化希望这篇文章能帮你省下几百个小时的踩坑时间。 技术不复杂复杂的是对细节的死磕。作者林焱独立开发者店群自动化架构师博客林焱RPA全网同名转载需授权不接受免费咨询 全文约4400字