店小秘多平台铺货配方:7 平台建品脚本 + 静默拦截诊断法 + 项目状态

- TikTok/Shopee/速卖通/Walmart 各 46-47 条已在待发布
- Ozon 47 条已建、改价 40/47
- SHEIN 配方跑通第一条;Temu 差「预览图」一步
- 核心产出:MYJ.message.error 钩子诊断法(解开了三个平台的保存静默拦截)
- 含下一轮提示词 NEXT-ROUND-PROMPT.md
This commit is contained in:
admin9webs committed 2026-09-20 17:29:38 +08:00
commit f5720ad613
94 files changed
+21561

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
# SHEIN 建品配方(已跑通第一条)
## 完整流程
1. `https://www.dianxiaomi.com/web/sheinProduct/add`
2. **选店铺**(按品类分店,见 shein_route.json)
3. **选类目**:点「选择分类」→ 弹窗 `input[placeholder="搜索分类名称"]` → 搜索 → 点 `.search-result-item`
4. **标题**:`sheinAddProductStore.formState.productName`
5. **★ 加第二个规格**:点 `.theme-select-box` 里的 `尺寸(Size)`
—— 这是之前一直卡住的关键!类目要求**颜色+尺寸两个规格**,只选颜色不行
6. **选规格值**:两个 `.sku-info-box` 里各点一个 `.sku-option`(要点里面的 `label.d-checkbox`)
7. **填 SKU 行**:`sheinSkuDataStore.formState.skuDataList[]`
`skcSupplierCode / skcRetailPrice / supplyPrice / stockVariety / weight / length / width / height`
⚠️ **加第二个规格会重建 SKU 行,之前的填充会丢** —— 必须在规格选完之后再填
8. **尺码表**:点「编辑尺码表」→ 弹窗内 6 个 `placeholder="请输入"` 的 input → 点「确定」
9. **图片**:`sheinImgStore.formState.mainImgData.multipleSpuImgs` → **数组**
10. **保存** → `POST /api/sheinProduct/add.json`
## 关键教训
- 报错 `有必选规格未选择` 的真实含义是:**类目定义的规格没选全**,不是主规格没值
- 加规格会重置 SKU 行 → **顺序不能错**:先规格、后 SKU、再尺码表
- 所有报错都会被 `MYJ.message.error` 吞掉,必须挂钩子才看得到
## 待批量
- 40 条按品类分到 4 家店(美妆18 / 文具13 / 箱包8 / 内睡袜子1)
- 需要把上述流程写成 `shbatch.mjs`,逐条跑
+22
View File
@@ -0,0 +1,22 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const r = await c.evalJS(`(()=>{const box=[...document.querySelectorAll('.theme-select-box')].filter(e=>e.offsetParent!==null).pop();
if(!box)return'nf';
const chips=[...box.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/^\\s*尺寸\\(Size\\)\\s*$/.test((e.innerText||'').trim()));
if(!chips.length)return'no-chip';
const el=chips[chips.length-1];
for(const ev of ['mousedown','mouseup','click']) el.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
return 'clicked';})()`);
console.log('点尺寸:', String(r));
await wait(4000);
const o = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
const boxes=[...document.querySelectorAll('.sku-info-box')].filter(e=>e.offsetParent!==null).map((b,i)=>({
i, txt:(b.innerText||'').replace(/\\s+/g,' ').slice(0,90), opts:b.querySelectorAll('.sku-option').length,
sels:[...b.querySelectorAll('.ant-select')].map(s=>(s.innerText||'').replace(/\\s+/g,' ').slice(0,16)),
ins:[...b.querySelectorAll('input')].filter(i=>i.offsetParent!==null&&i.type!=='checkbox').map(i=>({ph:i.placeholder||'',cls:String(i.className||'').slice(0,40)})).slice(0,4)}));
const btns=[...document.querySelectorAll('button')].filter(b=>b.offsetParent!==null&&/添加/.test(b.innerText||'')).map(b=>({t:(b.innerText||'').trim(),dis:b.disabled}));
return JSON.stringify({attrs:(a.formState.skuAttrs||[]).map(x=>({n:x.name, opts:(x.options||[]).length})), boxes, btns},null,1);})()`);
console.log(String(o).slice(0,1400));
process.exit(0);
+21
View File
@@ -0,0 +1,21 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const reqs=[];
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
await c.send('Network.enable').catch(()=>{});
const btns = await c.evalJS(`(()=>[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).map((b,i)=>{
const r=b.getBoundingClientRect(); return {i, t:(b.innerText||'').replace(/\\s+/g,' ').trim().slice(0,14), y:Math.round(r.y), dis:b.disabled};}).filter(x=>x.t))()`);
console.log('按钮:', JSON.stringify(btns).slice(0,700));
// 点“保存”并立刻轮询任何反馈
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
if(x){ for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); } return 1;})()`);
const seen=[];
for(let i=0;i<18;i++){ await wait(700);
const m=await c.evalJS(`(()=>{const a=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal-confirm,.ant-modal')].filter(e=>e.offsetParent!==null)
.map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,130)).filter(Boolean); return JSON.stringify(a.slice(0,3));})()`);
JSON.parse(String(m)).forEach(x=>seen.push(x)); }
console.log('反馈:', [...new Set(seen)].slice(0,4).join(' || ').slice(0,400));
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
process.exit(0);
+22
View File
@@ -0,0 +1,22 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const o = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/产品分类/.test(l.innerText||'');});
if(!it)return'nf'; return JSON.stringify({txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,120), html:it.innerHTML.slice(0,600)});})()`);
console.log('分类控件:', String(o).slice(0,900));
const pos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/产品分类/.test(l.innerText||'');});
if(!it)return'[]'; const el=it.querySelector('.ant-select-selector')||it.querySelector('input');
if(!el)return'[]'; el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
if(pos.length){ for(const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[1],button:'left',clickCount:1}); await wait(4000); }
const o2 = await c.evalJS(`(()=>{
const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).map(m=>({t:(m.querySelector('.ant-modal-title')||{}).innerText||'',
inputs:[...m.querySelectorAll('input')].map(i=>i.placeholder||i.type).slice(0,5),
btns:[...m.querySelectorAll('button')].map(b=>(b.innerText||'').trim()).slice(0,6),
txt:(m.innerText||'').replace(/\\s+/g,' ').slice(0,300)}));
const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).map(d=>(d.innerText||'').replace(/\\s+/g,' ').slice(0,260));
return JSON.stringify({modals:ms, dropdowns:dd},null,1);})()`);
console.log(String(o2).slice(0,1400));
process.exit(0);
+26
View File
@@ -0,0 +1,26 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const pick = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/主规格/.test(l.innerText||'');});
if(!it)return'nf'; const box=it.querySelector('.sku-info-box')||it;
const opts=[...box.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null);
const hit=opts.find(e=>/Black\\(/.test(e.innerText||''))||opts[0];
if(!hit)return'no-opt';
const lab=hit.querySelector('label'); const inp=hit.querySelector('input[type=checkbox]');
if(lab) for(const ev of ['mousedown','mouseup','click']) lab.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
if(inp && !inp.checked) inp.click();
return 'clicked:'+(hit.innerText||'').trim()+' checked='+(inp?inp.checked:'?');})()`);
console.log('点色:', pick);
await wait(7000);
const o = await c.evalJS(`(()=>{
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,15),
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
return JSON.stringify({tbls, n:(d.formState.skuDataList||[]).length, row0:(d.formState.skuDataList||[])[0],
chosen:(a.formState.skuAttrs||[]).map(x=>({name:x.name, vals:(x.attributeValues||[]).map(v=>v.attribute_value||v)}))},null,1);})()`);
console.log(String(o).slice(0,1800));
process.exit(0);
+34
View File
@@ -0,0 +1,34 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
// 打开主规格颜色下拉
const pos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/主规格/.test(l.innerText||'');});
const s=it.querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'});
const r=s.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1});
await wait(3000);
const opts = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
return JSON.stringify({n:its.length, first:its.slice(0,6).map(e=>(e.innerText||'').trim())});})()`);
console.log('颜色选项:', String(opts).slice(0,400));
// 选 Multicolor / Black(找不到就第一个)
const pick = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
let hit=its.find(o=>/Multicolor|Multi-color/i.test(o.innerText||'')) || its.find(o=>/^\\s*Black\\(/.test((o.innerText||'').trim())) || its[0];
if(!hit)return'no-opt';
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
return 'picked:'+(hit.innerText||'').trim();})()`);
console.log('选择:', pick);
await wait(6000);
const o = await c.evalJS(`(()=>{
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,14),
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
return JSON.stringify({tbls, skuDataList:(d.formState.skuDataList||[]).slice(0,2), n:(d.formState.skuDataList||[]).length,
chosen:(a.formState.skuAttrs||[]).map(x=>({name:x.name, n:(x.attributeValues||x._chosen||[]).length}))},null,1);})()`);
console.log(String(o).slice(0,1800));
process.exit(0);
+62
View File
@@ -0,0 +1,62 @@
import { newTarget, CDP } from '/tmp/cdp3.mjs';
import fs from 'fs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const SHOP = process.argv[2] || '③shein-易捷天宇-美妆';
const CATKW = process.argv[3] || '发夹';
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
for (const t of tl.filter(x=>x.type==='page'&&/shein/i.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
await wait(1000);
const t = await newTarget('about:blank');
const c = await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Page.enable'); await c.send('Runtime.enable');
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/sheinProduct/add'});
await wait(21000);
// 店铺
const pos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/店铺/.test(l.innerText||'');});
const s=(it||document).querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'});
const r=s.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[1],button:'left',clickCount:1});
await wait(2500);
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
if(hit) for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(8000);
// 开分类下拉并选目标
const cp = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/产品分类/.test(l.innerText||'');});
const s=it.querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'});
const r=s.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:cp[0],y:cp[1],button:'left',clickCount:1});
await wait(3500);
const pk = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
const hit=its.find(o=>new RegExp('^\\\\s*'+${JSON.stringify(CATKW)}).test((o.innerText||'').trim()));
if(!hit)return'no-hit:'+its.slice(0,5).map(o=>o.innerText.trim()).join(',');
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
return 'picked:'+(hit.innerText||'').trim();})()`);
console.log('类目:', pk);
await wait(12000);
const o = await c.evalJS(`(()=>{
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
const req=!!x.querySelector('.ant-form-item-required');
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
return (req?'* ':' ')+lab.slice(0,24).padEnd(25)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,22);
}).filter(x=>x.trim().length>4);
const tables=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,12),
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
const cut=(x,d=0)=>(d>3?'…':(Array.isArray(x)?(x.length?'['+x.length+'] '+JSON.stringify(x[0]).slice(0,220):'[]'):(x&&typeof x==='object'?Object.fromEntries(Object.entries(x).map(([k,v])=>[k,cut(v,d+1)])):x)));
const dump={};
for(const n of ['sheinAddProductStore','sheinSkuDataStore','sheinSkuAttrStore','sheinAddBasicStore','sheinImgStore'])
{ try{dump[n]=cut(JSON.parse(JSON.stringify(p._s.get(n).$state)));}catch(e){dump[n]='ERR';} }
return JSON.stringify({items, tables, dump},null,1);})()`);
fs.writeFileSync('/tmp/shein_deep.json', String(o));
const j=JSON.parse(String(o));
console.log('=== form ==='); j.items.forEach(x=>console.log(' '+x));
console.log('=== tables ==='); console.log(JSON.stringify(j.tables));
console.log('=== stores ===');
for(const [n,v] of Object.entries(j.dump)) console.log(' ##',n,JSON.stringify(v).slice(0,520));
process.exit(0);
+189
View File
@@ -0,0 +1,189 @@
{
"items": [
"* 店铺账号 [select] ③shein-易捷天宇-美妆",
"* 产品分类 [select] 发夹(Hair Clips)",
" 产品属性 [select] No(否)",
" 品牌 [input] ",
" 是否含木质材料 (Whether it cont [select] No(否)",
" 敏感类别 (Hazard Category) [input] ",
" 关税种类 (Tariff Type) [select] Hairpin(发夹)",
" 材质 (Material) [select] Zinc Alloy(锌合金)",
" 风格 (Style) [input] ",
" 数量 (Quantity) [select] piece(s)(件)",
" 季节 (Season) [input] ",
" 细化图案 (Specific Pattern) [input] ",
" 类型 (Type) [input] ",
" 特征 (Features) [input] ",
" 证书编号/检测机构 (Certificate N [input] ",
" 是否有型号 (Is there a Model [input] ",
" 电池使用方式 (Battery Usage Ty [input] ",
" 版型 (Version) [input] ",
" 包装类型 (type of packaging) [input] ",
" 成分 (Composition) [input] ",
" 次要材质 (Other Material) [input] ",
" 发圈类型 (hair tie type) [input] ",
" 元素 (Element) [select] None(无)",
" 制作工艺 (Craftsmanship Type [input] ",
" 企划风格 (Design Style) [input] ",
" 产品型号 (Product Model) [input] ",
" 镀贵金属类型 (Plating) [input] ",
" 镶嵌类型 (Inlay type) [input] ",
" 织造方式 (Fabric Weave Type) [input] ",
" NCM-BR专属 (Ncm-Br Exclusi [input] ",
" 系列 (Series) [input] ",
" 是否多套件 [-] ",
" 店小秘分类 [-] ",
" 来源URL [input] ",
" [input] ",
"* 产品标题: [input] ",
" 商品IP [input] ",
" 图片模式 [input] 1",
" 产品视频 [select] SHEIN国际站",
"* 主规格 [select] 颜色(Color)",
" 其他规格 [-] ",
"* 尺码表 [-] "
],
"tables": [],
"dump": {
"sheinAddProductStore": {
"formState": {
"imageType": 0,
"productName": ""
},
"dataState": {
"newSizeGroups": "[]",
"newBootSizeGroups": "[]"
}
},
"sheinSkuDataStore": {
"formState": {
"identifierCodeType": 1,
"supplierType": "EAN",
"skuDataList": "[]",
"deleteCombinationSkus": "[]",
"siteTableInfoData": "[]",
"sampleInfoObj": {
"sampleJudgeType": "2",
"reserveSampleFlag": "2",
"spotFlag": "1"
}
},
"dataState": {
"skcRetailPriceEdit": true,
"stockEdit": true,
"specialPriceEdit": true,
"supplyPriceEdit": true,
"priceEdit": true
},
"siteStockStr": "_stock",
"recordMainVariant": {}
},
"sheinSkuAttrStore": {
"formState": {
"skuAttrs": "[1] {\"attributeId\":27,\"name\":\"颜色\",\"nameEn\":\"Color\",\"_options\":[{\"attribute_value\":\"Apricot\",\"attribute_value_en\":\"Apricot\",\"attribute_value_id\":78,\"is_show\":1,\"supplier_id\":0,\"attribute_value_cn\":\"杏色\"},{\"attribute_value\":\"Be",
"sizeTableInfo": "[]"
},
"dataState": {
"mainVariantId": 27,
"mainVariantList": "[1] {\"id\":4810372362706115,\"puid\":70000,\"shopId\":6226408,\"categoryId\":0,\"productTypeId\":603,\"businessMode\":\"5\",\"attributeId\":27,\"name\":\"颜色\",\"nameEn\":\"Color\",\"show\":1,\"label\":1,\"mode\":2,\"source\":1,\"status\":3,\"type\":1,\"dimensi",
"variantList": "[3] {\"id\":4810372362706123,\"puid\":70000,\"shopId\":6226408,\"categoryId\":0,\"productTypeId\":603,\"businessMode\":\"5\",\"attributeId\":87,\"name\":\"尺寸\",\"nameEn\":\"Size\",\"show\":1,\"label\":0,\"mode\":2,\"source\":1,\"status\":3,\"type\":1,\"dimensio",
"isHasSizeChart": true,
"cacheIsHasSizeChart": true,
"sizeTableData": "[6] {\"id\":4810372362706189,\"puid\":70000,\"shopId\":6226408,\"categoryId\":0,\"productTypeId\":603,\"businessMode\":\"5\",\"attributeId\":55,\"name\":\"长度 (cm)\",\"nameEn\":\"Length (cm)\",\"show\":1,\"label\":0,\"mode\":0,\"source\":1,\"status\":3,\"type\"",
"isAllowEditSkcData": "[]",
"collectDetailImages": ""
},
"customAttrOptions": {
"27": "[]"
},
"recordData": {
"oldOption": "[]",
"newOption": "[]"
}
},
"sheinAddBasicStore": {
"formState": {
"shopId": "6226408",
"categoryId": 2734
},
"shopList": "[11] {\"id\":5986682,\"puid\":70000,\"name\":\"⑧Shein-易世通达-文具\",\"apiKey\":null,\"merchantId\":\"14349C4899B04FE59CECE6A4EA6CDB9C\",\"merchantUserName\":\"2\",\"awsAccessKeyId\":\"zh-cn|CNY\",\"marketPlaceId\":\"4328424\",\"lastSyncTimeProduct\":null,\"l",
"dataState": {
"categories": "[67] {\"id\":4810372411213177,\"puid\":70000,\"shopId\":6226408,\"categoryId\":2735,\"productTypeId\":604,\"parentId\":2732,\"name\":\"发爪\",\"nameEn\":\"Hair Claws\",\"nodePath\":\"服饰装饰品(Apparel Accessories)/女士配饰(Women Accessories)/女士发饰(Women Hair ",
"categoryPathName": "服饰装饰品(Apparel Accessories) > 女士配饰(Women Accessories) > 女士发饰(Women Hair Accessories) > 发夹(Hair Clips)",
"shopIds": "[1] \"6226408\"",
"euSiteList": "[]",
"shopSiteList": "[57] {\"id\":4810364349509615,\"puid\":70000,\"shopId\":6226408,\"currency\":\"USD\",\"siteAbbr\":\"shein-www\",\"siteName\":\"SHEIN国际站\",\"mainSite\":\"shein\",\"mainSiteName\":\"SHEIN\",\"storeType\":0,\"symbolLeft\":\"$\",\"symbolRight\":\"\",\"rateInfo\":\"[{\\",
"warehouseList": "[]",
"euSiteStockShopList": "[]",
"categoryUpdated": false
},
"currencyCode": "CNY",
"isEurSite": false,
"isAllowEditOnlineProductFlag": false,
"infoDisplayRules": {
"image_module_show": true,
"reference_module_show": false,
"allow_switch_spu_picture": true,
"sample_spec_show": false,
"sample_spec_required": false,
"minimum_stock_quantity_show": false,
"minimum_stock_quantity_required": false,
"reference_product_link_show": false,
"reference_product_link_required": false,
"proof_of_stock_show": false,
"proof_of_stock_required": false,
"skc_retail_price_show": true,
"skc_retail_price_required": false,
"quantity_info_show": true,
"quantity_info_required": false,
"package_type_show": true,
"package_type_required": false,
"supplier_barcode_show": false,
"supplier_barcode_required": false,
"goods_ip_show": true,
"goods_ip_required": false,
"product_video_show": true,
"product_video_required": false,
"shelf_require_show": true,
"shelf_require_required": false,
"title_max_length": 150
},
"imageDisplayRules": {
"switch_spu_picture": false,
"spu_image_detail_show": false,
"spu_image_detail_required": false,
"spu_image_detail_single": false,
"spu_image_square_show": false,
"spu_image_square_required": false,
"skc_image_detail_show": true,
"skc_image_detail_required": true,
"skc_image_detail_single": false,
"skc_image_square_show": true,
"skc_image_square_required": true,
"sku_image_detail_show": true,
"sku_image_detail_required": false,
"package_type_show": false,
"package_type_required": false,
"supplier_barcode_show": false,
"supplier_barcode_required": false,
"shelf_require_show": false,
"shelf_require_required": false,
"title_max_length": 1000
},
"isMxSite": false,
"isJapanSite": false,
"isKoreanSite": false
},
"sheinImgStore": {
"formState": {
"mainImgData": {
"multipleSpuImgs": "[]"
}
},
"dataState": {
"newSizeGroups": "[]",
"newBootSizeGroups": "[]"
}
}
}
}
+114
View File
@@ -0,0 +1,114 @@
{
"stores": [
"sheinAddStore",
"sheinAddProductStore",
"sheinSkuDataStore",
"sheinSkuAttrStore",
"sheinAddBasicStore",
"sheinAddOtherStore",
"sheinQualoficaStore",
"sheinProductVideoStore",
"sheinImgStore"
],
"items": [
"* 店铺账号 [select] ①Shein-武汉九个网-箱包",
"* 产品分类 [input] ",
" 是否多套件 [-] ",
" 店小秘分类 [-] ",
" 来源URL [input] ",
" [input] ",
"* 产品标题: [input] ",
" 图片模式 [input] 1"
],
"dump": {
"sheinAddStore": {
"attrsInfo.showSizeAttr": "false",
"sharedData.isFeedBacking": "false",
"sharedData.isAllowEditOnline": "false",
"sharedData.variantImageNum": "0",
"dataState.isVip": "true"
},
"sheinAddProductStore": {
"formState.imageType": "0"
},
"sheinSkuDataStore": {
"formState.identifierCodeType": "1",
"formState.supplierType": "EAN",
"dataState.skcRetailPriceEdit": "true",
"dataState.stockEdit": "true",
"dataState.specialPriceEdit": "true",
"dataState.supplyPriceEdit": "true",
"dataState.priceEdit": "true",
"siteStockStr": "_stock"
},
"sheinSkuAttrStore": {
"dataState.isHasSizeChart": "false",
"dataState.cacheIsHasSizeChart": "false"
},
"sheinAddBasicStore": {
"formState.shopId": "6229819",
"dataState.categoryUpdated": "false",
"currencyCode": "CNY",
"isEurSite": "false",
"isAllowEditOnlineProductFlag": "false",
"infoDisplayRules.image_module_show": "false",
"infoDisplayRules.reference_module_show": "false",
"infoDisplayRules.allow_switch_spu_picture": "false",
"infoDisplayRules.sample_spec_show": "false",
"infoDisplayRules.sample_spec_required": "false",
"infoDisplayRules.minimum_stock_quantity_show": "false",
"infoDisplayRules.minimum_stock_quantity_required": "false",
"infoDisplayRules.reference_product_link_show": "false",
"infoDisplayRules.reference_product_link_required": "false",
"infoDisplayRules.proof_of_stock_show": "false",
"infoDisplayRules.proof_of_stock_required": "false",
"infoDisplayRules.skc_retail_price_show": "false",
"infoDisplayRules.skc_retail_price_required": "false",
"infoDisplayRules.quantity_info_show": "false",
"infoDisplayRules.quantity_info_required": "false",
"infoDisplayRules.package_type_show": "false",
"infoDisplayRules.package_type_required": "false",
"infoDisplayRules.supplier_barcode_show": "false",
"infoDisplayRules.supplier_barcode_required": "false",
"infoDisplayRules.goods_ip_show": "false",
"infoDisplayRules.goods_ip_required": "false",
"infoDisplayRules.product_video_show": "false",
"infoDisplayRules.product_video_required": "false",
"infoDisplayRules.shelf_require_show": "false",
"infoDisplayRules.shelf_require_required": "false",
"infoDisplayRules.title_max_length": "1000",
"imageDisplayRules.switch_spu_picture": "false",
"imageDisplayRules.spu_image_detail_show": "false",
"imageDisplayRules.spu_image_detail_required": "false",
"imageDisplayRules.spu_image_detail_single": "false",
"imageDisplayRules.spu_image_square_show": "false",
"imageDisplayRules.spu_image_square_required": "false",
"imageDisplayRules.skc_image_detail_show": "true",
"imageDisplayRules.skc_image_detail_required": "true",
"imageDisplayRules.skc_image_detail_single": "false",
"imageDisplayRules.skc_image_square_show": "true",
"imageDisplayRules.skc_image_square_required": "true",
"imageDisplayRules.sku_image_detail_show": "true",
"imageDisplayRules.sku_image_detail_required": "false",
"imageDisplayRules.package_type_show": "false",
"imageDisplayRules.package_type_required": "false",
"imageDisplayRules.supplier_barcode_show": "false",
"imageDisplayRules.supplier_barcode_required": "false",
"imageDisplayRules.shelf_require_show": "false",
"imageDisplayRules.shelf_require_required": "false",
"imageDisplayRules.title_max_length": "1000",
"isMxSite": "false",
"isJapanSite": "false",
"isKoreanSite": "false"
},
"sheinAddOtherStore": {},
"sheinQualoficaStore": {
"formData.isGrayShow": "false",
"formData.isShow": "false"
},
"sheinProductVideoStore": {
"dataState.supported": "false"
},
"sheinImgStore": {}
}
}
+62
View File
@@ -0,0 +1,62 @@
{
"美妆": {
"shop": "③shein-易捷天宇-美妆",
"kw": {
"661190598139": "大肠圈",
"902440558099": "大肠圈",
"823560564174": "大肠圈",
"607330049419": "大肠圈",
"854003999648": "大肠圈",
"735147938123": "发箍",
"610164857063": "发箍",
"833753630925": "发箍",
"676236596821": "发夹",
"944470567338": "发夹",
"787261097583": "发夹",
"857781294719": "发夹",
"863384503032": "发夹",
"726442972676": "发夹",
"893592264148": "发夹",
"836512561982": "化妆包",
"714492799386": "化妆包",
"719391062058": "化妆包"
}
},
"文具": {
"shop": "⑥Shein-南京亿千-文具",
"kw": {
"953869908708": "涂鸦贴纸",
"863524134410": "涂鸦贴纸",
"623011262210": "涂鸦贴纸",
"997450831819": "涂鸦贴纸",
"639409307567": "涂鸦贴纸",
"698782391248": "涂鸦贴纸",
"591994902720": "笔袋",
"682195499823": "笔袋",
"657617635303": "回形针",
"703822717997": "回形针",
"621300323670": "桌面摆件",
"823838532886": "卡套",
"1027332215847": "文具收纳盒"
}
},
"箱包": {
"shop": "①Shein-武汉九个网-箱包",
"kw": {
"641673262387": "其他收纳包",
"707826905566": "卡包",
"694637537727": "保温午餐包",
"708420302844": "包包挂件",
"750271307455": "包包挂件",
"774494945453": "包包挂件",
"775057912118": "包包挂件",
"896469995553": "包包挂件"
}
},
"内睡袜子": {
"shop": "⑤Shein-惠州九衢-内睡(袜子)",
"kw": {
"627879474686": "袜子"
}
}
}
+42
View File
@@ -0,0 +1,42 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const reqs=[];
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
await c.send('Network.enable').catch(()=>{});
const IMGS=['https://cbu01.alicdn.com/img/ibank/O1CN01IHtNAHKMzNC1chua_!!2222444614399-0-cib.jpg',
'https://cbu01.alicdn.com/img/ibank/O1CN01UGEWafTzqTH1chua_!!2222444614399-0-cib.jpg',
'https://cbu01.alicdn.com/img/ibank/O1CN01d0hA4N72jzD1chua_!!2222444614399-0-cib.jpg',
'https://cbu01.alicdn.com/img/ibank/O1CN01edgGxIlmdtJ1chua_!!2222444614399-0-cib.jpg'];
const r = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const d=p._s.get('sheinSkuDataStore').$state.formState;
const rows=d.skuDataList||[]; let n=0;
rows.forEach((x,i)=>{ x.skcSupplierCode='SHEIN-TEST-'+(i+1); x.skcRetailPrice='12.90'; x.supplyPrice='8.50';
x.stockVariety='999'; x.weight='100'; x.length='200'; x.width='150'; x.height='30'; n++; });
const im=p._s.get('sheinImgStore').$state.formState;
im.mainImgData.multipleSpuImgs=${JSON.stringify(IMGS.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800})))};
return JSON.stringify({rows:n, imgs:im.mainImgData.multipleSpuImgs.length, imgType:Array.isArray(im.mainImgData.multipleSpuImgs)?'array':typeof im.mainImgData.multipleSpuImgs});})()`);
console.log('填充:', r);
await wait(3500);
const chk = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&/图片/.test(e.innerText||'')&&e.querySelectorAll('img').length>0);
return JSON.stringify({domImgs:it.length?it[0].querySelectorAll('img').length:0});})()`);
console.log('UI 图片:', String(chk));
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
const sv = await c.evalJS(`(()=>{const xs=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null);
let x=xs.find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()))||xs.find(e=>/保存/.test(e.innerText||''));
if(!x)return'nf'; for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return (x.innerText||'').trim();})()`);
console.log('保存按钮:', sv);
await wait(13000);
await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
for(const m of ms){const b=[...m.querySelectorAll('button')].find(x=>/确定|确认/.test(x.innerText||'')); if(b)b.click();} return 1;})()`);
await wait(4000);
console.log('POST 请求:', JSON.stringify([...new Set(reqs)].slice(0,5)));
const red = await c.evalJS(`(()=>{const red=[]; document.querySelectorAll('*').forEach(el=>{ if(el.offsetParent===null||el.children.length)return;
const t=(el.innerText||'').trim(); if(!t||t.length>60)return;
const s=getComputedStyle(el); const m=(s.color||'').match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); if(!m)return;
if(+m[1]>160&&+m[2]<140&&+m[3]<140) red.push(t); });
const toasts=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
return JSON.stringify({red:[...new Set(red)].slice(0,10), toasts:[...new Set(toasts)].slice(0,3)});})()`);
console.log('红字/提示:', String(red).slice(0,600));
process.exit(0);
+38
View File
@@ -0,0 +1,38 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const reqs=[];
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
await c.send('Network.enable').catch(()=>{});
// 两个规格组各点一个 .sku-option
const r = await c.evalJS(`(()=>{const boxes=[...document.querySelectorAll('.sku-info-box')].filter(e=>e.offsetParent!==null);
const done=[];
boxes.forEach((b,i)=>{ const opts=[...b.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null);
if(!opts.length){done.push(i+':no-opt');return;}
const inp=opts[0].querySelector('input[type=checkbox]');
if(inp&&inp.checked){done.push(i+':already');return;}
const lab=opts[0].querySelector('label')||opts[0];
for(const ev of ['mousedown','mouseup','click']) lab.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
if(inp&&!inp.checked) inp.click();
done.push(i+':'+(opts[0].innerText||'').trim().slice(0,18)); });
return done.join(' | ');})()`);
console.log('选规格值:', String(r));
await wait(7000);
const st = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
return JSON.stringify({attrs:(a.formState.skuAttrs||[]).map(x=>({n:x.name, chosen:(x.options||[]).length})),
skuRows:(d.formState.skuDataList||[]).length});})()`);
console.log('状态:', String(st));
// 保存
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(12000);
const cap = await c.evalJS(`JSON.stringify(window.__cap||[])`);
console.log('捕获:', String(cap).slice(0,300));
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,3)));
const t = await c.evalJS(`JSON.stringify([...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,130)).filter(Boolean).slice(0,3))`);
console.log('提示:', String(t).slice(0,400));
process.exit(0);
+60
View File
@@ -0,0 +1,60 @@
import { newTarget, CDP } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const SHOP='③shein-易捷天宇-美妆', CATKW='发夹';
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
for (const x of tl.filter(y=>y.type==='page'&&/shein/i.test(y.url))) await fetch(`http://127.0.0.1:9223/json/close/${x.id}`).catch(()=>{});
await wait(900);
const nt = await newTarget('about:blank');
const c = await CDP.connect(nt.webSocketDebuggerUrl);
await c.send('Page.enable'); await c.send('Runtime.enable');
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/sheinProduct/add'});
await wait(21000);
// 店铺
const sp=JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/店铺/.test(l.innerText||'');});
const s=(it||document).querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'});
const r=s.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:sp[0],y:sp[1],button:'left',clickCount:1});
await wait(2400);
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
if(hit) for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(8000);
// 类目
const cp=JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/产品分类/.test(l.innerText||'');});
const s=it.querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'});
const r=s.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:cp[0],y:cp[1],button:'left',clickCount:1});
await wait(3500);
console.log('类目:', String(await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
if(!dd)return'no-dd'; const its=[...dd.querySelectorAll('.ant-select-item-option')];
const hit=its.find(o=>new RegExp('^\\\\s*'+${JSON.stringify(CATKW)}).test((o.innerText||'').trim()));
if(!hit)return'no-hit'; for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
return (hit.innerText||'').trim();})()`)));
await wait(12000);
// 标题
await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/产品标题/.test(l.innerText||'');});
const el=it.querySelector('input:not([type=hidden])'); el.focus();
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,'Test Hair Clip Set for Women Girls Daily Use Fashion Accessory');
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true}));
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
p._s.get('sheinAddProductStore').$state.formState.productName='Test Hair Clip Set for Women Girls Daily Use Fashion Accessory';
return 1;})()`);
await wait(1500);
// 选主规格颜色(取第一个可选值)—— 先看控件结构
const mg = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/主规格/.test(l.innerText||'');});
if(!it)return'nf';
const sels=[...it.querySelectorAll('.ant-select')].map(s=>({txt:(s.innerText||'').replace(/\\s+/g,' ').slice(0,40), cls:String(s.className).slice(0,50)}));
const cbs=[...it.querySelectorAll('.ant-checkbox-wrapper,.ant-tag,[class*=tag]')].slice(0,6).map(e=>(e.innerText||'').trim().slice(0,16));
return JSON.stringify({sels, cbs, html:it.innerHTML.slice(0,400)});})()`);
console.log('主规格控件:', String(mg).slice(0,800));
// 图片字段测试:数组 vs 字符串
const imgTest = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const im=p._s.get('sheinImgStore').$state.formState;
const before=JSON.stringify(im).slice(0,120);
return JSON.stringify({before});})()`);
console.log('图片字段现状:', String(imgTest).slice(0,300));
process.exit(0);
+50
View File
@@ -0,0 +1,50 @@
import { newTarget, CDP } from '/tmp/cdp3.mjs';
import fs from 'fs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const SHOP = process.argv[2] || '①Shein-武汉九个网-箱包';
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
for (const t of tl.filter(x=>x.type==='page'&&/shein/i.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
await wait(1200);
const t = await newTarget('about:blank');
const c = await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Page.enable'); await c.send('Runtime.enable');
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/sheinProduct/add'});
await wait(22000);
// 选店铺
const pos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/店铺/.test(l.innerText||'');});
const s=(it||document).querySelector('.ant-select-selector');
if(!s)return'[]'; s.scrollIntoView({block:'center'}); const r=s.getBoundingClientRect();
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[1],button:'left',clickCount:1});
await wait(2500);
const pk = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
if(!dd)return'no-dd'; const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
if(!hit)return'no-hit'; for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'ok';})()`);
console.log('选店铺:', pk, SHOP);
await wait(9000);
const o = await c.evalJS(`(()=>{
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const stores=[...p._s.keys()].filter(k=>/shein/i.test(k));
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null).map(x=>{
const l=x.querySelector('label'); const lab=l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';
const req=!!x.querySelector('.ant-form-item-required');
const inp=x.querySelector('input:not([type=hidden])'); const ta=x.querySelector('textarea'); const sel=x.querySelector('.ant-select-selection-item');
return (req?'* ':' ')+lab.slice(0,26).padEnd(27)+'['+(ta?'ta':(sel?'select':(inp?'input':'-')))+'] '+(sel?sel.innerText.trim():(inp?inp.value:(ta?ta.value:''))).slice(0,26);
}).filter(x=>x.trim().length>4);
const dump={};
for(const n of stores){ try{ const s=p._s.get(n).$state; const o2={};
for(const k of Object.keys(s)){ const v=s[k];
if(v===null||['string','number','boolean'].includes(typeof v)){ if(v!==''&&v!==null&&String(v).length<55) o2[k]=String(v); }
else if(v&&typeof v==='object'&&!Array.isArray(v)){ for(const k2 of Object.keys(v)){ const v2=v[k2];
if(v2===null||['string','number','boolean'].includes(typeof v2)){ if(v2!==''&&v2!==null&&String(v2).length<45) o2[k+'.'+k2]=String(v2); } } } }
dump[n]=o2; }catch(e){ dump[n]='ERR'; } }
return JSON.stringify({stores, items, dump},null,1);})()`);
fs.writeFileSync('/tmp/shein_map.json', String(o));
const j=JSON.parse(String(o));
console.log('stores:', j.stores.join(', '));
console.log('--- form ---'); j.items.forEach(x=>console.log(' '+x));
console.log('--- store 关键字段 ---');
for(const [n,v] of Object.entries(j.dump)){ const hit=Object.entries(v||{}).filter(([k])=>/shop|site|currency|cat|brand|price|weight/i.test(k));
if(hit.length) console.log(' ##',n,hit.map(([k,val])=>k+'='+val).join(' | ').slice(0,320)); }
process.exit(0);
+27
View File
@@ -0,0 +1,27 @@
import { newTarget, CDP, SAN } from '/tmp/cdp3.mjs';
const t=await newTarget('about:blank');
const c=await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Page.enable'); await c.send('Runtime.enable'); await c.send('Network.enable');
const caps=[];
c.on(m=>{ if(m.method==='Network.requestWillBeSent' && /shop/i.test(m.params.request.url))
caps.push({m:m.params.request.method,u:m.params.request.url.replace('https://www.dianxiaomi.com',''),p:String(m.params.request.postData||'').slice(0,150)}); });
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/ozonProduct/draft'});
await new Promise(r=>setTimeout(r,24000));
for(let i=0;i<8;i++){
const x=await c.evalJS(`(()=>{const v=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
if(!v.length)return'n'; const b=v[0].querySelector('.ant-modal-close'); if(b){b.click();return'c';} return's';})()`);
if(x==='n'){await new Promise(z=>setTimeout(z,900));continue;} if(x==='s')break;
await new Promise(z=>setTimeout(z,1200));
}
console.log(' ══ 含 shop 的接口 ══');
[...new Set(caps.map(x=>x.m+' '+x.u))].slice(0,10).forEach(x=>console.log(' '+x));
// 直接调最可能的那个拿映射
const r=await c.evalJS(`(async()=>{
const u=( ${JSON.stringify(caps.map(x=>x.u).filter(x=>/shop/i.test(x)))} )[0];
if(!u) return 'no-url';
const res=await fetch(u,{headers:{'X-Requested-With':'XMLHttpRequest'}});
const tx=await res.text();
return u+' -> '+tx.slice(0,900);})()`, true);
console.log('\n ══ 店铺列表内容 ══');
console.log(' '+SAN(String(r)).slice(0,900));
c.close();
+45
View File
@@ -0,0 +1,45 @@
import { newTarget, CDP } from '/tmp/cdp3.mjs';
import fs from 'fs';
const URLX = process.argv[2];
const TAG = process.argv[3] || 'x';
const wait = ms => new Promise(z => setTimeout(z, ms));
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
for (const t of tl.filter(x => x.type==='page' && x.url.includes(TAG))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
await wait(1000);
const t = await newTarget('about:blank');
const c = await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Page.enable'); await c.send('Runtime.enable');
await c.send('Page.navigate',{url:URLX});
await wait(22000);
console.log('URL:', await c.evalJS('location.href'));
const loc = await c.evalJS(`(()=>{
const items=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null);
for(let i=0;i<items.length;i++){ const l=items[i].querySelector('label'); if(l&&/店铺/.test(l.innerText||'')){
const s=items[i].querySelector('.ant-select'); if(s){ const r=s.getBoundingClientRect(); return JSON.stringify({i, label:l.innerText.trim(), x:Math.round(r.x+r.width/2), y:Math.round(r.y+r.height/2), cur:(s.querySelector('.ant-select-selection-item')||{}).innerText||''}); } } }
const s=[...document.querySelectorAll('.ant-select')].filter(e=>e.offsetParent!==null)[0];
if(!s) return 'no-select'; const r=s.getBoundingClientRect();
return JSON.stringify({i:-1, label:'(第一个)', x:Math.round(r.x+r.width/2), y:Math.round(r.y+r.height/2), cur:''});})()`);
console.log('店铺选择器:', loc);
const L = JSON.parse(String(loc));
if (L.x) {
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:L.x,y:L.y,button:'left',clickCount:1});
await wait(2500);
const all = new Set();
for (let i=0;i<20;i++){
const got = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null);
if(!dd.length)return'[]'; const d=dd[dd.length-1];
return JSON.stringify([...d.querySelectorAll('.ant-select-item-option')].map(e=>(e.innerText||'').trim()).filter(Boolean));})()`);
const arr=JSON.parse(String(got)); const before=all.size; arr.forEach(x=>all.add(x));
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null);
if(!dd.length)return 0; const d=dd[dd.length-1];
const rc=d.querySelector('.rc-virtual-list-holder')||d; rc.scrollTop=rc.scrollTop+800; return rc.scrollTop;})()`);
await wait(700);
if (all.size===before && i>2) break;
}
const list=[...all];
fs.writeFileSync('/tmp/shops_'+TAG+'.json', JSON.stringify(list,null,1));
console.log('店铺数:', list.length);
list.sort((a,b)=>a.localeCompare(b));
list.forEach((n,i)=>console.log(' '+String(i+1).padStart(2)+'. '+n));
}
process.exit(0);
+32
View File
@@ -0,0 +1,32 @@
import { CDP, SAN } from '/tmp/cdp3.mjs';
const list=await (await fetch('http://127.0.0.1:9223/json/list')).json();
const t=list.find(x=>x.type==='page'&&/dianxiaomi/.test(x.url));
if(!t){console.log(' ❌');process.exit(0);}
const c=await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Runtime.enable');
const r=await c.evalJS(`(async()=>{
const out=[];
const cands=[['/api/ozonProduct/getShop.json','GET'],['/api/shop/getShopList.json','GET'],
['/api/ozonProduct/shopList.json','GET'],['/api/ozonProduct/getAuthShopList.json','GET'],
['/api/shop/list.json','GET'],['/api/ozonProduct/getShopList.json','POST']];
for(const [u,m] of cands){
try{
const res=await Promise.race([
fetch(u, m==='POST'?{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded','X-Requested-With':'XMLHttpRequest'},body:''}
:{headers:{'X-Requested-With':'XMLHttpRequest'}}),
new Promise((_,rj)=>setTimeout(()=>rj(new Error('to')),10000))]);
const tx=await res.text();
if(res.status===200 && !tx.includes('<!DOCTYPE')) out.push(u+' -> '+tx.slice(0,420));
}catch(e){}
}
return JSON.stringify(out);})()`, true);
const arr=JSON.parse(r);
if(!arr.length) console.log(' 店铺接口都没找到');
arr.forEach(x=>console.log(' ★ '+SAN(x).slice(0,420)));
// 直接在页面上找店铺下拉里的映射
const dom=await c.evalJS(`(()=>{
const txt=document.body.innerText;
const i=txt.indexOf('Yejet');
return JSON.stringify({has:txt.includes('Yejet'), around:i>=0?txt.slice(Math.max(0,i-60),i+60).replace(/\\n/g,' | '):''});})()`);
console.log('\n 页面含 Yejet:', SAN(dom).slice(0,220));
c.close();
+21
View File
@@ -0,0 +1,21 @@
import { CDP, SAN } from '/tmp/cdp3.mjs';
const list=await (await fetch('http://127.0.0.1:9223/json/list')).json();
const t=list.find(x=>x.type==='page'&&/dianxiaomi/.test(x.url));
if(!t){console.log(' ❌');process.exit(0);}
const c=await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Runtime.enable');
const r=await c.evalJS(`(async()=>{
const out={};
// userIn 里通常带店铺
try{ const res=await fetch('/api/userIn.json',{headers:{'X-Requested-With':'XMLHttpRequest'}});
const tx=await res.text(); out.userIn=tx.slice(0,1200); }catch(e){ out.userIn='ERR'; }
// 页面上是否嵌了 shop 映射
out.htmlHas4248777 = document.documentElement.innerHTML.includes('4248777');
const m=document.documentElement.innerHTML.match(/.{0,80}4248777.{0,120}/g);
out.ctx = m? m.slice(0,3).map(x=>x.replace(/\\s+/g,' ')) : [];
return JSON.stringify(out);})()`, true);
const d=JSON.parse(r);
console.log(' userIn 片段:', SAN(String(d.userIn)).slice(0,600));
console.log('\n HTML 含 4248777:', d.htmlHas4248777);
(d.ctx||[]).forEach(x=>console.log(' 上下文: '+SAN(String(x)).slice(0,190)));
c.close();
+18
View File
@@ -0,0 +1,18 @@
import { CDP, SAN } from '/tmp/cdp3.mjs';
const list=await (await fetch('http://127.0.0.1:9223/json/list')).json();
const t=list.find(x=>x.type==='page'&&/dianxiaomi/.test(x.url));
if(!t){console.log(' ❌');process.exit(0);}
const c=await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Runtime.enable');
const r=await c.evalJS(`(async()=>{
const j=await (await fetch('/api/userIn.json',{headers:{'X-Requested-With':'XMLHttpRequest'}})).json();
const sm=(j.data||{}).shopMap||{};
const rows=Object.keys(sm).map(k=>({id:k, name:String(sm[k].name||''), plat:String(sm[k].platform||sm[k].site||'')}));
const target=rows.find(x=>x.id==='4248777');
return JSON.stringify({n:rows.length, target, yejet:rows.filter(x=>/yejet/i.test(x.name)).slice(0,8)});})()`, true);
const d=JSON.parse(r);
console.log(' shopMap 共', d.n, '个店铺');
console.log(' ══ shopId=4248777 是 ══');
console.log(' ', SAN(JSON.stringify(d.target)).slice(0,220));
console.log('\n 名称含 yejet 的店铺:', SAN(JSON.stringify(d.yejet)).slice(0,300));
c.close();
+40
View File
@@ -0,0 +1,40 @@
import { attach, SAN } from '/tmp/cdp3.mjs';
const c = await attach('/web/ozonProduct/draft') || await attach('dianxiaomi.com');
if(!c){ console.log('✗ 无页面'); process.exit(1); }
await c.send('Runtime.enable');
const r = await c.evalJS(`(async()=>{
const rr = await fetch('/api/userInfo.json?_t='+Date.now(), {headers:{'X-Requested-With':'XMLHttpRequest'}});
const tx = await rr.text();
if(!tx.startsWith('{')) return JSON.stringify({err:'notjson', head:tx.slice(0,120)});
const j = JSON.parse(tx); const d = j.data||{};
const out = { topKeys: Object.keys(d) };
// shopMap 结构
const sm = d.shopMap || d.shops || {};
out.shopMapType = Array.isArray(sm)?'array':typeof sm;
if (Array.isArray(sm)) {
out.shops = sm.map(s=>({id:s.id, idStr:s.idStr, name:s.name||s.shopName, platform:s.platform||s.platformName, site:s.site}));
} else {
out.shopMapKeys = Object.keys(sm).slice(0,40);
out.shops = [];
for (const [k,v] of Object.entries(sm)) {
if (Array.isArray(v)) v.forEach(s=>out.shops.push({platform:k, id:s.id, idStr:s.idStr, name:s.name||s.shopName, site:s.site}));
else if (v && typeof v === 'object') out.shops.push({platform:k, id:v.id, idStr:v.idStr, name:v.name||v.shopName});
}
}
// 平台列表
out.platformList = d.platformList || d.platforms || d.shopPlatformList || null;
return JSON.stringify(out);
})()`, true);
try{
const d = JSON.parse(String(r));
if (d.err) { console.log(' ✗', d.err, d.head); process.exit(0); }
console.log(' userInfo 顶层字段:', (d.topKeys||[]).join(', ').slice(0,300));
console.log(' shopMap 类型:', d.shopMapType);
if (d.shopMapKeys) console.log(' shopMap 键(平台):', d.shopMapKeys.join(', '));
console.log();
console.log(' ── 全部店铺 ──');
(d.shops||[]).forEach(s=>console.log(' ', String(s.platform).padEnd(16), 'id='+String(s.id||s.idStr).padEnd(12), s.name||''));
if (d.platformList) console.log();
if (d.platformList) console.log(' platformList:', SAN(JSON.stringify(d.platformList)).slice(0,400));
}catch(e){ console.log('err', e.message, SAN(String(r)).slice(0,600)); }
c.close();
+43
View File
@@ -0,0 +1,43 @@
import { attach, SAN } from '/tmp/cdp3.mjs';
const c = await attach('/web/ozonProduct/draft') || await attach('dianxiaomi.com');
await c.send('Runtime.enable');
const r = await c.evalJS(`(async()=>{
const rr = await fetch('/api/userInfo.json?_t='+Date.now(), {headers:{'X-Requested-With':'XMLHttpRequest'}});
const d = JSON.parse(await rr.text()).data;
const out = {};
// shopMap 一个条目的完整结构
const sm = d.shopMap||{};
const firstKey = Object.keys(sm)[0];
out.shopMapSample = JSON.stringify(sm[firstKey]).slice(0, 400);
// shopSmtTypeMap
out.smtTypeMap = JSON.stringify(d.shopSmtTypeMap||{}).slice(0, 400);
out.smtShopId = String(d.smtShopId);
out.shopId = String(d.shopId);
// platformPermission
out.platformPermission = JSON.stringify(d.platformPermission||{}).slice(0, 500);
// shopDataMap
const sdm = d.shopDataMap||{};
const k2 = Object.keys(sdm)[0];
out.shopDataMapSample = k2? (k2+' => '+JSON.stringify(sdm[k2]).slice(0,300)) : '(空)';
// 按 platform 归类
const byPlat = {};
for (const [k,v] of Object.entries(sm)) {
const p = (v&&(v.platform||v.platformName||v.type))||'?';
(byPlat[p]=byPlat[p]||[]).push((v&&(v.name||v.shopName))||k);
}
out.byPlatform = byPlat;
return JSON.stringify(out);
})()`, true);
try{
const d = JSON.parse(String(r));
console.log(' smtShopId:', d.smtShopId, '| shopId:', d.shopId);
console.log();
console.log(' shopMap 条目结构:'); console.log(' ', SAN(d.shopMapSample).slice(0,380));
console.log(' shopDataMap 样例:'); console.log(' ', SAN(d.shopDataMapSample).slice(0,300));
console.log(' shopSmtTypeMap:'); console.log(' ', SAN(d.smtTypeMap).slice(0,380));
console.log(' platformPermission:'); console.log(' ', SAN(d.platformPermission).slice(0,480));
console.log();
console.log(' ── 按平台归类 ──');
for (const [p, arr] of Object.entries(d.byPlatform||{})) console.log(' ' + p + ' (' + arr.length + '): ' + arr.slice(0,8).join(', '));
}catch(e){ console.log('err', e.message, SAN(String(r)).slice(0,700)); }
c.close();
+13
View File
@@ -0,0 +1,13 @@
[
"⑧Shein-易世通达-文具",
"⑦shein-易捷天宇-家居",
"⑩Shein-南京九个网-美妆",
"⑨Shein-南京亿千-运动",
"③shein-易捷天宇-美妆",
"⑤Shein-惠州九衢-内睡(袜子)",
"④Shein-武汉玖璨-家居",
"⑥Shein-南京亿千-文具",
"①Shein-武汉九个网-箱包",
"②Shein-易世通达-箱包",
"⑪-南京九个网-家居"
]
+17
View File
@@ -0,0 +1,17 @@
[
"①Alimingpro",
"②ALuckycao",
"③Bagbelle",
"④Caoup",
"⑤Bookinq",
"⑥Wxzgz",
"⑦Zdcjl",
"⑧Ewhe",
"⑨wowimartq",
"⑩JoyJumbe",
"⑪ Chugupi",
"⑫Qapple",
"⑬Wbalana",
"⑮Tapeach",
"⑭HoHaven"
]
+12
View File
@@ -0,0 +1,12 @@
import { attach } from '/tmp/cdp3.mjs';
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const o = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/主规格/.test(l.innerText||'');});
if(!it)return'nf'; const box=it.querySelector('.sku-info-box')||it;
const first=[...box.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null)[0];
if(!first)return'no-opt';
return JSON.stringify({outer:first.outerHTML.slice(0,600),
parentCls:String(first.parentElement.className||'').slice(0,60),
sibCount:first.parentElement.children.length});})()`);
console.log(String(o).slice(0,900));
process.exit(0);
+31
View File
@@ -0,0 +1,31 @@
import { attach, SAN } from '/tmp/cdp3.mjs';
import fs from 'fs';
const all=JSON.parse(fs.readFileSync('/tmp/all48.json','utf8'));
const wm=JSON.parse(fs.readFileSync('/tmp/wm_verdicts.json','utf8'));
const clean=JSON.parse(fs.readFileSync('/tmp/wm_clean.json','utf8'));
const SHORT=['1027332215847','601964534137','750271307455'];
const c=await attach('/web/smt/smtProductList/draft')||await attach('dianxiaomi.com');
await c.send('Runtime.enable');
for(const oid of SHORT){
const info=all[oid];
const raw=await c.evalJS(`(async()=>{
const rr=await fetch('/api/ozonProduct/add.json?id=${info.pid}&_t='+Date.now(),{headers:{'X-Requested-With':'XMLHttpRequest'}});
const tx=await rr.text(); if(!tx.startsWith('{'))return'{}';
const p=JSON.parse(tx).data.product;
const s=new Set(); if(p.mainImage) s.add(p.mainImage);
(p.extraImages||'').split(';').filter(Boolean).forEach(u=>s.add(u));
return JSON.stringify({name:(p.name||'').slice(0,40), main:p.mainImage, urls:[...s]});
})()`,true);
const d=JSON.parse(String(raw));
console.log('\n ════ '+oid+' ════');
console.log(' '+d.name);
console.log(' 干净图: '+(clean[oid]||[]).length+' 张');
console.log(' ── 全部候选图的判定 ──');
(d.urls||[]).slice(0,18).forEach((u,i)=>{
const v=wm[u];
const tag = !v ? '?' : (v.err? '✗'+v.err : (v.wm||v.contact? '⚠水印':'✅干净'));
const txt = v && (v.text||v.reason) ? (' | '+String(v.text||v.reason).slice(0,50)) : '';
console.log(` [${i}] ${tag.padEnd(8)} ${u.split('/').pop().split('_')[0].slice(0,26)}${txt}`);
});
}
c.close();
+24
View File
@@ -0,0 +1,24 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const pick = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/主规格/.test(l.innerText||'');});
if(!it)return'nf'; const box=it.querySelector('.sku-info-box')||it;
const opts=[...box.querySelectorAll('.sku-option')].filter(e=>e.offsetParent!==null);
const hit=opts.find(e=>/Multicolor|Black\\(/.test(e.innerText||''))||opts[0];
if(!hit)return'no-opt';
for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));
return 'picked:'+(hit.innerText||'').trim();})()`);
console.log('选色:', pick);
await wait(6000);
const o = await c.evalJS(`(()=>{
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const tbls=[...document.querySelectorAll('table')].filter(t=>t.offsetParent!==null).map(t=>({
th:[...t.querySelectorAll('th')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).slice(0,15),
rows:[...t.querySelectorAll('tbody tr')].filter(x=>x.querySelectorAll('input').length).length}));
const d=JSON.parse(JSON.stringify(p._s.get('sheinSkuDataStore').$state));
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
return JSON.stringify({tbls, n:(d.formState.skuDataList||[]).length, row0:(d.formState.skuDataList||[])[0],
chosen:(a.formState.skuAttrs||[]).map(x=>({name:x.name, vals:(x.attributeValues||[]).map(v=>v.attribute_value||v) }))},null,1);})()`);
console.log(String(o).slice(0,1900));
process.exit(0);
+53
View File
@@ -0,0 +1,53 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const reqs=[];
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
await c.send('Network.enable').catch(()=>{});
// 重填 SKU 行
const r = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const d=p._s.get('sheinSkuDataStore').$state.formState;
const rows=d.skuDataList||[]; let n=0;
rows.forEach((x,i)=>{ x.skcSupplierCode='SHEIN-'+String(i+1).padStart(3,'0'); x.skcRetailPrice='12.90';
x.supplyPrice='8.50'; x.stockVariety='999'; x.weight='100'; x.length='200'; x.width='150'; x.height='30'; n++; });
return 'rows:'+n;})()`);
console.log('重填 SKU:', r);
await wait(2500);
// 重填尺码表(走弹窗)
await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/尺码表/.test(l.innerText||'');});
if(!it)return 0; const el=it.querySelector('.link')||it.querySelector('a,span'); if(!el)return 0;
el.scrollIntoView({block:'center'});
for(const ev of ['mousedown','mouseup','click']) el.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(5000);
const f = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
if(!m)return'no-modal'; const ins=[...m.querySelectorAll('input')].filter(i=>i.offsetParent!==null&&i.placeholder==='请输入');
const vals=['10','18','3','2','18','5']; let n=0;
ins.forEach((el,i)=>{ if(i>=vals.length)return; el.focus();
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el,vals[i]);
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); el.dispatchEvent(new Event('blur',{bubbles:true})); n++; });
return 'filled:'+n;})()`);
console.log('尺码表:', f);
await wait(1200);
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
if(m){const b=[...m.querySelectorAll('button')].find(x=>/^\\s*确定\\s*$/.test(x.innerText||'')); if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window}));} return 1;})()`);
await wait(4000);
// 重填图片(可能也被清了)
await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const im=p._s.get('sheinImgStore').$state.formState;
const cur=im.mainImgData.multipleSpuImgs;
const urls=${JSON.stringify(['https://cbu01.alicdn.com/img/ibank/O1CN01IHtNAHKMzNC1chua_!!2222444614399-0-cib.jpg','https://cbu01.alicdn.com/img/ibank/O1CN01UGEWafTzqTH1chua_!!2222444614399-0-cib.jpg','https://cbu01.alicdn.com/img/ibank/O1CN01d0hA4N72jzD1chua_!!2222444614399-0-cib.jpg','https://cbu01.alicdn.com/img/ibank/O1CN01edgGxIlmdtJ1chua_!!2222444614399-0-cib.jpg'])};
if(!Array.isArray(cur)||cur.length<4){ im.mainImgData.multipleSpuImgs=urls.map((u,i)=>({url:u,imgUrl:u,name:'img'+(i+1)+'.jpg',width:800,height:800})); }
return 'imgs:'+(Array.isArray(im.mainImgData.multipleSpuImgs)?im.mainImgData.multipleSpuImgs.length:'?');})()`);
await wait(2500);
// 保存
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
if(x) for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(12000);
console.log('捕获:', String(await c.evalJS(`JSON.stringify(window.__cap||[])`)).slice(0,300));
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,3)));
const t = await c.evalJS(`JSON.stringify([...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,130)).filter(Boolean).slice(0,3))`);
console.log('提示:', String(t).slice(0,400));
process.exit(0);
+35
View File
@@ -0,0 +1,35 @@
import { newTarget, CDP } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const SHOP = process.argv[2];
const tl = await (await fetch('http://127.0.0.1:9223/json/list')).json();
for (const t of tl.filter(x=>x.type==='page'&&/shein/i.test(x.url))) await fetch(`http://127.0.0.1:9223/json/close/${t.id}`).catch(()=>{});
await wait(1000);
const t = await newTarget('about:blank');
const c = await CDP.connect(t.webSocketDebuggerUrl);
await c.send('Page.enable'); await c.send('Runtime.enable');
await c.send('Page.navigate',{url:'https://www.dianxiaomi.com/web/sheinProduct/add'});
await wait(21000);
const pos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/店铺/.test(l.innerText||'');});
const s=(it||document).querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'});
const r=s.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[1],button:'left',clickCount:1});
await wait(2500);
await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
const hit=[...dd.querySelectorAll('.ant-select-item-option')].find(o=>(o.innerText||'').trim()===${JSON.stringify(SHOP)});
if(hit) for(const ev of ['mousedown','mouseup','click']) hit.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(8000);
// 开分类下拉
const cp = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/产品分类/.test(l.innerText||'');});
const s=it.querySelector('.ant-select-selector'); s.scrollIntoView({block:'center'});
const r=s.getBoundingClientRect(); return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
for (const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:cp[0],y:cp[1],button:'left',clickCount:1});
await wait(3500);
const o = await c.evalJS(`(()=>{const dd=[...document.querySelectorAll('.ant-select-dropdown')].filter(e=>e.offsetParent!==null).pop();
if(!dd)return'no-dd';
const opts=[...dd.querySelectorAll('.ant-select-item-option')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim());
return JSON.stringify({n:opts.length, opts:opts.slice(0,26)});})()`);
console.log('════', SHOP);
console.log(String(o).slice(0,1100));
process.exit(0);
+21
View File
@@ -0,0 +1,21 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const pos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/尺码表/.test(l.innerText||'');});
if(!it)return'[]'; const el=it.querySelector('.link')||it.querySelector('a,span');
if(!el)return'[]'; el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
console.log('编辑尺码表坐标:', JSON.stringify(pos));
if(pos.length){ for(const type of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type,x:pos[0],y:pos[1],button:'left',clickCount:1}); }
await wait(6000);
const o = await c.evalJS(`(()=>{
const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).map(m=>({t:(m.querySelector('.ant-modal-title')||{}).innerText||'',
inputs:[...m.querySelectorAll('input')].map(i=>i.placeholder||i.type).slice(0,8),
btns:[...m.querySelectorAll('button')].map(b=>(b.innerText||'').trim()).slice(0,8),
txt:(m.innerText||'').replace(/\\s+/g,' ').slice(0,320)}));
const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
let sz=null; try{ sz=JSON.stringify(p._s.get('sheinSkuAttrStore').$state.formState.sizeTableInfo).slice(0,200);}catch(e){}
return JSON.stringify({modals:ms, sizeTableInfo:sz},null,1);})()`);
console.log(String(o).slice(0,1400));
process.exit(0);
+18
View File
@@ -0,0 +1,18 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
// 打开尺码表弹窗
const pos = JSON.parse(String(await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/尺码表/.test(l.innerText||'');});
if(!it)return'[]'; const el=it.querySelector('.link')||it.querySelector('a,span'); if(!el)return'[]';
el.scrollIntoView({block:'center'}); const r=el.getBoundingClientRect();
return JSON.stringify([Math.round(r.x+r.width/2),Math.round(r.y+r.height/2)]);})()`)));
if(pos.length){ for(const t of ['mousePressed','mouseReleased']) await c.send('Input.dispatchMouseEvent',{type:t,x:pos[0],y:pos[1],button:'left',clickCount:1}); }
await wait(6000);
const o = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
if(!m)return'no-modal';
const rows=[...m.querySelectorAll('tr')].slice(0,10).map(tr=>[...tr.querySelectorAll('td,th')].map(td=>{
const i=td.querySelector('input'); return i?('IN:'+(i.placeholder||i.value||'')):(td.innerText||'').replace(/\\s+/g,' ').trim().slice(0,18);}));
return JSON.stringify({title:(m.querySelector('.ant-modal-title')||{}).innerText||'', rows, btns:[...m.querySelectorAll('button')].map(b=>(b.innerText||'').trim()).slice(0,10)},null,1);})()`);
console.log(String(o).slice(0,1500));
process.exit(0);
+47
View File
@@ -0,0 +1,47 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const reqs=[];
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
await c.send('Network.enable').catch(()=>{});
// 填 6 格
const f = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
if(!m)return'no-modal'; const ins=[...m.querySelectorAll('input')].filter(i=>i.offsetParent!==null);
const vals=['10','18','3','2','18','5']; let n=0;
ins.forEach((el,i)=>{ if(i>=vals.length)return;
el.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el, vals[i]);
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true}));
el.dispatchEvent(new Event('blur',{bubbles:true})); n++; });
return 'filled:'+n+'/'+ins.length;})()`);
console.log('填尺码表:', f);
await wait(1500);
// 确定
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
if(!m)return 0; const b=[...m.querySelectorAll('button')].find(x=>/^\\s*确定\\s*$/.test(x.innerText||''));
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(5000);
const st = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const a=p._s.get('sheinSkuAttrStore').$state.formState;
const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/尺码表/.test(l.innerText||'');});
return JSON.stringify({sizeTableInfo:String(JSON.stringify(a.sizeTableInfo)).slice(0,200), ui:(it?it.innerText:'').replace(/\\s+/g,' ').slice(0,60)});})()`);
console.log('尺码表状态:', String(st).slice(0,300));
// 保存
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
await c.evalJS(`(()=>{const xs=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null);
const x=xs.find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim())); if(!x)return 0;
for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(14000);
await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
for(const m of ms){const b=[...m.querySelectorAll('button')].find(x=>/确定|确认|继续编辑/.test(x.innerText||'')); if(b)b.click();} return 1;})()`);
await wait(4000);
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
const red = await c.evalJS(`(()=>{const red=[]; document.querySelectorAll('*').forEach(el=>{ if(el.offsetParent===null||el.children.length)return;
const t=(el.innerText||'').trim(); if(!t||t.length>60)return;
const s=getComputedStyle(el); const m=(s.color||'').match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); if(!m)return;
if(+m[1]>160&&+m[2]<140&&+m[3]<140) red.push(t); });
const toasts=[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim()).filter(Boolean);
return JSON.stringify({red:[...new Set(red)].slice(0,10), toasts:[...new Set(toasts)].slice(0,3)});})()`);
console.log('红字/提示:', String(red).slice(0,600));
process.exit(0);
+38
View File
@@ -0,0 +1,38 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const reqs=[];
c.on(m=>{ if(m.method==='Network.requestWillBeSent'){const r=m.params.request;
if(/shein/i.test(r.url)&&/\.json/.test(r.url)&&r.method==='POST') reqs.push(r.url.replace('https://www.dianxiaomi.com','')); }});
await c.send('Network.enable').catch(()=>{});
const f = await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
if(!m)return'no-modal';
const ins=[...m.querySelectorAll('input')].filter(i=>i.offsetParent!==null&&i.placeholder==='请输入');
const vals=['10','18','3','2','18','5']; let n=0;
ins.forEach((el,i)=>{ if(i>=vals.length)return;
el.focus(); Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set.call(el, vals[i]);
el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true}));
el.dispatchEvent(new Event('blur',{bubbles:true})); n++; });
return 'filled:'+n+'/'+ins.length+' vals='+ins.map(i=>i.value).join(',');})()`);
console.log('填:', f);
await wait(1500);
await c.evalJS(`(()=>{const m=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null).pop();
if(!m)return 0; const b=[...m.querySelectorAll('button')].find(x=>/^\\s*确定\\s*$/.test(x.innerText||''));
if(b) for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(5000);
const st = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const a=p._s.get('sheinSkuAttrStore').$state.formState;
const openM=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null&&/编辑尺码表/.test(e.innerText||'')).length;
return JSON.stringify({sizeTableInfo:String(JSON.stringify(a.sizeTableInfo)).slice(0,240), modalOpen:openM});})()`);
console.log('尺码表:', String(st).slice(0,340));
await c.evalJS(`window.scrollTo(0,0)`); await wait(600);
await c.evalJS(`(()=>{const x=[...document.querySelectorAll('button')].filter(e=>e.offsetParent!==null).find(e=>/^\\s*保存\\s*$/.test((e.innerText||'').trim()));
if(!x)return 0; for(const ev of ['mousedown','mouseup','click']) x.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 1;})()`);
await wait(14000);
await c.evalJS(`(()=>{const ms=[...document.querySelectorAll('.ant-modal')].filter(e=>e.offsetParent!==null);
for(const m of ms){const b=[...m.querySelectorAll('button')].find(x=>/确定|确认|继续编辑/.test(x.innerText||'')); if(b)b.click();} return 1;})()`);
await wait(4000);
console.log('POST:', JSON.stringify([...new Set(reqs)].slice(0,4)));
const t = await c.evalJS(`(()=>[...document.querySelectorAll('.ant-message-notice,.ant-notification-notice,.ant-modal')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').replace(/\\s+/g,' ').trim().slice(0,120)).filter(Boolean).slice(0,3))()`);
console.log('提示:', String(t).slice(0,400));
process.exit(0);
+15
View File
@@ -0,0 +1,15 @@
import { attach } from '/tmp/cdp3.mjs';
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
const o = await c.evalJS(`(()=>{
const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/主规格/.test(l.innerText||'');});
if(!it)return'nf';
const box=it.querySelector('.sku-info-box')||it;
// 列出所有叶子的 tag/class/文本
const leaves=[...box.querySelectorAll('*')].filter(e=>e.offsetParent!==null&&!e.children.length&&(e.innerText||'').trim())
.slice(0,14).map(e=>({tag:e.tagName, cls:String(e.className||'').slice(0,55), t:(e.innerText||'').trim().slice(0,18)}));
const clickable=[...box.querySelectorAll('button,.ant-btn,.ant-tag,.ant-checkbox-wrapper,[class*=tag],[class*=item],[class*=option]')]
.filter(e=>e.offsetParent!==null).slice(0,12).map(e=>({tag:e.tagName, cls:String(e.className||'').slice(0,50), t:(e.innerText||'').trim().slice(0,16)}));
return JSON.stringify({leafCount:box.querySelectorAll('*').length, leaves, clickable},null,1);})()`);
console.log(String(o).slice(0,2000));
process.exit(0);
+30
View File
@@ -0,0 +1,30 @@
import { attach } from '/tmp/cdp3.mjs';
const wait = ms => new Promise(z => setTimeout(z, ms));
const c = await attach('/web/sheinProduct/add') || await attach('dianxiaomi.com');
// 1. 主规格控件
const o1 = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/主规格/.test(l.innerText||'');});
if(!it)return'nf'; return JSON.stringify({txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,160), html:it.innerHTML.slice(0,500)});})()`);
console.log('主规格:', String(o1).slice(0,700));
// 2. 尺码表控件
const o2 = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/尺码表/.test(l.innerText||'');});
if(!it)return'nf'; return JSON.stringify({txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,160), html:it.innerHTML.slice(0,400)});})()`);
console.log('尺码表:', String(o2).slice(0,600));
// 3. 图片区
const o3 = await c.evalJS(`(()=>{const it=[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null)
.find(x=>{const l=x.querySelector('label'); return l&&/图片/.test(l.innerText||'');});
if(!it)return'nf'; return JSON.stringify({lab:(it.querySelector('label')||{}).innerText||'', txt:(it.innerText||'').replace(/\\s+/g,' ').slice(0,200)});})()`);
console.log('图片区:', String(o3).slice(0,500));
// 4. 主规格可选值(第一个属性)
const o4 = await c.evalJS(`(()=>{const p=document.querySelector('#app').__vue_app__.config.globalProperties.$pinia;
const a=JSON.parse(JSON.stringify(p._s.get('sheinSkuAttrStore').$state));
const at=(a.formState.skuAttrs||[])[0]||{};
return JSON.stringify({attrName:at.name, attrId:at.attributeId, optCount:(at._options||[]).length,
opt0:(at._options||[])[0], chosen:at.attributeValues||at.checked||null});})()`);
console.log('主规格属性:', String(o4).slice(0,600));
// 5. 是否有必填标记的项
const o5 = await c.evalJS(`(()=>[...document.querySelectorAll('.ant-form-item')].filter(e=>e.offsetParent!==null&&e.querySelector('.ant-form-item-required'))
.map(x=>{const l=x.querySelector('label'); return l?(l.innerText||'').replace(/\\s+/g,' ').trim():'';}).filter(Boolean).slice(0,15))()`);
console.log('必填项:', String(o5).slice(0,400));
process.exit(0);