diff --git a/妙手建品/RECIPE.md b/妙手建品/RECIPE.md index de78f2a..d67dcca 100644 --- a/妙手建品/RECIPE.md +++ b/妙手建品/RECIPE.md @@ -130,3 +130,32 @@ await c.send('DOM.setFileInputFiles', { files: ['/path/ms_import.csv'], nodeId } ### 当前队列状态 已导入文件列表里有 3 条失败任务(`ms_import` / `ms_import2` / `ms_nobom`),可在页面上「删除文件」清掉。 + +## ✅✅ 导入链接采集终于通了:`ms2.xls` 解析成功 47/0/47(2026-09-21 08:55) + +### 能用的 .xls 表头(**照抄这 8 列**) +| 列 | 示例值 | 说明 | +|---|---|---| +| `产品主编号` | `548459586741` | 1688 商品 id,**必须**有 | +| `链接地址(必填)` | `https://detail.1688.com/offer/548459586741.html` | 采集源链接 | +| `产品标题` | 英文标题 | 可留空(留空以来源为准) | +| `货币类型` | `CNY` | **必须**;空值报「不允许此货币类型」 | +| `SKU售价` | `34.0` | **必须 ≥ 0.01**;空值报「必须大于等于0.01」 | +| `SKU库存` | `999` | **必须 ≥ 0** | +| `价格(RMB)` | `34.0` | 模板原有列 | +| `促销价(RMB)` | 空 | 模板原有列(表头提示:仅可用于采集到 Lazada) | + +### 关键结论(下一轮别再走弯路) +1. **必须是 `.xls`/`.xlsx` 二进制表格**——CSV 一律只报「未识别到【产品主编号】表头」,`txt` 走 JSON 也走不通; +2. **页面下载的模板是旧的**:它只有 4 列、**缺 `产品主编号`**,照它填一定失败 + (错误提示会写「请下载最新模版」,但最新模板实际要的就是上面这 8 列); +3. 生成脚本:`pip install --break-system-packages xlwt` → `xlwt.Workbook(encoding='utf-8')` → 写表头+47 行 → + 存 `.xls` → 用 `DOM.setFileInputFiles` 注入 `input.jx-upload__input` → 点「确认」; +4. 失败时**点「查看失败原因」**能看到逐行逐列的精确原因(这次就是靠它拿到 `货币类型/SKU售价/SKU库存` 三个列名)。 + +### 产物 +`/home/admin9webs/miaoshou/dl/ms2.xls`(47 行,可复用为模板);上传脚本 `ms_upload.mjs`; +状态查询 `ms_status.mjs`;失败原因 `ms_reason.mjs`。 + +### 下一步 +「查看导入产品」/ **公用采集箱** 里核对这 47 条采集结果,再验证子账号能否 `认领`/`发布`。 diff --git a/妙手建品/ms_items.mjs b/妙手建品/ms_items.mjs new file mode 100644 index 0000000..5b125f4 --- /dev/null +++ b/妙手建品/ms_items.mjs @@ -0,0 +1,20 @@ +import { listPages, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { CDP, SAN as S } from '/tmp/cdp3.mjs'; +const t = (await listPages()).find(x=>/91miaoshou/i.test(x.url)); +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('Page.enable'); await c.send('Runtime.enable'); await wait(1200); +console.log('点查看导入产品:', String(await c.evalJS(`(()=>{ + const rows=[...document.querySelectorAll('tr')].filter(e=>e.offsetParent!==null&&/^\\s*ms2\\s/.test((e.innerText||'').trim())); + const row=rows[0]; if(!row) return 'no-row'; + const a=[...row.querySelectorAll('*')].find(e=>(e.innerText||'').trim()==='查看导入产品'); + if(!a) return 'no-link'; + for(const ev of ['mousedown','mouseup','click']) a.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'clicked';})()`))); +await wait(7000); +const o = await c.evalJS(`(()=>{ + const rows=[...document.querySelectorAll('tr')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(Boolean).slice(0,5); + const tabs=[...document.querySelectorAll('[class*=tab]')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,10)).filter(Boolean); + return JSON.stringify({href:location.href, rows, tabs:[...new Set(tabs)].slice(0,10), + body:(document.body.innerText||'').replace(/\\s+/g,' ').trim().slice(0,200)});})()`); +console.log('导入产品:', S(String(o)).slice(0,1000)); +try{ await c.shot('/tmp/ms_items.jpg'); console.log('shot ok'); }catch(e){} +process.exit(0); diff --git a/妙手建品/ms_reason.mjs b/妙手建品/ms_reason.mjs new file mode 100644 index 0000000..14a8f14 --- /dev/null +++ b/妙手建品/ms_reason.mjs @@ -0,0 +1,20 @@ +import { listPages, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { CDP, SAN as S } from '/tmp/cdp3.mjs'; +const t = (await listPages()).find(x=>/91miaoshou/i.test(x.url)); +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('Page.enable'); await c.send('Runtime.enable'); await wait(1200); +console.log('点查看失败原因:', String(await c.evalJS(`(()=>{ + const rows=[...document.querySelectorAll('tr')].filter(e=>e.offsetParent!==null&&/^\\s*ms\\s/.test((e.innerText||'').trim())); + const row=rows[0]; if(!row) return 'no-row'; + const link=[...row.querySelectorAll('*')].find(e=>(e.innerText||'').trim()==='查看失败原因'); + if(!link) return 'no-link'; + for(const ev of ['mousedown','mouseup','click']) link.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); + return 'clicked';})()`))); +await wait(5000); +const o = await c.evalJS(`(()=>{ + const dlgs=[...document.querySelectorAll('.jx-dialog,.jx-drawer,[class*=dialog]')].filter(e=>e.offsetParent!==null) + .map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,500)).filter(Boolean); + return JSON.stringify({dlgs:dlgs.slice(0,2)});})()`); +console.log('失败原因:', S(String(o)).slice(0,1200)); +try{ await c.shot('/tmp/ms_reason.jpg'); console.log('shot ok'); }catch(e){} +process.exit(0); diff --git a/妙手建品/示例-ms2.xls b/妙手建品/示例-ms2.xls new file mode 100644 index 0000000..b6d6e33 Binary files /dev/null and b/妙手建品/示例-ms2.xls differ