Files
atomk-hermes-skills/skills/archived/miaoshou-collect-box/SKILL.md
T

22 KiB
Raw Blame History

name, category, description
name category description
miaoshou-collect-box cross-border-ecommerce Explore and interact with Miaoshou ERP's "通用采集箱" (Common Collect Box) — URLs, collection methods, import paths, and login automation.

妙手ERP 通用采集箱 (Common Collect Box)

Overview

Miaoshou ERP (erp.91miaoshou.com) has a Common Collect Box (通用采集箱) at a fixed URL. This skill documents how to navigate to it, the collection methods available, and how to interact with the import/collection UI programmatically via DrissionPage.

Login

  • URL: https://erp.91miaoshou.com/auth/login
  • Credentials: xiaochaoren2026 / [REDACTED]
  • CAPTCHA: Base64 inline image (data:image/png;base64,...) rendered in .captcha-img
    • Unlike Dianxiaomi (which uses a separate PNG endpoint), Miaoshou embeds the captcha directly.
    • Extract with JS: document.querySelector('.captcha-img').src
    • Decode base64, save to file, run ddddocr.classification()
    • Apply digit cleaning: raw.translate(str.maketrans('oliOSsZz', '01105522'))
  • Form selectors:
    • Account: css:.account-input
    • Password: css:.password-input
    • Captcha text: css:.captcha-text
    • Captcha UUID: input[name="captchaUuid"] (must be set/synced)
    • Submit: css:.login-button
  • Success redirects to: https://erp.91miaoshou.com/welcome
  • Cookie file: ~/.hermes/cookies/miaoshou_cookies.json (autoLoginToken is critical)

Common Collect Box URL

Direct link (works after login):

https://erp.91miaoshou.com/common_collect_box/index

Title: 妙手-产品采集

Collection Methods Discovered

The page exposes 6 collection methods as tab-like options:

Method Chinese Name Notes
Link Collection 链接采集 Enter public product URLs (1688, Taobao, etc.). System scrapes the page. Requires publicly accessible pages — internal systems (like AtomK behind login) will NOT work.
Import Collection 导入采集 Bulk import via file upload (template-based). This is the viable path for transferring data from AtomK or other internal systems.
1688 AI Selection 1688Ai选品 AI-assisted product selection from 1688
Inventory Collection 货盘采集 Collect from inventory/supplier pallets
Keyword Collection 关键词采集 Search keyword-based batch collection
Whole-Store Collection 整店采集 Enter a store URL to collect all products
Plugin Collection 插件采集 Browser extension-based collection
Store Cross-Collection 店铺互采 Cross-store collection

Import Collection (导入采集) — 4 Sub-Methods

When you click "导入采集", the page switches to ?fetchType=importCopy and shows 4 sub-tabs:

Sub-Method Chinese Name What It Does
Import Link Collection 导入链接采集 Upload an XLS file with product URLs. Miaoshou scrapes each URL.
Excel Spreadsheet Import Excel表格导入 Upload a pre-structured Excel with full product data (details TBD).
Local Material Package 本地素材包导入 Upload a local asset package (images + data).
Jushuitan Material Package 聚水潭素材包导入 Import from Jushuitan ERP format.

This is the most important one for AtomK-style workflows.

Template file: 导入产品链接模板.xls (downloadable via "下载导入模板" link in the modal) File format: .xls (older Excel 97-2003, NOT xlsx) — verified April 2026. .xlsx triggers 解析文件数据错误.

CRITICAL: The template is strictly 5 columns. Any extra column causes failure.

