多平台开店的商家准备上线进销存或网店ERP时,第一步往往不是装软件,而是整理商品数据。淘宝、拼多多、抖店各导出一份商品表,同一件商品在三个平台上的标题、规格写法、商家编码各不相同,有的甚至没填编码。这些数据不清洗就导入系统,结果就是同一商品被拆成好几个,库存永远对不上。
本文用 Python + pandas 实现一个可直接运行的清洗流程(Python 3.8+,需安装 pandas):文本标准化、规格解析、编码规范化、缺失编码推断和映射校验,最后输出一张可以导入ERP的 SKU 映射表。
先看一个典型的例子,同一款白色 M 码 T 恤,在三个平台上是这样的:
平台 | 商品标题 | 规格 | 商家编码 |
|---|---|---|---|
淘宝 | 纯棉短袖T恤 男女同款 | 白色/M | FZ-001-WH-M |
拼多多 | 纯棉短袖T恤(男女同款) | 白色 M码 | (空) |
抖店 | 纯棉 短袖T恤 | 白-M | FZ001WHM |
归纳起来,常见问题有五类:
人工处理几十个商品还行,几百上千个 SKU 就必须用程序批量处理。
各平台导出表 ──> 合并
──> 文本标准化(全角转半角、大写、去空格)
──> 规格解析(颜色、尺码)
──> 编码规范化(统一成 品类-款号-颜色-尺码)
──> 缺失编码推断(按同款已知编码 + 规格补全)
──> 映射校验(未映射 / 推断待确认 / 规格冲突)
──> 输出 SKU 映射表编码规则采用“品类(2位字母)-款号(3位数字)-颜色(2位字母)-尺码”,例如 FZ-001-WH-M 表示服装、001 款、白色、M 码。规则本身可以按企业实际调整,关键是全公司只用一套。
代码使用内置的示例数据,复制即可运行;实际使用时,把第 1 步换成 pd.read_excel() 读取各平台导出的文件。
import re
import unicodedata
import pandas as pd
# 1. 模拟三个平台导出的商品数据(实际使用时换成 pd.read_excel 读取各平台导出文件)
taobao = pd.DataFrame({
"平台": "淘宝",
"商品标题": ["纯棉短袖T恤 男女同款", "纯棉短袖T恤 男女同款", "保温杯500ml"],
"规格": ["白色/M", "白色/L", "黑色"],
"商家编码": ["FZ-001-WH-M", "FZ-001-WH-L", "BW-500-BK"],
})
pdd = pd.DataFrame({
"平台": "拼多多",
"商品标题": ["纯棉短袖T恤(男女同款)", "保温杯 500ML"],
"规格": ["白色 M码", "黑色"],
"商家编码": ["", "bw500bk"],
})
douyin = pd.DataFrame({
"平台": "抖店",
"商品标题": ["纯棉 短袖T恤", "纯棉 短袖T恤"],
"规格": ["白-M", "白-XL"],
"商家编码": ["FZ001WHM", " FZ-001-WH-XL "],
})
raw = pd.concat([taobao, pdd, douyin], ignore_index=True)
# 2. 文本标准化:全角转半角、统一大小写、去多余空格
def norm_text(s):
if pd.isna(s): # 读 Excel 时空单元格是 NaN,要先处理
return ""
s = unicodedata.normalize("NFKC", str(s))
return re.sub(r"\s+", " ", s).strip().upper()
# 3. 规格解析:把各平台五花八门的写法统一成 颜色 + 尺码
COLOR_MAP = {"白色": "WH", "白": "WH", "黑色": "BK", "黑": "BK"}
SIZES = ["XXL", "XL", "L", "M", "S"]
def parse_spec(spec):
spec = norm_text(spec)
color = next((code for word, code in COLOR_MAP.items() if word in spec), "")
size = next((sz for sz in SIZES if re.search(rf"(?<![A-Z]){sz}(?![A-Z])", spec)), "")
return color, size
# 4. 商家编码标准化:去空格、去分隔符后重新按段拼接
def norm_code(code):
c = re.sub(r"[^A-Z0-9]", "", norm_text(code))
# 正则对应本文的编码规则,换成自己的规则时同步修改
m = re.fullmatch(r"([A-Z]{2})(\d{3})([A-Z]{2})([A-Z]{0,3})", c)
if not m:
return ""
return "-".join(p for p in m.groups() if p)
df = raw.copy()
df[["颜色", "尺码"]] = df["规格"].apply(lambda s: pd.Series(parse_spec(s)))
df["标准编码"] = df["商家编码"].apply(norm_code)
# 5. 编码缺失的行:按 款号 + 颜色 + 尺码 从同款已知编码中推断
known = df[df["标准编码"] != ""].copy()
known["款号"] = known["标准编码"].str.extract(r"^([A-Z]{2}-\d{3})")
style_by_title = {}
for _, r in known.iterrows():
key = re.sub(r"[^一-龥A-Z0-9]", "", norm_text(r["商品标题"]))
style_by_title.setdefault(key, r["款号"])
def infer_code(r):
if r["标准编码"]:
return r["标准编码"], "原编码"
key = re.sub(r"[^一-龥A-Z0-9]", "", norm_text(r["商品标题"]))
style = next((v for k, v in style_by_title.items() if k in key or key in k), None)
if style and r["颜色"]:
return "-".join(p for p in [style, r["颜色"], r["尺码"]] if p), "推断"
return "", "无法推断"
df[["系统编码", "来源"]] = df.apply(lambda r: pd.Series(infer_code(r)), axis=1)
# 6. 映射校验
unmapped = df[df["系统编码"] == ""]
inferred = df[df["来源"] == "推断"]
sku_list = df[df["系统编码"] != ""]["系统编码"].drop_duplicates().sort_values()
# 冲突校验:同一个系统编码,解析出的颜色/尺码必须一致
spec_cnt = df[df["系统编码"] != ""].groupby("系统编码")[["颜色", "尺码"]].nunique()
conflict = spec_cnt[(spec_cnt["颜色"] > 1) | (spec_cnt["尺码"] > 1)]
print("清洗后的映射表:")
print(df[["平台", "规格", "商家编码", "系统编码", "来源"]].to_string(index=False))
print(f"\n去重后的系统SKU:{len(sku_list)} 个 -> {', '.join(sku_list)}")
print(f"需人工确认的推断编码:{len(inferred)} 条;无法映射:{len(unmapped)} 条;规格冲突:{len(conflict)} 个")
# 7. 导出结果,供导入ERP前人工复核(需安装 openpyxl)
# df.to_excel("SKU映射表.xlsx", index=False)运行结果:
清洗后的映射表:
平台 规格 商家编码 系统编码 来源
淘宝 白色/M FZ-001-WH-M FZ-001-WH-M 原编码
淘宝 白色/L FZ-001-WH-L FZ-001-WH-L 原编码
淘宝 黑色 BW-500-BK BW-500-BK 原编码
拼多多 白色 M码 FZ-001-WH-M 推断
拼多多 黑色 bw500bk BW-500-BK 原编码
抖店 白-M FZ001WHM FZ-001-WH-M 原编码
抖店 白-XL FZ-001-WH-XL FZ-001-WH-XL 原编码
去重后的系统SKU:4 个 -> BW-500-BK, FZ-001-WH-L, FZ-001-WH-M, FZ-001-WH-XL
需人工确认的推断编码:1 条;无法映射:0 条;规格冲突:0 个7 行平台商品最终归并为 4 个系统 SKU:三个平台的白色 M 码 T 恤都对应到 FZ-001-WH-M,小写的 bw500bk 被规范成 BW-500-BK,带空格的编码也被正确识别。
1. 为什么先判断空值,再用 unicodedata.normalize("NFKC")? 用 read_excel 读取时,空单元格会变成 NaN,直接转字符串会得到 "nan",所以要先判断空值。 NFKC 规范化会把全角字母、数字、括号转换成半角,“T恤(男女同款)”会变成“T恤(男女同款)”。这一步放在最前面,后面的匹配规则才不用考虑全角半角两套写法。
2. 尺码匹配为什么要按 XXL、XL、L 的顺序? 如果先匹配 L,“XL”里的 L 也会被命中。按从长到短的顺序,并用前后不是字母的边界条件 (?<![A-Z]) 和 (?![A-Z]) 限定,可以避免误判。
3. 编码规范化的思路 先去掉所有非字母数字字符,再用正则按“2位字母 + 3位数字 + 2位字母 + 0到3位字母”切段,重新用短横线拼接。这样 FZ001WHM、fz-001-wh-m、FZ-001-WH-M 都会得到同一个结果。不符合规则的编码返回空值,留给后续推断或人工处理。
4. 缺失编码的推断 先从已有编码的商品里提取“款号”,以标准化后的标题为键建立对照;遇到编码缺失的行,用标题做包含匹配找到款号,再拼上解析出的颜色和尺码。推断出来的编码标记为“推断”,必须人工确认后再导入,不能直接当成准确结果。
5. 三项校验
三项都清零后,映射表才可以导入。
清洗后的 SKU 映射表有两个用途:
以网上管家婆网店ERP为例,它在进销存的基础上增加了多平台订单处理和库存同步,据其系统内对接平台页面显示已对接200多个平台。商品资料导入的具体格式和字段,以所用版本的导入模板为准。
作者:俊达软件(广州俊达软件技术有限公司),网上管家婆授权全国经销商,从事进销存与网店ERP的上线实施。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。