抖店铺货自动化:7个核心功能的技术实现方案
做抖店批量运营的开发者,最终都会面临同一个问题:怎么用代码实现一套稳定、安全的铺货自动化系统?
这篇文章不讲废话,直接上技术方案。从多店铺隔离到AI优化,7个核心功能的技术实现思路和关键代码。
技术栈选择
语言:Python 3.10+
浏览器控制:Playwright(推荐,比Selenium稳定)
数据存储:SQLite(轻量,不需要额外部署)
AI图片处理:Pillow + OpenCV
为什么选Playwright而不是Selenium?
- Playwright的Context隔离更彻底,适合多店铺场景
- Playwright的API更现代,支持async/await
- Playwright的稳定性更好,不容易因为浏览器更新而挂
功能一:多店铺独立浏览器环境
核心思路:为每个店铺创建独立的BrowserContext。
python
from playwright.sync_api import sync_playwright
from pathlib import Path
import json
class StoreManager:
def __init__(self):
self.playwright = None
self.browser = None
self.stores = {} # {store_name: BrowserContext}
def init(self):
self.playwright = sync_playwright().start()
self.browser = self.playwright.chromium.launch(
headless=True,
args=['--disable-blink-features=AutomationControlled']
)
def add_store(self, store_name: str, cookie_file: str):
"""为每个店铺创建独立的浏览器上下文"""
context = self.browser.new_context(
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
viewport={'width': 1920, 'height': 1080},
locale='zh-CN'
)
# 注入反检测脚本
context.add_init_script("""
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
window.navigator.chrome = { runtime: {} };
""")
# 加载独立的登录Cookie
cookie_path = Path(cookie_file)
if cookie_path.exists():
with open(cookie_path, 'r', encoding='utf-8') as f:
cookies = json.load(f)
context.add_cookies(cookies)
self.stores[store_name] = context
def get_page(self, store_name: str):
return self.stores[store_name].new_page()
def close(self):
for ctx in self.stores.values():
ctx.close()
self.browser.close()
self.playwright.stop()
关键点:每个店铺的Context是完全隔离的。Cookie、LocalStorage、Session互不干扰,从根本上杜绝串号。
数据全部存在本地,不存密码,不上传到第三方服务器。
功能二:智能选品(多维度筛选)
python
import sqlite3
class ProductFilter:
def __init__(self, db_path: str):
self.conn = sqlite3.connect(db_path)
self.conn.row_factory = sqlite3.Row
def filter_products(self, **criteria) -> list:
"""
多维度筛选商品
criteria: min_rating, max_return_rate, min_shipping_rate,
min_price, max_price, category
"""
query = "SELECT * FROM products WHERE 1=1"
params = []
if 'min_rating' in criteria:
query += " AND rating >= ?"
params.append(criteria['min_rating'])
if 'max_return_rate' in criteria:
query += " AND return_rate <= ?"
params.append(criteria['max_return_rate'])
if 'min_shipping_rate' in criteria:
query += " AND shipping_rate >= ?"
params.append(criteria['min_shipping_rate'])
if 'min_price' in criteria:
query += " AND price >= ?"
params.append(criteria['min_price'])
if 'max_price' in criteria:
query += " AND price <= ?"
params.append(criteria['max_price'])
if 'category' in criteria:
query += " AND category = ?"
params.append(criteria['category'])
cursor = self.conn.execute(query, params)
return [dict(row) for row in cursor.fetchall()]
# 使用示例
pf = ProductFilter('./products.db')
results = pf.filter_products(
min_rating=95, # 好评率95%以上
max_return_rate=5, # 退货率5%以下
min_shipping_rate=98, # 发货率98%以上
min_price=30, # 最低价30元
max_price=80 # 最高价80元
)
功能三:全自动铺货
python
import asyncio
import random
class AutoPublisher:
def __init__(self, store_manager: StoreManager):
self.store_manager = store_manager
async def publish_batch(self, store_name: str, products: list):
page = self.store_manager.get_page(store_name)
for i, product in enumerate(products):
try:
# 打开商品发布页面
page.goto('https://haohuo.douyin.com/goods/publish')
page.wait_for_load_state('networkidle')
# 自动填写标题
title = self._generate_title(product)
page.fill('[name="title"]', title)
# 选择类目
page.click('[class*="category-select"]')
page.wait_for_selector(f'text={product["category"]}')
page.click(f'text={product["category"]}')
# 设置价格(自动加价)
price = self._calc_price(product)
page.fill('[name="price"]', str(price))
# 上传主图(AI优化后的图片)
page.set_input_files('input[type="file"]', product['optimized_image'])
# 提交发布
page.click('button:has-text("发布")')
page.wait_for_selector('.success-toast', timeout=10000)
print(f"[{store_name}] 商品 {i+1}/{len(products)} 发布成功")
except Exception as e:
print(f"[{store_name}] 商品 {i+1} 发布失败: {e}")
# 随机延时,避免触发风控
await asyncio.sleep(random.uniform(3, 8))
def _generate_title(self, product: dict) -> str:
"""自动生成标题:类目关键词 + 卖点 + 规格"""
keywords = product.get('keywords', [])
selling_points = product.get('selling_points', [])
return f"{' '.join(keywords[:3])} {' '.join(selling_points[:2])}"
def _calc_price(self, product: dict) -> float:
"""自动加价:成本 + 运费 + 利润"""
cost = product['cost']
freight = product.get('freight', 5)
markup = product.get('markup_rate', 0.3) # 默认30%利润
return round(cost * (1 + markup) + freight, 2)
功能四:AI商品优化(图片处理)
python
from PIL import Image, ImageDraw, ImageFont
import os
class ImageOptimizer:
"""AI辅助商品图片优化"""
# 抖店不同位置的尺寸要求
SIZES = {
'main': (800, 800), # 主图
'detail': (750, 1000), # 详情页
'banner': (750, 300), # 轮播图
}
def optimize(self, source_path: str, product_type: str) -> dict:
"""根据商品类型智能优化图片"""
img = Image.open(source_path)
result = {}
for position, size in self.SIZES.items():
optimized = self._smart_crop(img, size, product_type, position)
output_path = source_path.replace('.', f'_{position}.')
optimized.save(output_path, quality=95)
result[position] = output_path
return result
def _smart_crop(self, img, target_size, product_type, position):
"""智能裁剪:根据商品类型调整裁剪策略"""
w, h = img.size
tw, th = target_size
ratio = tw / th
img_ratio = w / h
if position == 'main':
# 主图:居中裁剪,保留商品主体
if img_ratio > ratio:
new_w = int(h * ratio)
left = (w - new_w) // 2
cropped = img.crop((left, 0, left + new_w, h))
else:
new_h = int(w / ratio)
top = (h - new_h) // 2
cropped = img.crop((0, top, w, top + new_h))
elif position == 'detail':
# 详情页:保持比例,白边填充
bg = Image.new('RGB', target_size, (255, 255, 255))
img.thumbnail((tw, th), Image.LANCZOS)
x = (tw - img.width) // 2
y = (th - img.height) // 2
bg.paste(img, (x, y))
cropped = bg
elif position == 'banner':
# 轮播图:取顶部区域
cropped = img.crop((0, 0, w, int(w / ratio)))
return cropped.resize(target_size, Image.LANCZOS)
功能五:批量商品清理
python
class ProductCleaner:
def __init__(self, store_manager: StoreManager):
self.store_manager = store_manager
def clean_no_exposure(self, store_name: str, days: int = 3):
"""清理无曝光商品"""
page = self.store_manager.get_page(store_name)
# 进入商品管理页面
page.goto('https://haohuo.douyin.com/goods/list')
page.wait_for_load_state('networkidle')
# 设置筛选条件:无曝光天数
page.click('[class*="filter-btn"]')
page.fill('[class*="no-exposure-days"]', str(days))
page.click('button:has-text("筛选")')
page.wait_for_selector('.product-list')
# 全选
page.click('[class*="select-all"]')
# 批量下架
page.click('button:has-text("批量下架")')
page.wait_for_selector('.confirm-dialog')
page.click('button:has-text("确认")')
print(f"[{store_name}] 已清理 {days} 天无曝光商品")
def clean_by_supplier(self, store_name: str, supplier: str):
"""按供应商批量清理"""
page = self.store_manager.get_page(store_name)
page.goto('https://haohuo.douyin.com/goods/list')
page.wait_for_load_state('networkidle')
# 搜索指定供应商的商品
page.fill('[class*="search-input"]', supplier)
page.press('[class*="search-input"]', 'Enter')
page.wait_for_selector('.product-list')
# 全选并删除
page.click('[class*="select-all"]')
page.click('button:has-text("批量删除")')
page.wait_for_selector('.confirm-dialog')
page.click('button:has-text("确认")')
print(f"[{store_name}] 已清理供应商 [{supplier}] 的所有商品")
功能六:对话式控制
python
import re
class CommandInterpreter:
"""聊天式命令解析"""
HANDLERS = {}
@classmethod
def register(cls, pattern: str):
def decorator(func):
cls.HANDLERS[pattern] = func
return func
return decorator
def execute(self, command: str, **kwargs):
for pattern, handler in self.HANDLERS.items():
if re.search(pattern, command):
return handler(command, **kwargs)
return "未识别的指令,支持的指令:铺货、下架、参加活动、停止"
# 注册命令
@CommandInterpreter.register(r'铺货|发布')
def cmd_publish(command, **kwargs):
store = kwargs.get('store_name', 'default')
return f"开始为 [{store}] 自动铺货..."
@CommandInterpreter.register(r'下架.*无曝光')
def cmd_unpublish_no_exposure(command, **kwargs):
days = 3
match = re.search(r'(\d+)天', command)
if match:
days = int(match.group(1))
return f"正在清理 {days} 天无曝光商品..."
@CommandInterpreter.register(r'参加活动|报名活动')
def cmd_activity(command, **kwargs):
return "正在查找可报名的活动..."
@CommandInterpreter.register(r'停止|暂停')
def cmd_stop(command, **kwargs):
return "已停止当前任务"
# 使用
interpreter = CommandInterpreter()
print(interpreter.execute("铺货")) # 开始自动铺货...
print(interpreter.execute("下架7天无曝光商品")) # 正在清理 7 天无曝光商品...
print(interpreter.execute("参加活动")) # 正在查找可报名的活动...
功能七:定时任务
python
import schedule
import time
from datetime import datetime
class Scheduler:
def __init__(self, publisher, cleaner, store_name):
self.publisher = publisher
self.cleaner = cleaner
self.store_name = store_name
def setup(self):
"""配置定时任务"""
schedule.every().day.at("08:00").do(
self._task_wrapper, "自动铺货", self.publisher.daily_publish, self.store_name
)
schedule.every().day.at("14:00").do(
self._task_wrapper, "清理无曝光商品", self.cleaner.clean_no_exposure, self.store_name, 3
)
schedule.every().day.at("22:00").do(
self._task_wrapper, "报名活动", self.publisher.join_activities, self.store_name
)
def _task_wrapper(self, task_name, func, *args):
print(f"[{datetime.now().strftime('%H:%M')}] 开始执行: {task_name}")
try:
func(*args)
print(f"[{datetime.now().strftime('%H:%M')}] 完成: {task_name}")
except Exception as e:
print(f"[{datetime.now().strftime('%H:%M')}] 失败: {task_name} - {e}")
def run(self):
"""启动定时调度"""
self.setup()
print("定时任务已启动,等待执行...")
while True:
schedule.run_pending()
time.sleep(60)
# 启动
if __name__ == '__main__':
sm = StoreManager()
sm.init()
sm.add_store('店铺A', './cookies/store_a.json')
scheduler = Scheduler(AutoPublisher(sm), ProductCleaner(sm), '店铺A')
scheduler.run()
总结
7个核心功能的技术实现要点:
| 功能 | 关键技术 | 难点 |
|---|---|---|
| 多店铺隔离 | Playwright独立Context | 反检测脚本 |
| 智能选品 | SQLite多维筛选 | 策略设计 |
| 全自动铺货 | Playwright页面操作 | 随机延时防风控 |
| AI商品优化 | Pillow智能裁剪 | 不同位置适配 |
| 批量清理 | DOM操作+批量选择 | 页面改版适配 |
| 对话式控制 | 正则命令解析 | 中文分词 |
| 定时任务 | schedule库 | 异常处理 |
完整代码可以基于上面的示例进行整合。核心思路是:本地运行、数据本地存储、每个店铺独立隔离。
如果你的团队没有开发能力,市面上也有成熟的桌面自动化方案,原理基本一致。