import json import re import os # Load OCR results with open(r"D:\workspace\membank\售价\ocr_results.json", "r", encoding="utf-8") as f: all_results = json.load(f) def extract_sku_v4(text_lines): """ Robust extraction: Pattern in PDD backend: SKU_name ... [price1] ... [5.00] ... [price2] where price2 = price1 - 5 """ results = [] # Build full text with confidences items = [(t[0].strip(), float(t[1]) if t[1] else 0.0) for t in text_lines] i = 0 while i < len(items): text, conf = items[i] # Check for price patterns - find lines with two prices and 5.00 in between # Look ahead for price patterns prices_in_window = [] window_items = [] for j in range(i, min(i + 8, len(items))): t = items[j][0] nums = re.findall(r'(\d{2,4}\.\d{2})', t) if nums: prices_in_window.extend([float(n) for n in nums]) window_items.append((j, t, [float(n) for n in nums])) if not prices_in_window: i += 1 continue prices_set = set(round(p, 2) for p in prices_in_window) # Try to find original price and discounted price (diff = 5) found_prices = [] for p in sorted(prices_set): if round(p - 5, 2) in prices_set and p > 15: found_prices.append((round(p - 5, 2), p)) # (discounted, original) elif round(p + 5, 2) in prices_set and p > 15: found_prices.append((p, round(p + 5, 2))) if not found_prices: i += 1 continue # Now find the SKU name - look backwards from the price # The SKU name is typically 1-3 lines before the price sku_name = None for j in range(i-1, max(0, i-5), -1): t = items[j][0] # Must contain CPU or platform identifiers if re.search(r'(J1900|j1900|J3160|j3160|i3|I3|i5|I5|1037[Uu]|5005[Uu]|N5105|16G[Bb]|预装系统|双盘位|单盘位|三盘位|四盘位|黑裙|黑群晖|飞牛|蜗牛|Nas|NAS|盘位)', t): # Not a pure UI element if not re.match(r'^(商品|营销|官方|拼多多|商家|查看|修改|活动|价格|关注|折扣|券后|ID)', t): sku_name = t break if sku_name: for discounted, original in found_prices: results.append({ 'sku': sku_name, 'price': discounted, 'original_price': original, 'confidence': conf }) i += 1 return results def normalize_sku_key(name): """Create a normalized key for deduplication by extracting core identifiers""" name = name.strip() # Extract CPU type cpu = '' cpu_match = re.search(r'(J1900|j1900|J3160|j3160|i3|I3|i5|I5|1037[Uu]|5005[Uu]|N5105|N5095|J3355)', name) if cpu_match: cpu = cpu_match.group(1).upper().replace('J1900','J1900').replace('J3160','J3160') # Extract memory mem = '' mem_match = re.search(r'(\d+[Gg])[+]?(\d+[Gg])?', name) if mem_match: mem = mem_match.group(0).upper() # Extract form factor form = '' if '单盘' in name or '单盤' in name: form = '单盘' elif '双盘' in name or '雙盤' in name: form = '双盘' elif '三盘' in name: form = '三盘' elif '四盘' in name: form = '四盘' # System USB if '16G' in name.upper() or '16g' in name: if 'windows' in name.lower(): return 'USB:16GB:windows' elif '飞牛' in name or '蜗牛' in name: return 'USB:16GB:飞牛' return f'USB:16GB:{name[:20]}' return f"{cpu}:{mem}:{form}" def clean_sku_name(name): """Clean up OCR artifacts in SKU name""" name = name.strip() # Remove known noise name = re.sub(r'关注券5元优惠券', '', name) name = re.sub(r'店铺关注券5元优惠券', '', name) name = re.sub(r'[>\s]+$', '', name) name = re.sub(r'^[>\s]+', '', name) name = re.sub(r'\s+', ' ', name) return name # Collect all unique SKUs all_entries = [] for img_name, lines in all_results.items(): entries = extract_sku_v4(lines) all_entries.extend(entries) # Deduplicate - keep highest confidence per normalized key dedup = {} for e in all_entries: key = normalize_sku_key(e['sku']) if key and key not in dedup: dedup[key] = e elif key and e['confidence'] > dedup[key]['confidence']: dedup[key] = e # Sort by price sorted_entries = sorted(dedup.values(), key=lambda x: x['price']) # Print for debugging print("=" * 60) print("Extracted unique SKUs:") for i, e in enumerate(sorted_entries): key = normalize_sku_key(e['sku']) print(f" [{key}] {clean_sku_name(e['sku'])} -> ${e['price']} (orig: ${e['original_price']})") # Build markdown md = [] md.append("# SKU 售价汇总") md.append("") md.append("> 从拼多多商家后台「商品价格管理」截图 OCR 识别提取") md.append(f"> 提取日期: 2026-06-16") md.append(f"> 图片数量: {len(all_results)} 张") md.append(f"> 识别 SKU: {len(sorted_entries)} 个(已去重合并)") md.append("") md.append("---") md.append("") # Group by platform groups = { '系统U盘 (16GB)': [], '单盘位 - 1037U': [], '双盘位 - 1037U': [], '单盘位 - J1900': [], '双盘位 - J1900': [], '单盘位 - J3160': [], '双盘位 - J3160': [], '单盘位 - i3': [], '双盘位 - i3': [], '双盘位 - i5': [], '三盘位/四盘位 - i3/i5': [], '5005U 系列': [], '其他NAS/黑裙晖': [], } for e in sorted_entries: name = e['sku'] key = normalize_sku_key(name) if 'USB' in key: groups['系统U盘 (16GB)'].append(e) elif '1037U' in key: if '单盘' in key: groups['单盘位 - 1037U'].append(e) else: groups['双盘位 - 1037U'].append(e) elif 'J1900' in key: if '单盘' in key: groups['单盘位 - J1900'].append(e) else: groups['双盘位 - J1900'].append(e) elif 'J3160' in key: if '单盘' in key: groups['单盘位 - J3160'].append(e) else: groups['双盘位 - J3160'].append(e) elif 'I3' in key: if '三盘' in key or '四盘' in key: groups['三盘位/四盘位 - i3/i5'].append(e) elif '单盘' in key: groups['单盘位 - i3'].append(e) else: groups['双盘位 - i3'].append(e) elif 'I5' in key: if '三盘' in key or '四盘' in key: groups['三盘位/四盘位 - i3/i5'].append(e) else: groups['双盘位 - i5'].append(e) elif '5005U' in key: groups['5005U 系列'].append(e) else: groups['其他NAS/黑裙晖'].append(e) for group_name in groups: entries = groups[group_name] if not entries: continue md.append(f"## {group_name}") md.append("") md.append("| SKU 配置 | 券后售价 (¥) | 券前原价 (¥) | 优惠 |") md.append("|---------|------------:|------------:|-----|") for e in entries: name = clean_sku_name(e['sku']) md.append(f"| {name} | {e['price']:.2f} | {e['original_price']:.2f} | -5.00 |") md.append("") md.append("---") md.append("") md.append("## 说明") md.append("") md.append("- **优惠**: 统一使用店铺关注券 -5.00 元") md.append("- **数据来源**: 拼多多商家后台「商品价格管理」页面截图") md.append("- **识别工具**: RapidOCR (rapidocr-onnxruntime)") md.append("- **注意事项**: OCR 识别的 SKU 名称可能有碎片化/错字,建议对比后台实际数据确认") md.append("- **价格日期**: 2026-06-05 (截图时间)") md.append("") # Fix the price summary line overall_min = min(e['price'] for e in sorted_entries) if sorted_entries else 0 overall_max = max(e['price'] for e in sorted_entries) if sorted_entries else 0 md.append(f"> 价格范围: ¥{overall_min:.2f} ~ ¥{overall_max:.2f}") output_path = r"D:\workspace\membank\售价\SKU售价汇总.md" with open(output_path, "w", encoding="utf-8") as f: f.write("\n".join(md)) print(f"\n\nSaved to: {output_path}") print(f"Total unique SKUs: {len(sorted_entries)}")