Column Field Required Notes
A 链接地址(必填) Yes Product detail page URL. Must be publicly accessible. Login-gated pages (like AtomK behind login) will fail.
B 产品标题 No AI titles ARE preserved — verified. Overrides scraped 1688 title.
C 价格(RMB No AI prices ARE preserved — verified. Overrides scraped 1688 price.
D 促销价(RMB No Only used when publishing to Lazada.
E 提示:促销价仅可用于采集到Lazada No Literal hint text — include this column to match the official template exactly.

Empirically verified findings (April 2026):

  • 5-column .xls with exact headers above → 解析成功 N/0/N
  • 6+ columns (e.g., adding "产品描述") → 导入文件未识别到:【产品主编号】表头
  • Adding "产品主编号" column → 解析文件数据错误
  • .xlsx format → 解析文件数据错误
  • AI title from XLS overrides scraped 1688 title in the final product
  • AI price from XLS overrides scraped 1688 price
  • Sheet name can be Worksheet or Sheet1 (doesn't matter with correct 5-col headers)
  • Encoding can be utf_16_le or utf-8 (doesn't matter with correct 5-col headers)

Important: The error "导入文件未识别到:【产品主编号】表头" is a misleading catch-all for "your columns don't match the expected 5-column template". Do NOT add a "产品主编号" column — doing so triggers 解析文件数据错误. Always use the exact 5-column format. The system does not actually use or store a "产品主编号" from Excel; SKU must be filled later via the edit dialog.

Critical limitation: The "导入链接采集" method fundamentally relies on web scraping each product URL. If the URL requires authentication (e.g., AtomK product pages behind atomlisting.com login), Miaoshou cannot fetch the data. The 5-column XLS only passes the link + AI title + AI price; Miaoshou still scrapes the 1688 page for images, description, specs, etc.

File Upload Mechanics

When the import modal is open:

  • The upload area is a hidden <input type="file" class="jx-upload__input"> inside a .pro-upload / .jx-upload--text container.
  • Selector: css:input[type=file] or css:.jx-upload__input
  • Accepts: .xls,.xlsx,.csv (but only .xls works reliably)
  • Use file_input.input('/path/to/file.xls') to set the file path programmatically.
  • After upload, the file name appears in .jx-upload-list li.
  • The confirm button is inside the modal dialog — search within .jx-dialog for buttons with text 确认.
  • After clicking confirm, the modal closes and the file appears in the "已导入的文件列表" table. Wait ~15s for parsing, then refresh the page to check status.
  • The page does NOT auto-update after confirm. You must navigate back to ?fetchType=importCopy or use page.get() to refresh and see the parse result.

Complete Upload Flow (Verified April 2026)

# 1. Navigate to import page
page.get("https://erp.91miaoshou.com/common_collect_box/index?fetchType=importCopy")
time.sleep(10)

# 2. Click Excel import button
page.run_js("""
    var all = document.querySelectorAll('span, div, button, a');
    for (var i=0; i<all.length; i++) {
        var txt = all[i].innerText || '';
        if (txt.includes('Excel表格导入') && txt.length < 20) {
            all[i].click(); break;
        }
    }
""")
time.sleep(5)

# 3. Upload file
file_inputs = page.eles('css:input[type="file"]')
file_inputs[0].input('/path/to/import.xls')
time.sleep(5)

# 4. Click confirm
page.run_js("""
    var btns = document.querySelectorAll('button');
    for (var i=0; i<btns.length; i++) {
        if (btns[i].innerText && btns[i].innerText.trim() === '确认') {
            btns[i].click(); break;
        }
    }
""")

# 5. Wait for backend parsing, then refresh to check result
time.sleep(20)
page.get("https://erp.91miaoshou.com/common_collect_box/index?fetchType=importCopy")
time.sleep(10)

# 6. Check result in page body text
body_text = page.run_js("return document.body.innerText;")
# Look for: '解析成功' or '解析失败' near the filename

Known Error Patterns

Error Message Meaning Fix
导入文件未识别到:【产品主编号】表头 Column count ≠ 5 or headers don't match Ensure exactly 5 columns with headers: 链接地址(必填), 产品标题, 价格(RMB, 促销价(RMB, 提示:促销价仅可用于采集到Lazada. Do NOT add "产品主编号" — it's a misleading error.
解析文件数据错误 Wrong format or added "产品主编号" Use .xls (not .xlsx). Remove any "产品主编号" column. Ensure exact 5-column headers.
解析失败 (with 0/N/N) URLs accepted but scraping failed Linked pages are unsupported (e.g., COS-hosted custom HTML, login-gated pages). Use standard 1688/Taobao/Pinduoduo URLs.

Collect Box Product Edit Dialog (采集箱产品编辑弹窗)

After a product is imported and claimed to a platform (e.g., Temu全托管), it appears in the 已认领 tab. Each row has an 编辑 button that opens a modal dialog for editing the product's full data.

This dialog is the key to filling AI-generated fields (SKU, description, keywords) that cannot be imported via the 5-column XLS template.

Dialog Structure

  • Root dialog: .jx-dialog.pro-dialog.collect-box-editor-dialog-V2 (1880×953)
  • Editor body: .collect-box-editor (1559×953)
  • Tabs: 基本信息, 产品属性, 销售属性, 产品图片, 产品视频, 认证说明, 物流信息, 货源链接

Critical Field Mappings (Verified April 2026)

The editor uses Vue.js with custom jx-input components. Direct .value assignment works but must be followed by event dispatching for Vue to sync state.

Field DOM Element Selector Strategy Notes
产品标题 input[type="text"] (multiple) Find first input whose .value contains the title text Title is pre-filled from import; usually does NOT need updating.
产品主编号 (SKU) input[type="text"] at index 1 editor.querySelectorAll('input')[1] Placeholder: 请输入产品主编号,不填将自动生成. This is the most reliable field to target.
简易描述 textarea at index 0 editor.querySelectorAll('textarea')[0] Plain text. Fills to Shopee/Wish/Joom/Mercado Libre/Ozon/Walmart product description.
详细描述 iframe at index 0 (TinyMCE-like) editor.querySelectorAll('iframe')[0]contentDocument.body Rich text editor. Used for Lazada/速卖通/TikTok/Coupang/eBay/Allegro. Body is contenteditable.

Vue State Sync (Mandatory)

Miaoshou uses Vue.js. Simply setting .value is not enough — the change must be propagated to Vue's reactive state. After setting any value, dispatch:

el.value = 'your-value';
el.dispatchEvent(new Event('input', {bubbles: true}));
el.dispatchEvent(new Event('change', {bubbles: true}));
el.dispatchEvent(new Event('blur', {bubbles: true}));
el.dispatchEvent(new KeyboardEvent('keydown', {key: 'a', bubbles: true}));
el.dispatchEvent(new KeyboardEvent('keyup', {key: 'a', bubbles: true}));

Without this, clicking 保存修改 will silently discard your changes.

Opening the Edit Dialog Programmatically

Products are rendered in a virtual scroll table (.pro-virtual-table__row), not a standard <table>.

# 1. Navigate to claimed tab
page.get('https://erp.91miaoshou.com/common_collect_box/items')
# ... click 已认领 tab ...

# 2. Find the row by collect_box_id and click 编辑
page.run_js("""
    var rows = document.querySelectorAll('.pro-virtual-table__row, .pro-virtual-scroll__row');
    for (var i=0; i<rows.length; i++) {
        var txt = rows[i].innerText || '';
        if (txt.includes('3516358992')) {  // your collect_box_id
            var btns = rows[i].querySelectorAll('button');
            for (var j=0; j<btns.length; j++) {
                if (btns[j].innerText && btns[j].innerText.trim() === '编辑') {
                    btns[j].click();
                    break;
                }
            }
        }
    }
""")
time.sleep(10)  # Dialog takes time to render
editor = page.ele('css:.collect-box-editor')

Filling Each Field

SKU (产品主编号)

editor.run_js(f"""
    var editor = document.querySelector('.collect-box-editor');
    var skuInput = editor.querySelectorAll('input')[1];
    skuInput.value = {json.dumps(sku)};
    skuInput.dispatchEvent(new Event('input', {{bubbles: true}}));
    skuInput.dispatchEvent(new Event('change', {{bubbles: true}}));
    skuInput.dispatchEvent(new Event('blur', {{bubbles: true}}));
    skuInput.dispatchEvent(new KeyboardEvent('keydown', {{key: 'a', bubbles: true}}));
    skuInput.dispatchEvent(new KeyboardEvent('keyup', {{key: 'a', bubbles: true}}));
""")

Simple Description (简易描述)

editor.run_js(f"""
    var editor = document.querySelector('.collect-box-editor');
    var ta = editor.querySelectorAll('textarea')[0];
    ta.value = {json.dumps(description)};
    ta.dispatchEvent(new Event('input', {{bubbles: true}}));
    ta.dispatchEvent(new Event('change', {{bubbles: true}}));
    ta.dispatchEvent(new Event('blur', {{bubbles: true}}));
""")

Rich Description (详细描述 — iframe)

html_content = f'<p><span style="font-size: 12.0pt;">{description.replace(chr(10), "</span></p><p><span style=\\"font-size: 12.0pt;\\">")}</span></p>'

editor.run_js(f"""
    var editor = document.querySelector('.collect-box-editor');
    var iframe = editor.querySelectorAll('iframe')[0];
    var doc = iframe.contentDocument || iframe.contentWindow.document;
    var body = doc.querySelector('body');
    body.innerHTML = {json.dumps(html_content)};
    body.dispatchEvent(new Event('input', {{bubbles: true}}));
""")

Saving

page.run_js("""
    var btns = document.querySelectorAll('button');
    for (var i=0; i<btns.length; i++) {
        if (btns[i].innerText && btns[i].innerText.includes('保存')) {
            btns[i].click();
            break;
        }
    }
""")
time.sleep(5)

Verification

After saving, re-open the edit dialog and read back the values to confirm persistence:

verify = editor.run_js("""
    var editor = document.querySelector('.collect-box-editor');
    var inputs = editor.querySelectorAll('input');
    var textareas = editor.querySelectorAll('textarea');
    var iframes = editor.querySelectorAll('iframe');
    var result = {};
    if (inputs.length > 1) result.sku = inputs[1].value;
    if (textareas.length > 0) result.desc = textareas[0].value;
    for (var i=0; i<iframes.length; i++) {
        try {
            var doc = iframes[i].contentDocument || iframes[i].contentWindow.document;
            var body = doc.querySelector('body');
            if (body) { result.rich_desc = body.innerText; break; }
        } catch(e) {}
    }
    return result;
""")
# verify.sku should match, verify.desc should contain AI text

AtomK → Miaoshou Batch Import Script

A production-ready end-to-end script exists at /home/ubuntu/auto_miaoshou_import.py:

What it does:

  1. Logs into AtomK API, fetches claimed products + their AI-generated listings
  2. Generates a 5-column .xls file (utf_16_le encoding, Worksheet sheet name)
  3. Launches headless Chromium, loads Miaoshou cookies
  4. Navigates to ?fetchType=importCopy, clicks Excel表格导入
  5. Uploads the .xls file, clicks 确认
  6. Refreshes the page after 20s to verify 解析成功
  7. Logs results and updates state file

State tracking: /home/ubuntu/.hermes/miaoshou_import_state.json

  • Tracks already-imported product_ids to avoid duplicates
  • Updated after each successful run

Cron job: miaoshou-atomk-auto-import (job_id: 2b6587d6895b)

  • Schedule: 0 * * * * (every hour on the hour)
  • Batch size: 6 products per run
  • Logs to: /home/ubuntu/.hermes/miaoshou_import.log

Usage:

# Manual run
python3 /home/ubuntu/auto_miaoshou_import.py

# Check cron status
hermes cron list
Scenario Viable? Recommended Path
AtomK product pages are public No Even public AtomK HTML pages (hosted on Tencent COS) fail with 解析失败 0/3. Miaoshou's scraper is hardcoded for domestic e-commerce platforms (1688, Taobao, Pinduoduo), not custom HTML pages.
AtomK product pages require login No Same as above — plus authentication barrier.
5-column XLS import (link + AI title + AI price) Yes 解析成功. Titles and prices from Excel override scraped 1688 data. Verified: AI title Romantic Heart... appears instead of 1688 original.
5-column XLS + extra columns (desc, SKU, images, EAN, etc.) No Triggers 导入文件未识别到:【产品主编号】表头. Adding "产品主编号" triggers 解析文件数据错误.
".xlsx" format No Triggers 解析文件数据错误. Must use .xls (xlwt).
Full AI data via dual-browser automation Yes Proven workflow (April 2026):
1. Import via 5-col XLS (link + AI title + AI price)
2. Claim product to target platform
3. Open collect box edit dialog
4. Auto-fill SKU, 简易描述, 详细描述 (rich text) via DrissionPage + JS
5. Save and verify
Reference script: /home/ubuntu/miaoshou_atomk_bridge.py
Direct platform upload (bypass Miaoshou) ⚠️ Partial Generate Temu/AliExpress CSV from AtomK API → upload to platform directly. Bypasses Miaoshou entirely but loses Miaoshou's inventory/order management.

Page Structure Notes

  • Left sidebar contains: 通用功能, 产品采集, 公用采集箱, AI工作台, 侵权检测, 货盘中心, etc.
  • Supported platforms displayed as tags: 1688, 义乌小商品城, 浙宝网, Vvic, 搜款网, 网商园, 货捕头, etc.
  • Bottom action bar: 采集并自动认领, 采集并自动发布
  • A modal/popup may appear on first visit with collection settings — dismiss with "我知道了".

Browser Setup (DrissionPage)

from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions()
co.set_browser_path('/home/ubuntu/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome')
co.set_argument('--headless=new')
co.set_argument('--no-sandbox')
co.set_argument('--disable-dev-shm-usage')
co.set_argument('--window-size=1440,900')
co.set_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36')
co.set_local_port(get_free_port())
page = ChromiumPage(addr_or_opts=co)

Pitfalls

  • Cookie expiration: The saved cookie (autoLoginToken) expires relatively quickly. Re-login with captcha is often needed.
  • Base64 captcha session: Must extract the captcha image from the same browser session (via JS document.querySelector('.captcha-img').src) — do NOT use requests to fetch it separately, or the captcha UUID will mismatch.
  • Tab clicking: The collection method tabs (链接采集, 导入采集, etc.) are <span> elements inside radio-button groups, not standard <button> or <a> tags. Standard .ele('xpath://*[contains(text(), "导入采集")]').click() may fail. Use JS click via page.run_js() or search for the actual <button> inside the tab component.
  • Import dialog: Clicking "导入链接采集" opens a modal dialog. The template download link "下载导入模板" triggers a browser download to the default Downloads folder. In headless mode, the file goes to /home/ubuntu/Downloads/导入产品链接模板.xls.
  • XLS format only: The import link template is .xls (Excel 97-2003). Using .xlsx will likely fail.
  • "暂无当前模块权限": Some accounts may see "You don't have permission for this module" if the collect box feature is not enabled for the sub-account. Contact the main account admin to enable.
  • SPA navigation: Clicking menu items may not trigger full navigation. Use page.get('https://erp.91miaoshou.com/common_collect_box/index?fetchType=importCopy') for direct access.
  • Link scraper limitation: The 导入链接采集 method requires publicly accessible product detail pages. Any page behind authentication (AtomK, internal ERPs) will result in empty/failed collection.

Next Exploration Steps (when needed)

  1. Click the "导入采集" tab and inspect the file upload input (Done)
  2. Download the template to understand required columns (Done — 5-column .xls: link, title, price, promo_price, hint)
  3. Analyze "Excel表格导入" template for full-data import (Done — the XLS template is corrupt/unreadable; full-field bulk import is not viable. The working path is 5-col import + edit-dialog automation.)
  4. Automate the file upload and submission (Done — upload via file_input.input() + confirm button in modal + page refresh to check result.)
  5. Automate the edit dialog post-import fill (Done — SKU, 简易描述, 详细描述 all verified saving correctly.)
  6. Build end-to-end automation (Done — script at /home/ubuntu/auto_miaoshou_import.py fetches AtomK API data, generates 5-col XLS, uploads via browser, and tracks state. Cron job miaoshou-atomk-auto-import runs hourly.)
  7. Set up cron job for hourly batch import (Done — job miaoshou-atomk-auto-import runs at 0 * * * *, imports 6 products per run, skips already-imported IDs via /home/ubuntu/.hermes/miaoshou_import_state.json.)
  8. 🔴 Future: Extend edit-dialog automation to fill 产品图片 (replace 1688 images with AI-generated ones from AtomK platform_fields.pictures), 产品属性 (material, shape, brand), and 关键词 fields.
  9. 🔴 Future: Build batch loop that processes multiple collect_box_ids end-to-end (import → claim → edit → save).
  10. 🔴 Future: Investigate if 快速上货 (Quick Listing) offers a more direct API or form for bulk AI data injection, bypassing the collect box entirely.