feat: 升级插件至 V1.0、更新全新Logo、调整采集梯度(50/100/200)并支持页面浮窗与弹窗双重进度可视化
This commit is contained in:
+67
-4
@@ -317,7 +317,7 @@ async function run(tabId,manual,collectionMode='standard'){
|
||||
await chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:()=>{window.__TMALL_AI_STOP_REVIEW__=false;window.__TMALL_AI_REVIEW_PROGRESS__={count:0,round:0,stagnant:0,mode:'启动',elapsedMs:0,preview:[]};}});
|
||||
let collectorDone=false,collectorError=null;const startedAt=Date.now();
|
||||
const collectorPromise=chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:collector,args:[manual,collectionMode]}).then(v=>v).catch(e=>{collectorError=e;return null}).finally(()=>{collectorDone=true});
|
||||
while(!collectorDone){try{const rr=await chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:()=>window.__TMALL_AI_REVIEW_PROGRESS__||null});const live=rr?.[0]?.result;if(live)await setStatus(tabId,{state:'collecting_reviews',message:`评论采集中:${live.count||0} 条 · ${live.mode||'处理中'}${live.stagnant?` · ${live.stagnant}轮无新增`:''}`,progress:Math.min(67,18+(Number(live.round||0)*2)),reviewLive:{...live,elapsedMs:Date.now()-startedAt}})}catch(e){}await sleep(600)}
|
||||
while(!collectorDone){try{const rr=await chrome.scripting.executeScript({target:{tabId},world:'MAIN',func:()=>window.__TMALL_AI_REVIEW_PROGRESS__||null});const live=rr?.[0]?.result;if(live){const target=live.reviewCap||(collectionMode==='quick'?50:collectionMode==='full'?200:100);const reviewRatio=Math.min(1,(live.count||0)/target);const displayProg=live.progress||Math.min(75,Math.round(20+reviewRatio*55));await setStatus(tabId,{state:'collecting_reviews',message:`评论采集中:${live.count||0} / ${target} 条 (${Math.round(reviewRatio*100)}%) · ${live.mode||'处理中'}${live.stagnant?` · ${live.stagnant}轮无新增`:''}`,progress:displayProg,reviewLive:{...live,elapsedMs:Date.now()-startedAt,reviewCap:target}})}}catch(e){}await sleep(500)}
|
||||
const collectorResult=await collectorPromise;
|
||||
if(collectorError)throw new Error('页面采集脚本执行失败:'+String(collectorError?.message||collectorError));
|
||||
const raw=collectorResult?.[0]?.result;
|
||||
@@ -376,12 +376,74 @@ async function collector(manual=false,collectionMode='standard'){
|
||||
const bodyText=clean(bodyClone?.innerText||document.body?.innerText||'');
|
||||
const captured=[];
|
||||
const reviews=[]; const questions=[];
|
||||
const reviewCap=collectionMode==='quick'?300:(collectionMode==='full'?1500:800);
|
||||
const reviewCap=collectionMode==='quick'?50:(collectionMode==='full'?200:100);
|
||||
const reviewNetMeta={totals:[],hasMoreFalse:false,lastPageFlags:[],pageHints:[],networkReviewObjects:0,networkResponses:0};
|
||||
const reviewStartedAt=Date.now();
|
||||
const processedCaptureKeys=new Set();
|
||||
function ensureInPageHud(){
|
||||
let hud=document.getElementById('__sansheng_hud__');
|
||||
if(!hud){
|
||||
hud=document.createElement('div');
|
||||
hud.id='__sansheng_hud__';
|
||||
hud.innerHTML=`<style>
|
||||
#__sansheng_hud__{position:fixed!important;top:20px!important;right:20px!important;z-index:2147483647!important;width:320px!important;background:rgba(18,28,33,.94)!important;backdrop-filter:blur(16px)!important;-webkit-backdrop-filter:blur(16px)!important;border:1px solid rgba(255,255,255,.16)!important;border-radius:12px!important;box-shadow:0 16px 36px rgba(0,0,0,.4)!important;color:#fff!important;font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif!important;padding:14px 16px!important;font-size:12px!important;line-height:1.5!important;box-sizing:border-box!important;pointer-events:auto!important;transition:opacity .3s,transform .3s!important}
|
||||
#__sansheng_hud__ *{box-sizing:border-box!important;margin:0!important}
|
||||
.__ss_header{display:flex!important;align-items:center!important;justify-content:space-between!important;margin-bottom:10px!important;padding-bottom:8px!important;border-bottom:1px solid rgba(255,255,255,.1)!important}
|
||||
.__ss_brand{display:flex!important;align-items:center!important;gap:8px!important;font-weight:600!important;font-size:13px!important;color:#fff!important}
|
||||
.__ss_pulse{width:8px!important;height:8px!important;border-radius:50%!important;background:#00a98f!important;box-shadow:0 0 0 3px rgba(0,169,143,.3)!important;animation:__ss_p 1.4s infinite!important}
|
||||
@keyframes __ss_p{50%{opacity:.35!important;transform:scale(.85)!important}}
|
||||
.__ss_stop_btn{background:rgba(255,255,255,.1)!important;border:1px solid rgba(255,255,255,.2)!important;color:#ff7875!important;border-radius:5px!important;font-size:11px!important;padding:3px 8px!important;cursor:pointer!important}
|
||||
.__ss_stop_btn:hover{background:rgba(255,78,78,.25)!important}
|
||||
.__ss_status_text{display:flex!important;justify-content:space-between!important;font-size:12px!important;color:#d1dedc!important;margin-bottom:6px!important}
|
||||
.__ss_status_text b{color:#2dd4bf!important}
|
||||
.__ss_prog_track{height:6px!important;background:rgba(255,255,255,.15)!important;border-radius:3px!important;overflow:hidden!important;margin-bottom:10px!important}
|
||||
.__ss_prog_bar{height:100%!important;width:0%!important;background:linear-gradient(90deg,#00a98f,#1675d1)!important;border-radius:3px!important;transition:width .3s ease!important}
|
||||
.__ss_stats{display:grid!important;grid-template-columns:repeat(3,1fr)!important;gap:6px!important;background:rgba(0,0,0,.25)!important;border-radius:6px!important;padding:6px 8px!important;text-align:center!important;margin-bottom:8px!important}
|
||||
.__ss_stats span{display:block!important;font-size:10px!important;color:#8da4a8!important}
|
||||
.__ss_stats b{font-size:12px!important;color:#fff!important}
|
||||
.__ss_preview{font-size:11px!important;color:#9cb3b6!important;white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;background:rgba(255,255,255,.06)!important;border-radius:4px!important;padding:4px 8px!important}
|
||||
</style>
|
||||
<div class="__ss_header"><div class="__ss_brand"><span class="__ss_pulse" id="__ss_pulse__"></span><span>三笙 · 商品采集 V1.0</span></div><button class="__ss_stop_btn" id="__ss_stop__">终止采集</button></div>
|
||||
<div class="__ss_status_text"><span id="__ss_step_label__">准备开始采集...</span><b id="__ss_pct__">0%</b></div>
|
||||
<div class="__ss_prog_track"><div class="__ss_prog_bar" id="__ss_bar__"></div></div>
|
||||
<div class="__ss_stats"><div><span>有效评价</span><b id="__ss_stat_reviews__">0 / ${reviewCap}</b></div><div><span>翻页轮次</span><b id="__ss_stat_rounds__">第 0 轮</b></div><div><span>已用耗时</span><b id="__ss_stat_time__">0s</b></div></div>
|
||||
<div class="__ss_preview" id="__ss_preview_box__">等待读取商品评价...</div>`;
|
||||
document.body.appendChild(hud);
|
||||
document.getElementById('__ss_stop__')?.addEventListener('click',()=>{window.__TMALL_AI_STOP_REVIEW__=true;const b=document.getElementById('__ss_stop__');if(b)b.textContent='终止中…'});
|
||||
}
|
||||
return hud;
|
||||
}
|
||||
function updateInPageHud(data){
|
||||
try{
|
||||
ensureInPageHud();
|
||||
const count=data.count||0;const cap=reviewCap;const pct=Math.min(100,Math.max(0,data.progress!=null?data.progress:Math.round((count/cap)*100)));
|
||||
const bar=document.getElementById('__ss_bar__');if(bar)bar.style.width=pct+'%';
|
||||
const pctEl=document.getElementById('__ss_pct__');if(pctEl)pctEl.textContent=pct+'%';
|
||||
const labelEl=document.getElementById('__ss_step_label__');if(labelEl)labelEl.textContent=data.message||`评论采集中 (${count}/${cap}条)`;
|
||||
const statReviews=document.getElementById('__ss_stat_reviews__');if(statReviews)statReviews.textContent=`${count} / ${cap}`;
|
||||
const statRounds=document.getElementById('__ss_stat_rounds__');if(statRounds)statRounds.textContent=`第 ${data.round||0} 轮`;
|
||||
const statTime=document.getElementById('__ss_stat_time__');if(statTime)statTime.textContent=Math.round((data.elapsedMs||0)/1000)+'s';
|
||||
const previewBox=document.getElementById('__ss_preview_box__');if(previewBox&&data.preview&&data.preview.length)previewBox.textContent='最新:'+data.preview[0];
|
||||
}catch(e){}
|
||||
}
|
||||
function finishInPageHud(success=true,message=''){
|
||||
try{
|
||||
const hud=document.getElementById('__sansheng_hud__');
|
||||
if(hud){
|
||||
const pulse=document.getElementById('__ss_pulse__');if(pulse){pulse.style.background=success?'#00a98f':'#ff7875';pulse.style.animation='none'}
|
||||
const label=document.getElementById('__ss_step_label__');if(label)label.textContent=message||(success?`✓ 采集完成(共 ${reviews.length} 条评论),正在提交...`:'采集已终止');
|
||||
const bar=document.getElementById('__ss_bar__');if(bar&&success)bar.style.width='100%';
|
||||
const pct=document.getElementById('__ss_pct__');if(pct&&success)pct.textContent='100%';
|
||||
setTimeout(()=>{if(hud&&hud.parentNode){hud.style.opacity='0';hud.style.transform='translateY(-12px)';setTimeout(()=>{if(hud.parentNode)hud.remove()},400)}},3500);
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
function dedupReviewCount(){const m=new Set();for(const r of reviews){const k=clean(r.content||'').replace(/\s+/g,'').slice(0,320);if(k)m.add(k)}return m.size}
|
||||
function publishReviewProgress(round=0,stagnant=0,mode='DOM',extra={}){const seen=new Set(),preview=[];for(let i=reviews.length-1;i>=0&&preview.length<3;i--){const c=clean(reviews[i]?.content||'');const k=c.replace(/\s+/g,'').slice(0,320);if(k&&!seen.has(k)){seen.add(k);preview.push(c.slice(0,70)+(c.length>70?'…':''))}}window.__TMALL_AI_REVIEW_PROGRESS__={
|
||||
function publishReviewProgress(round=0,stagnant=0,mode='DOM',extra={}){
|
||||
const seen=new Set(),preview=[];for(let i=reviews.length-1;i>=0&&preview.length<3;i--){const c=clean(reviews[i]?.content||'');const k=c.replace(/\s+/g,'').slice(0,320);if(k&&!seen.has(k)){seen.add(k);preview.push(c.slice(0,70)+(c.length>70?'…':''))}}
|
||||
const currCount=dedupReviewCount();const reviewRatio=Math.min(1,currCount/reviewCap);const overallProg=Math.min(78,Math.round(20+reviewRatio*58));
|
||||
updateInPageHud({count:currCount,round,progress:overallProg,message:mode||`评论采集中:${currCount}/${reviewCap}条`,elapsedMs:Date.now()-reviewStartedAt,preview});
|
||||
window.__TMALL_AI_REVIEW_PROGRESS__={progress:overallProg,reviewCap,
|
||||
count:dedupReviewCount(),
|
||||
rawCount:reviews.length,
|
||||
networkReviewObjects:reviewNetMeta.networkReviewObjects,
|
||||
@@ -563,7 +625,7 @@ async function collector(manual=false,collectionMode='standard'){
|
||||
// 每轮即时解析已捕获网络响应
|
||||
const capNow=(window.__TMALL_AI_CAPTURE__||[]).slice(-120);for(const c of capNow){try{const ck=String(c.__safeKey||[c.method||'',c.url||'',String(c.text||'').slice(0,240)].join('|'));if(processedCaptureKeys.has(ck))continue;processedCaptureKeys.add(ck);if(/review|rate|comment|feed/i.test(c.url||''))reviewNetMeta.networkResponses++;let z=(c.text||'').replace(/^\s*[\w$.]+\(/,'').replace(/\)\s*;?\s*$/,'');try{walk(JSON.parse(z),0,c.url||'')}catch(_){const m=z.match(/\{[\s\S]*\}/);if(m)try{walk(JSON.parse(m[0]),0,c.url||'')}catch(__){}}}catch(e){}}
|
||||
collectVisibleReviews();collectVisibleQuestions();const after=dedupReviewCount();stagnant=after<=before?stagnant+1:0;publishReviewProgress(round+1,stagnant,next?'翻页/加载更多':'短滚动',{lastDelta:after-before});
|
||||
if(collectionMode!=='full' && after>=reviewCap){collectionComplete=true;explicitEnd=true;stopReason='sample_cap';publishReviewProgress(round+1,stagnant,collectionMode==='quick'?'已采够快速样本':'已采够标准样本');break}
|
||||
if(after>=reviewCap){collectionComplete=true;explicitEnd=true;stopReason='sample_cap';publishReviewProgress(round+1,stagnant,collectionMode==='quick'?'已采够快速样本':collectionMode==='full'?'已采够深度样本':'已采够标准样本');break}
|
||||
const bodyNow=clean(document.body?.innerText||'');const noMoreText=/没有更多(?:评价|评论)|已显示全部(?:评价|评论)|到底了|暂无更多(?:评价|评论)/.test(bodyNow);const captcha=/验证码|滑块|安全验证|访问过于频繁|操作频繁/.test(bodyNow);
|
||||
if(captcha){stopReason='human_verification_required';publishReviewProgress(round+1,stagnant,'需要人工验证');break}
|
||||
if((disabledNext||noMoreText)&&stagnant>=1){explicitEnd=true;collectionComplete=true;stopReason='explicit_last_page';publishReviewProgress(round+1,stagnant,'页面明确结束');break}
|
||||
@@ -807,6 +869,7 @@ async function collector(manual=false,collectionMode='standard'){
|
||||
// 严禁“无新增/无下一页控件”单独作为完整依据
|
||||
const completeness=collectionComplete?'confirmed':'unconfirmed';
|
||||
await saveCheckpoint({reviews:[...deReview.values()],questions:[...deQ.values()],round:pagesVisited,updatedAt:Date.now(),complete:collectionComplete,stopReason,completeness,networkTotal,publicNumeric,reviewNetMeta});
|
||||
finishInPageHud(collectionComplete, collectionComplete ? ('✓ 采集完成(已获取 ' + deReview.size + ' 条评论),正在提交分析...') : ('采集结束:已保存 ' + deReview.size + ' 条评论'));
|
||||
return {
|
||||
meta:{platform:location.hostname.includes('tmall')?'tmall':'taobao',collectedAt:new Date().toISOString(),url:location.href,collectorVersion:'9.2.2',imageClassificationVersion:'strict-v1',networkCaptures:captured.length,structuredRoots:roots.length},
|
||||
product:{itemId,skuId,title,shop,brand:structured.brands[0]||scriptString(['brandName','brand'],120),category:structured.categories[0]||scriptString(['categoryName','catName'],160),url:location.href,parameterCollection},
|
||||
|
||||
Reference in New Issue
Block a user