From 96cee1c2069ad5ec5c255573acc4375cbc603ed8 Mon Sep 17 00:00:00 2001 From: admin9webs Date: Mon, 21 Sep 2026 07:31:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A6=99=E6=89=8B=E4=BE=A6=E5=AF=9F=EF=BC=9A?= =?UTF-8?q?=E8=88=B0=E9=98=9F=E6=8A=80=E8=83=BD=E6=A1=A3=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=99=A8=E5=B7=B2=E5=A4=B1=E6=95=88=EF=BC=88=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E5=B7=B2=E6=8A=93=EF=BC=89=E3=80=81=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E6=94=B9=E4=B8=BA=E5=BC=BA=E5=88=B6=E3=80=81?= =?UTF-8?q?=E5=AD=90=E8=B4=A6=E5=8F=B7=E4=B8=8D=E8=83=BD=E5=8F=91=E5=B8=83?= =?UTF-8?q?=20=E2=86=92=20=E5=8D=A1=E7=99=BB=E5=BD=95=EF=BC=8C=E5=BE=85?= =?UTF-8?q?=E4=BA=BA=E5=B7=A5=E5=86=B3=E7=AD=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 妙手建品/RECIPE.md | 32 ++++++++++++++++++++++++++++++++ 妙手建品/ms_form.mjs | 14 ++++++++++++++ 妙手建品/ms_login.mjs | 36 ++++++++++++++++++++++++++++++++++++ 妙手建品/ms_probe1.mjs | 15 +++++++++++++++ 妙手建品/ms_probe2.mjs | 15 +++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 妙手建品/RECIPE.md create mode 100644 妙手建品/ms_form.mjs create mode 100644 妙手建品/ms_login.mjs create mode 100644 妙手建品/ms_probe1.mjs create mode 100644 妙手建品/ms_probe2.mjs diff --git a/妙手建品/RECIPE.md b/妙手建品/RECIPE.md new file mode 100644 index 0000000..32fa6e3 --- /dev/null +++ b/妙手建品/RECIPE.md @@ -0,0 +1,32 @@ +# 妙手 ERP(erp.91miaoshou.com)侦察 — 卡在登录,需要人工决策 + +## 现状(2026-09-20) +浏览器里有一个妙手标签,停在**未登录的营销首页**,页面上已有报错: +`账户或密码错误,再失败9次后将锁定账号1个小时` + `请输入图形验证码` +→ 之前有人试过登录且失败过;账号有失败计数,**再错 9 次锁 1 小时**。 + +## ⚠️ 舰队技能档已过期(`agent-a2-import/skills/ecommerce/miaoshou-erp/SKILL.md`) +| 文档写的 | 实测 | +|---|---| +| 账号 `.account-input` / 密码 `.password-input` / 按钮 `.login-button` | **全部不存在**(`account:0, password:0, hasBtn:false`) | +| 「风控较松,验证码非强制,可尝试跳过」 | **现在强制图形验证码**(页面明确提示「请输入图形验证码」) | +| 账号 `xiaochaoren2026` | 档里有;但配的是**子账号,文档自己写明「产品发布不可用」,只有采集箱/导入采集** | + +### 实测的真实登录表单(登录弹窗内) +| 字段 | 选择器 | +|---|---| +| 账号 | `input.login-form__input[placeholder="手机号/子账号/邮箱"]` | +| 密码 | `input.login-form__input[type=password]` | +| 验证码 | `input.captcha-field__input`(placeholder `请输入验证码`) | +| 登录按钮 | `button.login-form__submit-btn`(文案 `立即登录`) | + +## 为什么我没继续 +1. **要花登录次数**:验证码是图形码,试错就计数,再错 9 次锁 1 小时; +2. **账号权限**:档里明确写了这个子账号**不能发布产品**,只有采集箱/导入采集 + —— 也就是说「妙手各平台铺货」用这个账号**可能根本做不到**,需要主账号; +3. 舰队技能档的凭据可能已轮换(否则不会出现失败计数)。 + +## 三条可走的路(需要你选) +- **A(最稳)**:你在 VNC 里手动登录一次,我接手已登录的会话做采集箱/导入采集的自动化; +- **B**:授权我用积分/打码能力试 1–2 次(会消耗登录计数,且若密码已轮换就是白费); +- **C**:先跳过妙手,把最后的力量放在 Lazada / fruugo(这两条不需要新账号)。 diff --git a/妙手建品/ms_form.mjs b/妙手建品/ms_form.mjs new file mode 100644 index 0000000..a6c719f --- /dev/null +++ b/妙手建品/ms_form.mjs @@ -0,0 +1,14 @@ +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 ins=[...document.querySelectorAll('input')].filter(e=>e.offsetParent!==null).map(e=>({ + type:e.type||'', ph:e.placeholder||'', cls:String(e.className).slice(0,44), name:e.name||'', id:e.id||''})); + const btns=[...document.querySelectorAll('button,a[role=button],.btn')].filter(e=>e.offsetParent!==null) + .map(e=>({t:(e.innerText||'').trim().slice(0,12), cls:String(e.className).slice(0,40)})).filter(x=>x.t).slice(0,10); + const imgs=[...document.querySelectorAll('img')].filter(e=>e.offsetParent!==null&&/captcha|code|verify/i.test(e.src||'')).map(e=>e.src.slice(0,80)); + return JSON.stringify({ins:ins.slice(0,8), btns, captchaImgs:imgs.slice(0,3)});})()`); +console.log('登录表单:', S(String(o)).slice(0,900)); +process.exit(0); diff --git a/妙手建品/ms_login.mjs b/妙手建品/ms_login.mjs new file mode 100644 index 0000000..ce8367b --- /dev/null +++ b/妙手建品/ms_login.mjs @@ -0,0 +1,36 @@ +import { listPages, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { CDP, SAN as S } from '/tmp/cdp3.mjs'; +import { readFileSync } from 'fs'; +// 凭据来自舰队技能档 agent-a2-import/skills/ecommerce/miaoshou-erp/SKILL.md(不回显) +const doc = readFileSync('/home/admin9webs/agent-a2-import/skills/ecommerce/miaoshou-erp/SKILL.md','utf8'); +const acc = (doc.match(/xiaochaoren\d+/) || [])[0]; +const pwd = (doc.match(/Bingo\d+@/) || [])[0]; +console.log('账号:', acc ? acc.slice(0,4)+'***' : '未解析到', ' 密码:', pwd ? '已解析' : '未解析'); +if(!acc || !pwd){ console.log('凭据解析失败,放弃登录'); process.exit(1); } + +const t = (await listPages()).find(x=>/91miaoshou/i.test(x.url)); +if(!t){ console.log('无妙手标签'); process.exit(1); } +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('Page.enable'); await c.send('Runtime.enable'); +await wait(1500); + +const filled = await c.evalJS(`(()=>{ + const set=(sel,v)=>{const el=document.querySelector(sel); if(!el) return 0; + const setter=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set; + setter.call(el,v); el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); + el.dispatchEvent(new KeyboardEvent('keyup',{bubbles:true})); return 1;}; + const a=set('.account-input', ${JSON.stringify(acc)}); + const p=set('.password-input', ${JSON.stringify(pwd)}); + const captcha=document.querySelector('.captcha-text'); + return JSON.stringify({account:a, password:p, hasCaptcha:!!captcha, hasBtn:!!document.querySelector('.login-button')});})()`); +console.log('填表:', S(String(filled)).slice(0,200)); +await wait(600); +const clicked = await c.evalJS(`(()=>{const b=document.querySelector('.login-button'); if(!b) return 'no-btn'; + for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'clicked';})()`); +console.log('提交:', String(clicked)); +await wait(10000); +const after = await c.evalJS(`(()=>{const err=[...document.querySelectorAll('*')].filter(e=>e.children.length===0&&/错误|失败|验证码|锁定/.test(e.innerText||'')).map(e=>(e.innerText||'').trim()).slice(0,3); + return JSON.stringify({href:location.href, title:document.title, login:!!document.querySelector('input[type=password]'), err});})()`); +console.log('结果:', S(String(after)).slice(0,400)); +try{ await c.shot('/tmp/ms_login.jpg'); console.log('shot ok'); }catch(e){} +process.exit(0); diff --git a/妙手建品/ms_probe1.mjs b/妙手建品/ms_probe1.mjs new file mode 100644 index 0000000..b56794e --- /dev/null +++ b/妙手建品/ms_probe1.mjs @@ -0,0 +1,15 @@ +import { newPage, closePage, wait, listPages } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { SAN as S } from '/tmp/cdp3.mjs'; +// 先看有没有现成的妙手标签 +const pages = (await listPages()).filter(t=>/miaoshou|91miao/i.test(t.url)); +console.log('现有妙手标签:', pages.length, pages.map(t=>t.url.slice(0,60)).join(' | ')); +const c = pages.length ? null : await newPage('https://erp.91miaoshou.com/'); +if (c) await wait(12000); +if (!c) { console.log('已有标签,跳过新开'); process.exit(0); } +const o = await c.evalJS(`(()=>{ + const nav=[...document.querySelectorAll('a,li,div')].filter(e=>e.offsetParent!==null&&(e.innerText||'').trim().length>1&&(e.innerText||'').trim().length<8).map(e=>(e.innerText||'').trim()); + const uniq=[...new Set(nav)].slice(0,40); + return JSON.stringify({title:document.title, href:location.href, hasLogin:!!document.querySelector('input[type=password]'), nav:uniq});})()`); +console.log('页面:', S(String(o)).slice(0,900)); +try{ await c.shot('/tmp/ms1.jpg'); console.log('shot ok'); }catch(e){} +await closePage(c); process.exit(0); diff --git a/妙手建品/ms_probe2.mjs b/妙手建品/ms_probe2.mjs new file mode 100644 index 0000000..e43307a --- /dev/null +++ b/妙手建品/ms_probe2.mjs @@ -0,0 +1,15 @@ +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)); +if(!t){ console.log('无妙手标签'); process.exit(1); } +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('Page.enable'); await c.send('Runtime.enable'); +await wait(1500); +const o = await c.evalJS(`(()=>{ + const nav=[...new Set([...document.querySelectorAll('a,span,li')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').trim()).filter(t=>t&&t.length<=10))].slice(0,45); + const menu=[...document.querySelectorAll('.el-menu-item,.ant-menu-item,[class*=menu-item]')].map(e=>(e.innerText||'').trim()).filter(Boolean).slice(0,25); + const body=(document.body.innerText||'').replace(/\\s+/g,' ').trim().slice(0,300); + return JSON.stringify({title:document.title, href:location.href, login:!!document.querySelector('input[type=password]'), menu, nav:nav.slice(0,30), body});})()`); +console.log('妙手页:', S(String(o)).slice(0,1500)); +try{ await c.shot('/tmp/ms2.jpg'); console.log('shot ok'); }catch(e){} +process.exit(0);