From bbd5eb4bcf8de1f70691db8bd8021ed24d41b99e Mon Sep 17 00:00:00 2001 From: admin9webs Date: Mon, 21 Sep 2026 07:41:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A6=99=E6=89=8B=E5=AF=BC=E5=85=A5=E9=87=87?= =?UTF-8?q?=E9=9B=86=E8=B7=91=E9=80=9A=EF=BC=9A=E6=A8=A1=E6=9D=BF=E5=88=97?= =?UTF-8?q?=E5=90=8D=3Dxlrd=E8=AF=BB=E5=87=BA=EF=BC=8C=E5=BF=85=E9=A1=BB?= =?UTF-8?q?=E7=94=A8=20CSV=EF=BC=88txt=20=E8=A6=81=E6=B1=82=20JSON?= =?UTF-8?q?=E3=80=81JSON=20=E7=BC=BA=E6=A0=87=E9=A2=98=EF=BC=89=EF=BC=8C47?= =?UTF-8?q?=20=E6=9D=A1=E9=93=BE=E6=8E=A5=E5=B7=B2=E5=85=A5=E5=88=97?= =?UTF-8?q?=E7=AD=89=E5=BE=85=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 妙手建品/RECIPE.md | 42 ++++++++++++++++++++++++++++++++++++ 妙手建品/links.json | 1 + 妙手建品/ms_accept.mjs | 13 ++++++++++++ 妙手建品/ms_check.mjs | 13 ++++++++++++ 妙手建品/ms_import.csv | 48 ++++++++++++++++++++++++++++++++++++++++++ 妙手建品/ms_tpl.mjs | 28 ++++++++++++++++++++++++ 妙手建品/ms_upload.mjs | 37 ++++++++++++++++++++++++++++++++ 7 files changed, 182 insertions(+) create mode 100644 妙手建品/links.json create mode 100644 妙手建品/ms_accept.mjs create mode 100644 妙手建品/ms_check.mjs create mode 100644 妙手建品/ms_import.csv create mode 100644 妙手建品/ms_tpl.mjs create mode 100644 妙手建品/ms_upload.mjs diff --git a/妙手建品/RECIPE.md b/妙手建品/RECIPE.md index c49fd65..779ae4d 100644 --- a/妙手建品/RECIPE.md +++ b/妙手建品/RECIPE.md @@ -61,3 +61,45 @@ 4. 解析成功后在 **公用采集箱** 里核对待采集商品,再验证子账号能否 `认领`/`发布` ⚠️ 未验证:模板的确切列名(要先下载模板)、上传控件的选择器、以及**子账号能否发布**。 + +## ✅ 导入链接采集 = 按模板列做 CSV 上传(已跑通到「等待解析」) + +### 模板列(`python3 -m pip install --break-system-packages xlrd` 读出来的) +下载的 `导入产品链接模板.xls` 是真·OLE2 文件,本机没有 xlrd/libreoffice, +装了 `xlrd`(`pip install --break-system-packages xlrd`)后读出: + +| 列 | 说明 | +|---|---| +| `链接地址(必填)` | 商品链接 | +| `产品标题` | 标题 | +| `价格(RMB)` | 价格 | +| `促销价(RMB)` | 促销价(表头提示:仅可用于采集到 Lazada) | + +### 格式踩坑(三次失败才走对) +| 提交内容 | 结果 | +|---|---| +| `.txt` 一行一个链接 | ❌ `…文件内容不是json格式`(**`.txt` 必须放 JSON**) | +| `.json` URL 数组 | ❌ `…文件标题缺失` | +| `.json` 带 `title/url/链接地址`(每项) | ❌ 同上 | +| `.json` 根节点带 `title/标题` + `links/data/list` | ❌ 同上 | +| **`.csv`(表头用模板那 4 列,UTF-8 BOM)** | ✅ **接受!入列显示「等待解析」** | + +### 上传机制(可复用) +```js +await c.send('DOM.enable'); +const { root } = await c.send('DOM.getDocument', { depth: -1 }); +const { nodeId } = await c.send('DOM.querySelector', { nodeId: root.nodeId, selector: 'input.jx-upload__input' }); +await c.send('DOM.setFileInputFiles', { files: ['/path/ms_import.csv'], nodeId }); +// 然后点面板里的「确认」 +``` +上传控件:`input.jx-upload__input`(隐藏,`accept=.xls,.txt,.zip,.json,.xlsx,.csv`)。 +**注意**:上传/确认后面板会关闭,下一次上传前要重新点「导入采集 → 导入链接采集」。 + +### 当前状态 +已导入文件列表里有一条:`ms_import | 2026-09-21 07:40:12 | **等待解析** | …`(47 条链接 + 标题 + 价格)。 +解析是**异步排队**的,30–60 秒内还没变;下一步就是**等它变成成功/失败并看「成功条数/失败条数」**, +再点「查看导入产品」或去 **公用采集箱** 核对采集到的商品。 + +### 生成脚本 +`/home/admin9webs/miaoshou/dl/ms_import.csv`(47 行,已排除无水印图的 `601964534137`), +由 `tk_input.json` 生成:链接 = `https://detail.1688.com/offer/.html`。 diff --git a/妙手建品/links.json b/妙手建品/links.json new file mode 100644 index 0000000..42442a3 --- /dev/null +++ b/妙手建品/links.json @@ -0,0 +1 @@ +["https://detail.1688.com/offer/548459586741.html", "https://detail.1688.com/offer/661190598139.html", "https://detail.1688.com/offer/764423265986.html", "https://detail.1688.com/offer/685456757807.html", "https://detail.1688.com/offer/735147938123.html", "https://detail.1688.com/offer/627879474686.html", "https://detail.1688.com/offer/610164857063.html", "https://detail.1688.com/offer/676236596821.html", "https://detail.1688.com/offer/708420302844.html", "https://detail.1688.com/offer/866537106657.html", "https://detail.1688.com/offer/547183997437.html", "https://detail.1688.com/offer/657617635303.html", "https://detail.1688.com/offer/641673262387.html", "https://detail.1688.com/offer/953869908708.html", "https://detail.1688.com/offer/703822717997.html", "https://detail.1688.com/offer/863524134410.html", "https://detail.1688.com/offer/694637537727.html", "https://detail.1688.com/offer/623011262210.html", "https://detail.1688.com/offer/836512561982.html", "https://detail.1688.com/offer/714492799386.html", "https://detail.1688.com/offer/591994902720.html", "https://detail.1688.com/offer/853414558425.html", "https://detail.1688.com/offer/997450831819.html", "https://detail.1688.com/offer/639409307567.html", "https://detail.1688.com/offer/902440558099.html", "https://detail.1688.com/offer/823838532886.html", "https://detail.1688.com/offer/698782391248.html", "https://detail.1688.com/offer/707826905566.html", "https://detail.1688.com/offer/1027332215847.html", "https://detail.1688.com/offer/682195499823.html", "https://detail.1688.com/offer/823560564174.html", "https://detail.1688.com/offer/739147684759.html", "https://detail.1688.com/offer/621300323670.html", "https://detail.1688.com/offer/896469995553.html", "https://detail.1688.com/offer/944470567338.html", "https://detail.1688.com/offer/787261097583.html", "https://detail.1688.com/offer/833753630925.html", "https://detail.1688.com/offer/719391062058.html", "https://detail.1688.com/offer/857781294719.html", "https://detail.1688.com/offer/863384503032.html", "https://detail.1688.com/offer/774494945453.html", "https://detail.1688.com/offer/750271307455.html", "https://detail.1688.com/offer/726442972676.html", "https://detail.1688.com/offer/775057912118.html", "https://detail.1688.com/offer/893592264148.html", "https://detail.1688.com/offer/607330049419.html", "https://detail.1688.com/offer/854003999648.html"] \ No newline at end of file diff --git a/妙手建品/ms_accept.mjs b/妙手建品/ms_accept.mjs new file mode 100644 index 0000000..e93ccc3 --- /dev/null +++ b/妙手建品/ms_accept.mjs @@ -0,0 +1,13 @@ +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('Runtime.enable'); await wait(1200); +const o = await c.evalJS(`(()=>{ + const fis=[...document.querySelectorAll('input[type=file]')].map(f=>({accept:f.accept||'(无)', cls:String(f.className).slice(0,40), vis:f.offsetParent!==null})); + const hints=[...document.querySelectorAll('*')].filter(e=>e.children.length===0&&/xls|xlsx|csv|格式|模板|大小|MB/.test(e.innerText||'')&&(e.innerText||'').length<70) + .map(e=>(e.innerText||'').trim()).slice(0,10); + const btns=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').trim()).filter(Boolean).slice(0,12); + return JSON.stringify({fileInputs:fis, hints:[...new Set(hints)], btns:[...new Set(btns)], href:location.href});})()`); +console.log('上传控件:', S(String(o)).slice(0,1200)); +process.exit(0); diff --git a/妙手建品/ms_check.mjs b/妙手建品/ms_check.mjs new file mode 100644 index 0000000..7e7a046 --- /dev/null +++ b/妙手建品/ms_check.mjs @@ -0,0 +1,13 @@ +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('Runtime.enable'); await wait(1200); +// 刷新列表 +await c.evalJS(`(()=>{const b=[...document.querySelectorAll('button,[class*=refresh]')].find(e=>/刷新|查询/.test(e.innerText||'')); if(b) b.click(); return 1;})()`); +await wait(4000); +const o = await c.evalJS(`(()=>{const rows=[...document.querySelectorAll('tr')].filter(e=>e.offsetParent!==null) + .map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(t=>t&&!/文件名称/.test(t)&&/ms_import|解析|成功/.test(t)).slice(0,3); + return JSON.stringify({rows, href:location.href});})()`); +console.log('导入任务:', S(String(o)).slice(0,600)); +process.exit(0); diff --git a/妙手建品/ms_import.csv b/妙手建品/ms_import.csv new file mode 100644 index 0000000..13d8582 --- /dev/null +++ b/妙手建品/ms_import.csv @@ -0,0 +1,48 @@ +链接地址(必填),产品标题,价格(RMB),促销价(RMB) +https://detail.1688.com/offer/548459586741.html,"6.5cm Colorful Plastic Party Whistles for Kids, Blow Out Whistle Toys for Birthday Sports Games",34.0, +https://detail.1688.com/offer/661190598139.html,Retro Hair Tie with Crystal Pendant for Dreadlocks & African Braids,26.64, +https://detail.1688.com/offer/764423265986.html,Magnetic Digital Thermometer Hygrometer for Home Office Kitchen,34.0, +https://detail.1688.com/offer/685456757807.html,Pure Natural Cotton Yarn for Baby Knitting - Soft 16-Ply,26.64, +https://detail.1688.com/offer/735147938123.html,Wide Denim Retro Hair Band for Women College Style Headband,34.0, +https://detail.1688.com/offer/627879474686.html,Cotton Cartoon Print High Socks for Girls Kids Autumn Warm,34.0, +https://detail.1688.com/offer/610164857063.html,Breathable Sports Headband Non-Slip Running Yoga Fitness Accessory,34.0, +https://detail.1688.com/offer/676236596821.html,"Cute Cartoon Baby Hair Clips Set, 3 Pcs Fabric Barrettes for Infant Toddler Girls",34.0, +https://detail.1688.com/offer/708420302844.html,Brass Keychain with 12 Zodiac Signs & 5 Emperor Coins,36.45, +https://detail.1688.com/offer/866537106657.html,Butterfly Resin Buttons for Sweaters Coats Cardigans,34.0, +https://detail.1688.com/offer/547183997437.html,Cute Horse Pendant Necklace Set Silver Jewelry for Girls,36.45, +https://detail.1688.com/offer/657617635303.html,Clear Magnetic Paperclip Box with Pastel Macaron Design,36.45, +https://detail.1688.com/offer/641673262387.html,"Organza Jewelry Pouch Bag with Drawstring, Clear White Gift Box for Rings Earrings",36.45, +https://detail.1688.com/offer/953869908708.html,Transparent Window Stickers Christmas Snowflakes & Cats for Shop Display,19.91, +https://detail.1688.com/offer/703822717997.html,"Stainless Steel Spring Clip for Notebook Pen Holder, Pocket Organizer",20.77, +https://detail.1688.com/offer/863524134410.html,50pcs Pink Aesthetic Vinyl Stickers for Laptop Suitcase DIY Decor Waterproof,20.73, +https://detail.1688.com/offer/694637537727.html,Large Aluminum Foil Food Thermos Bag Insulated Lunch Carrier,22.18, +https://detail.1688.com/offer/623011262210.html,Transparent Snowflake Window Stickers for Christmas Decor,21.27, +https://detail.1688.com/offer/836512561982.html,Cute Plush Velvet Cosmetic Case with Bear Design for Girls,30.2, +https://detail.1688.com/offer/714492799386.html,Canvas Makeup Bag Macaron Color Compact Cosmetic Pouch,28.36, +https://detail.1688.com/offer/591994902720.html,"Cute Cat Pencil Case with Moving Ears and Tail, Soft Plush Pen Organizer",31.91, +https://detail.1688.com/offer/853414558425.html,Soft Silicone Heart Phone Bracelet Keychain Pink Strap,20.45, +https://detail.1688.com/offer/997450831819.html,"Matte PET Emoji Stickers for Goodnotes & Scrapbooking, 10 Sheets Waterproof",18.07, +https://detail.1688.com/offer/639409307567.html,"3D Glowing Butterfly Wall Stickers, Magnetic Light Up Decals",25.82, +https://detail.1688.com/offer/902440558099.html,Large Beige Fabric Hair Tie with Pearl for Buns and Low Ponytails,27.27, +https://detail.1688.com/offer/823838532886.html,Cute Cartoon Cat Key Card Holder for Metro Office & Bag,27.0, +https://detail.1688.com/offer/698782391248.html,Boho Style White PVC Laptop Stickers 10cm Round Decal,23.6, +https://detail.1688.com/offer/707826905566.html,"Ultra-Slim Card Holder Wallet for Men Women, Compact Cash & Card Pouch",22.55, +https://detail.1688.com/offer/1027332215847.html,Silver Matte Tin Photo Box with Clear Window for Cards,22.55, +https://detail.1688.com/offer/682195499823.html,96-Cell Brush Organizer Case for Watercolor & Gouache,21.35, +https://detail.1688.com/offer/823560564174.html,"Cute Heart Hair Ties Set, Soft Elastic Bands for Buns & Ponytails, Colorful Hair Accessories",23.11, +https://detail.1688.com/offer/739147684759.html,Metallic Rectangular Coat Buttons with Leather Cover for Jackets,19.64, +https://detail.1688.com/offer/621300323670.html,Round Transparent Desktop Tape Dispenser with Smooth Cutter,21.89, +https://detail.1688.com/offer/896469995553.html,Angel Wings & Stars Keychain Y2K Retro Bag Charm Cute Accessory,25.18, +https://detail.1688.com/offer/944470567338.html,Korean Style Plaid Triangle Hair Clip with Decorative Button,22.18, +https://detail.1688.com/offer/787261097583.html,"Transparent Crab Hair Clips with Hearts, Clear Acrylic Set",22.18, +https://detail.1688.com/offer/833753630925.html,Wide Plaid Fabric Headband for Women - Comfortable Hair Accessory,21.27, +https://detail.1688.com/offer/719391062058.html,Clear Cosmetic Organizer with 6 Drawers for Desk Storage,23.71, +https://detail.1688.com/offer/857781294719.html,"Cute Bunny Ear Hair Clips for Kids with Ribbon Bow, 2 Pcs",22.73, +https://detail.1688.com/offer/863384503032.html,Elegant Red Rose Crab Hair Clip for Women Stylish Accessory,19.82, +https://detail.1688.com/offer/774494945453.html,Stainless Steel Cat Keychain with Fish Charm Cute Bag Ornament,19.45, +https://detail.1688.com/offer/750271307455.html,Romantic Heart Keychain for Couples Silver Plated Valentine Gift,20.18, +https://detail.1688.com/offer/726442972676.html,Cute Cartoon Animal Mini Hair Clips for Bangs and Small Strands,20.09, +https://detail.1688.com/offer/775057912118.html,"Stainless Steel Keychain with English Letter, Polished Metal Key Holder for Car Bag",19.84, +https://detail.1688.com/offer/893592264148.html,"Cute Plaid Hair Clips & Bands for Kids, Bright Yellow Children's Hair Accessories",26.45, +https://detail.1688.com/offer/607330049419.html,50 Pcs Elastic Thin Hair Ties for Women Girls Daily Use,22.24, +https://detail.1688.com/offer/854003999648.html,"High Elastic Wide Hair Ties Set of 5, Soft No-Break Hair Bands",21.15, diff --git a/妙手建品/ms_tpl.mjs b/妙手建品/ms_tpl.mjs new file mode 100644 index 0000000..aaaa199 --- /dev/null +++ b/妙手建品/ms_tpl.mjs @@ -0,0 +1,28 @@ +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'); +try { await c.send('Browser.setDownloadBehavior',{behavior:'allow', downloadPath:'/home/admin9webs/miaoshou/dl', eventsEnabled:true}); console.log('下载目录已设置'); } +catch(e){ try { await c.send('Page.setDownloadBehavior',{behavior:'allow', downloadPath:'/home/admin9webs/miaoshou/dl'}); console.log('用 Page.setDownloadBehavior'); } catch(e2){ console.log('设置下载失败:', String(e2).slice(0,80)); } } +await wait(800); +// 确保在导入采集页 +const href = String(await c.evalJS('location.href')); +if (!href.includes('importCopy')) { + console.log('切到导入采集页…'); + await c.evalJS(`(()=>{const h=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&(e.innerText||'').trim()==='导入采集'&&e.children.length===0); + if(h.length) for(const ev of ['mousedown','mouseup','click']) h[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`); + await wait(6000); +} +// 关掉可能的弹窗 +await c.evalJS(`(()=>{const b=[...document.querySelectorAll('.jx-dialog__headerbtn,.jx-dialog__close')]; b.forEach(x=>x.click()); return 1;})()`); +await wait(1000); +// 点 导入链接采集 +console.log('子模式:', String(await c.evalJS(`(()=>{const h=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&(e.innerText||'').trim()==='导入链接采集'&&e.children.length===0); + if(!h.length) return 'nf'; for(const ev of ['mousedown','mouseup','click']) h[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'clicked';})()`))); +await wait(3000); +// 点 下载导入模板 +console.log('下载模板:', String(await c.evalJS(`(()=>{const h=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/下载导入模板/.test(e.innerText||'')&&e.children.length===0&&(e.innerText||'').trim().length<12); + if(!h.length) return 'nf'; for(const ev of ['mousedown','mouseup','click']) h[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'clicked';})()`))); +await wait(8000); +process.exit(0); diff --git a/妙手建品/ms_upload.mjs b/妙手建品/ms_upload.mjs new file mode 100644 index 0000000..ac6f75c --- /dev/null +++ b/妙手建品/ms_upload.mjs @@ -0,0 +1,37 @@ +import { listPages, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { CDP, SAN as S } from '/tmp/cdp3.mjs'; +const FILE = process.argv[2] || '/home/admin9webs/miaoshou/dl/links.txt'; +const t = (await listPages()).find(x=>/91miaoshou/i.test(x.url)); +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('DOM.enable'); await c.send('Runtime.enable'); await wait(1000); +// 先确保「导入链接采集」面板是打开的(上一次上传后面板会关掉) +await c.evalJS(`(()=>{const close=[...document.querySelectorAll('.jx-dialog__headerbtn,.jx-dialog__close')]; close.forEach(x=>x.click()); return 1;})()`); +await wait(800); +const hasInput = async () => Number(await c.evalJS(`document.querySelectorAll('input.jx-upload__input').length`)); +if (!await hasInput()) { + await c.evalJS(`(()=>{const h=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&(e.innerText||'').trim()==='导入采集'&&e.children.length===0); + if(h.length) for(const ev of ['mousedown','mouseup','click']) h[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`); + await wait(5000); + await c.evalJS(`(()=>{const h=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&(e.innerText||'').trim()==='导入链接采集'&&e.children.length===0); + if(h.length) for(const ev of ['mousedown','mouseup','click']) h[0].dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`); + await wait(3000); +} +console.log('上传控件数:', await hasInput()); +const { root } = await c.send('DOM.getDocument', { depth: -1 }); +const { nodeId } = await c.send('DOM.querySelector', { nodeId: root.nodeId, selector: 'input.jx-upload__input' }); +console.log('文件输入 nodeId:', nodeId); +if (!nodeId) { console.log('没找到上传控件'); process.exit(1); } +await c.send('DOM.setFileInputFiles', { files: [FILE], nodeId }); +console.log('已注入文件:', FILE); +await wait(4000); +// 点确认 +console.log('确认:', String(await c.evalJS(`(()=>{const b=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*确认\\s*$/.test((e.innerText||'').trim())); + if(!b) return 'nf'; for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'clicked';})()`))); +await wait(9000); +const o = await c.evalJS(`(()=>{ + const rows=[...document.querySelectorAll('tr')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(t=>t&&!/文件名称/.test(t)).slice(0,4); + const toasts=[...document.querySelectorAll('[class*=message],[class*=toast],[class*=notice]')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,80)).filter(Boolean).slice(0,4); + return JSON.stringify({rows, toasts});})()`); +console.log('导入结果:', S(String(o)).slice(0,900)); +try{ await c.shot('/tmp/ms_upload.jpg'); console.log('shot ok'); }catch(e){} +process.exit(0);