一个人写了一套店群自动化系统:从“人肉切号”到“全自动躺平”的完整复盘
写在最前面:
- 别跟我说“你这就是个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:ROOT=Path("./AlienData")def__init__(self,shop_id:str,platform:str):self.shop_id=shop_id self.platform=platform self.path=self.ROOT/platform/shop_id self.user_dir=self.path/"user_data"self.fp_file=self.path/"fingerprint.json"defcreate(self,proxy:str,group:str="")->str:self.path.mkdir(parents=True,exist_ok=True)self.user_dir.mkdir(exist_ok=True)fp=self._build_fingerprint(proxy,group)withopen(self.fp_file,"w")asf:json.dump(fp,f,indent=2)# 预建缓存目录,避免首次启动报错(self.user_dir/"Cache").mkdir(exist_ok=True)(self.user_dir/"Local Storage").mkdir(exist_ok=True)returnstr(self.user_dir)def_build_fingerprint(self,proxy:str,group:str)->dict:# 以shop_id为种子,保证同一店铺每次生成相同指纹seed_str=f"{self.platform}:{self.shop_id}:{group}"seed=int(hashlib.md5(seed_str.encode()).hexdigest()[:8],16)rng=random.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):fp=env.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-port=0","--disable-blink-features=AutomationControlled"]proc=subprocess.Popen(cmd,...)# 捕获调试端口...``` 这个方案上线后,我的店铺关联封店率从每月5-10个降到了0。已经连续10个月没有因为环境关联被封过。---## 三、调度编排:让22个窗口像军队一样整齐环境建好了,下一步是让任务跑起来。 Alien的调度器是我写过最复杂的模块之一。它要同时满足:-最多22个浏览器窗口并发(再高会卡)--同一个店铺同一时间只能执行一个任务--不同店铺的任务可以任意并发--任务结束后浏览器不关,复用给下一个任务--空闲超过30分钟的浏览器自动关闭并清理缓存 下面这段是调度器的主干代码(真实工程提炼): ```pythonimportthreadingimportqueueimporttimeclassAlienScheduler:def__init__(self,max_concurrent=22):self.max_concurrent=max_concurrent self.sem=threading.Semaphore(max_concurrent)self.task_q=queue.Queue()self.shop_locks={}self.lock=threading.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,params=None):self.task_q.put((shop_id,flow_file,params))def_worker(self):whileTrue:shop_id,flow_file,params=self.task_q.get()shop_lock=self._get_shop_lock(shop_id)t=threading.Thread(target=self._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_port=self._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",f"shop_id={shop_id}","-param",f"debug_port={port}","-param",f"params={json.dumps(params)}"]subprocess.run(cmd,timeout=600,check=True)defstart(self,worker_count=22):for_inrange(worker_count):threading.Thread(target=self._worker,daemon=True).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`管道没有被及时读取,缓冲区积压。加上`stdout=subprocess.DEVNULL`后解决。**坑2:同一店铺两个任务同时写同一个文件。**有一个任务会把订单数据导出到`orders_{shop_id}.csv`。两个任务并行执行时,会互相覆盖,导致数据不完整。解决方案:每个任务生成带时间戳的临时文件`orders_{shop_id}_{timestamp}.tmp`,执行完后再合并到主文件。**坑3:代理IP在任务中途失效。**有些廉价代理不稳定,跑着跑着就断了。影刀脚本会卡在“加载中”页面,直到超时。我在`_execute`里加了页面加载超时检测(30秒),超时后重启浏览器并换一个代理,然后重试任务。**坑4:Windows更新半夜重启。**有一次Windows自动更新半夜重启了机器,所有正在执行的任务中断,部分店铺的登录态丢失。后来我把调度器改成了“断点续传”模式:每个任务开始前在数据库里记录状态,重启后自动恢复未完成的任务。---## 写在最后从一个人写Alien到现在,已经过去一年半。 它从几十行脚本,变成了上万行代码的完整系统。 有人问我:你为什么不直接用现成的指纹浏览器加影刀? 我的回答是:**自己造轮子不是为了证明自己厉害,而是为了在每一个细节上拥有控制权。**代理池要换?改代码。并发数要调整?改配置。平台更新了风控策略?加一层指纹伪装。 所有事情都在自己手里,不用等第三方更新,不用看供应商脸色。 如果你也在做店群自动化,希望这篇文章能帮你省下几百个小时的踩坑时间。 技术不复杂,复杂的是对细节的死磕。>作者:林焱>>独立开发者,店群自动化架构师>>博客:林焱RPA(全网同名)>>转载需授权,不接受免费咨询 (全文约4400字)