每天打开抖音小店、拼多多商家后台、天猫商家中心,重复"填标题、传图、选类目、提交"这套动作几十遍,是很多运营的日常。用 RPA 把这套流程自动化不难,难的是让它在生产环境稳定跑三个月不触发风控、不重复提交、不跑到一半全挂。
这篇文章不做工具安利,直接给方案。全文围绕四个技术点展开:状态机、指数退避、令牌桶限流、环境隔离,所有代码均为可改造的伪代码,看完就能落地。
一、先纠正一个认知:上架流程是状态机,不是一连串点击
新手搭 RPA 流程的通病是把脚本写成线性的"点 A → 等 3 秒 → 点 B"。一旦中间某步失败,整个流程的状态就丢了,重启只能从头发起,已处理的商品还会被重复提交。
正确做法是把每个 SKU 抽象成一个状态机,每一步执行后都持久化状态:
sql
PENDING ──领取──▶ RUNNING ──成功──▶ SUCCESS
│
├──瞬时故障──▶ RETRYING ──▶ RUNNING
├──需人工────▶ NEED_HUMAN ──▶ RUNNING
└──确定性失败▶ FAILED_FINAL
sql
stateDiagram-v2
[*] --> PENDING
PENDING --> RUNNING: 领取任务
RUNNING --> SUCCESS: 上架成功
RUNNING --> RETRYING: 瞬时故障
RETRYING --> RUNNING: 指数退避后重试
RUNNING --> NEED_HUMAN: 验证码/弹窗
NEED_HUMAN --> RUNNING: 人工处理后恢复
RUNNING --> FAILED_FINAL: 确定性失败
状态持久化用 SQLite 甚至一个 JSON 文件就够:
json
{
"sku_id": "SPU_10086",
"platform": "douyin",
"state": "RUNNING",
"current_step": "upload_images",
"retry_count": 1,
"last_error": null,
"screenshot": "runs/20260929/SPU_10086_step3.png"
}
这样做的好处:断点续传 是免费的------进程挂了重启,加载状态文件,只处理 PENDING 的条目,已成功的天然跳过。这也是后面所有设计的地基。
二、风控:自动化要"像人",靠的是降低机器特征的信噪比
平台风控系统看的不是"你是不是 RPA",而是一组可计算的异常信号:操作轨迹是否平滑、点击间隔是否恒定、环境指纹是否自洽、行为节奏是否超出正常运营。
逐项给技术方案。
1. 随机延时,且不要写死区间
固定 3 秒点一次是最典型的机器特征。人类操作间隔近似对数正态分布,长尾拖得很长:
python
import random, time, math
def human_delay(base=3.0, sigma=0.4):
"""对数正态分布模拟人工操作间隔"""
return random.lognormvariate(math.log(base), sigma)
# 每执行完一步调用
time.sleep(human_delay())
再叠加一条业务规则:每处理 N 个商品(比如 8~15 个,随机取),插入一次 30 秒以上的"离屏停顿",模拟人去回消息。
2. 环境指纹隔离 + 防关联
多店铺、多账号场景,每个账号必须一套独立环境:独立浏览器 Profile、一致的时区与分辨率、与账号常用地区匹配的属地信息。防关联的核心就一句话:不要让两个账号在任何环境维度上产生交集。异地登录、二次验证这类高风险动作留给人工完成首次,RPA 只在已授权的环境内操作。
3. 关键动作"回读",既是校验也是拟人化
填完标题回读字数、传完图等预览加载再提交。这些回读动作让行为序列出现"观察---判断---操作"的循环,更接近真实运营的页面交互模式。
4. 验证码:检测、挂起、通知、恢复
遇到滑块验证码、短信验证,正确的处理链是:
scss
if captcha_detected(page):
job.pause() # 挂起当前任务
notify("运营群机器人", f"{shop} 需要人工验证")
job.wait_for_human(timeout=1800) # 最长等 30 分钟
job.resume()
绝不硬闯。在合规边界内做事,是这套系统能长期跑的前提。
这类拟人化能力在成熟的电商RPA里通常是内置参数而非要自己造轮子。我在生产环境用蓝印RPA跑抖音小店批量上架时,随机延时和拟人化轨迹开箱即用,把更多精力放在状态机设计上------工具解决"像不像人",你解决"业务对不对"。
三、重试:指数退避 + 失败分类,绝不盲目重发
1. 失败先分三类,处理方式完全不同
| 类型 | 典型场景 | 策略 |
|---|---|---|
| 瞬时故障 | 网络超时、元素未加载 | 指数退避重试,上限 3 次 |
| 状态不确定 | 点击提交后无响应 | 先查结果再决策,禁止直接重发 |
| 确定性失败 | 资质缺失、字段校验不过 | 立即置为 FAILED_FINAL,跑完人工处理 |
2. 瞬时故障:指数退避加抖动
python
def retry_with_backoff(fn, max_retry=3):
for attempt in range(max_retry):
try:
return fn()
except TransientError:
if attempt == max_retry - 1:
raise
wait = (2 ** attempt) + random.uniform(0, 1) # 1s, 2s, 4s + 抖动
time.sleep(wait)
加抖动是为了避免多个任务同时失败、同时重试造成的"重试风暴"------这在多窗口并行时尤其重要。
3. 状态不确定:用幂等查询兜底
点击"提交"后页面无响应,此时商品可能已上架、也可能没上去。直接再点一次提交是最危险的操作,轻则重复铺货,重则留下异常记录。正确流程:
ini
submit_result = click_submit()
if not submit_result.confirmed:
time.sleep(human_delay(base=8))
if exists_in_product_list(sku_id): # 回查列表页
mark_success(sku_id) # 已上架,置 SUCCESS
else:
back_to_form() # 回到表单,从当前步骤继续
核心思想:提交类动作永远先查结果,查不到再重试流程,而不是重试点击。
4. 确定性失败要留档
资质不符导致的失败重试一百次也不会成功。这类 SKU 置为 FAILED_FINAL 后,连同失败截图、失败时页面快照、校验报错一起落盘,跑完导出清单批量补资质。这里提一句:状态记录与失败重跑在很多编排工具里是原生能力,蓝印RPA的失败任务可以直接导出复核清单,比翻日志找报错高效得多,断点续传配合这个机制,几百个 SKU 的批量任务也能做到"跑完即收尾"。
四、限流:令牌桶算法 + 业务节奏规则
单纯的速度限制不够,要把"算法限流"和"业务节奏"叠在一起。
算法层:令牌桶
python
class TokenBucket:
def __init__(self, rate, burst):
self.rate, self.burst = rate, burst
self.tokens = burst
self.last = time.monotonic()
def acquire(self):
now = time.monotonic()
self.tokens = min(self.burst,
self.tokens + (now - self.last) * self.rate)
self.last = now
if self.tokens >= 1:
self.tokens -= 1
return True
return False
# 单账号:每分钟 4 个令牌,突发上限 6
limiter = TokenBucket(rate=4/60, burst=6)
while job := next_pending():
if not limiter.acquire():
time.sleep(1)
continue
process(job)
业务层:四条实测有效的规则
- 单账号连续操作 30~50 分钟后,强制休息 10 分钟以上
- 单日上新量级跟店铺权重走:新店从严、老店从宽,宁可保守
- 同一批商品铺货至多个平台时,定时调度错峰执行,避免同一网络下多平台同时高频
- 改价、改库存这类影响交易的动作走更慢的独立限流桶,比上传新商品降一档
五、多店铺场景:分布式调度而不是单机堆账号
想提升吞吐,正确姿势是多窗口并行 + 中心调度:多台设备各自维护独立环境,中心节点只负责任务分发和状态汇总。每个窗口跑自己的店铺、自己的限流桶,互不共享令牌------这样整体吞吐上去了,单个账号的行为节奏反而更从容,防关联也天然成立。
六、一份可直接改造的配置
yaml
job:
name: douyin_daily_listing
platform: douyin_shop
schedule: "0 9 * * *" # 定时任务:每天 9 点
input: ./data/skus_douyin.csv
account:
profile: shop_A_env # 独立浏览器指纹环境
daily_limit: 60
rate_limit:
bucket: { rate: 0.07, burst: 6 } # 约每分钟 4 次
work_window: { min: 30, max: 50 } # 分钟后强制休息 10 分钟
human_pause_every: [8, 15]
retry:
strategy: exponential_backoff
max_retry: 3
jitter: true
on_captcha: pause_and_notify # 挂起 + 通知人工,不自动处理
on_final_fail: export_report # 导出失败清单
state_store: ./runs/state.db # 断点续传
拼多多商家后台和天猫商家中心各复制一份配置,改平台参数、选择器和限流值即可,数据层共用同一份商品 CSV。
七、常见问题
Q:页面改版导致选择器失效,怎么快速定位? A:每个关键节点存截图快照,失败时对比快照和当前 DOM。维护一份多策略定位配置(主 CSS 选择器 + 备选 XPath + 文本匹配兜底),并给流程加"连续 3 个节点失败即终止告警"的熔断,宁可停下也别空转。
Q:怎么保证提交动作幂等? A:用平台侧可查询的结果做唯一判据(商品列表、草稿箱、操作日志),而不是用本地状态推断。任何"不确定"都走查询确认路径,见第三节第 3 条。
Q:刚入门 RPA,先做什么后做什么? A:先手动跑通一遍流程并记录每一步的判定条件,再搭状态机,最后才加随机化和限流。顺序反了的人,基本都死在选择器上。
电商RPA 上架这件事,工具只占三成,剩下七成是状态机设计、失败分类的克制、和对节奏的耐心。把"风控、重试、限流"三件套做成工程问题而不是玄学问题,自动化才能真正成为生产力。