From a0624221de579360300bbc2d29afeff1c39e38da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=93=AD=E5=9D=A4?= <10372463+ming-kun@user.noreply.gitee.com> Date: Thu, 2 Apr 2026 00:03:16 +0800 Subject: [PATCH] new file: app/amazon/__pycache__/del_brand.cpython-39.pyc new file: app/amazon/__pycache__/main.cpython-39.pyc modified: app/amazon/del_brand.py modified: app/amazon/main.py modified: app/blueprints/__pycache__/main.cpython-39.pyc modified: app/blueprints/main.py modified: app/config.py modified: app/main.py new file: "app/web_source/brand-\346\227\247.html" modified: app/web_source/brand.html --- .../__pycache__/del_brand.cpython-39.pyc | Bin 0 -> 14781 bytes app/amazon/__pycache__/main.cpython-39.pyc | Bin 0 -> 14950 bytes app/amazon/del_brand.py | 16 +- app/amazon/main.py | 301 ++++++++++++++---- .../__pycache__/main.cpython-39.pyc | Bin 4587 -> 4689 bytes app/blueprints/main.py | 18 +- app/config.py | 4 +- app/main.py | 19 ++ 8 files changed, 274 insertions(+), 84 deletions(-) create mode 100644 app/amazon/__pycache__/del_brand.cpython-39.pyc create mode 100644 app/amazon/__pycache__/main.cpython-39.pyc diff --git a/app/amazon/__pycache__/del_brand.cpython-39.pyc b/app/amazon/__pycache__/del_brand.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..656d6827168b03c4140699dc9e3145499e900dd6 GIT binary patch literal 14781 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 literal 0 HcmV?d00001 diff --git a/app/amazon/__pycache__/main.cpython-39.pyc b/app/amazon/__pycache__/main.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1ed31bff55cdf0e43aab707dd70a14818a9106e GIT binary patch literal 14950 zcmbtbeQ;aVm49FQmSx$o9Vdh!LIP2MkkW3t^<-I6oU|#$&?La3lB$ZIk~py>`|63r z+9#TjKpc{Q89vg212!KGX(0p}AWc&EXJ@CgGdt7S>CATCUps9i+y5*(yPNI|JEi+O z=RQ44b|~AaJi5N`e!X+=J@=g7Irk{j(h^qi3AFw5!0C*l{Ei+PKRO=n#JBi55>qkN zK_w;st0|SAwUmbc+Mu4(Q@ToZ`k;~XrF=Pm%AX6Q0$gSc26LfQC>KtJ`PnxZ$wgDq zTuZ7Y7fZ!*t*O>rJQdHirP^}ssdjGTA6$^@NOj~oQ=O{v4aE$Y!9$7}JgB9*%#ayA zq@)&RyUoZRO-y7L9#qgr-J&F;GoV!IOR7%L%vf1#AeT+*j?p{NZ#nw<{D@+E|JZc_Z13*8}?=UOIATR(M&PjU&sv&X05E53^=Xd+H!ASdh7Zvcc;H||AzZF zI1y3G59D{ItxR!`_fXtb7;-v$H+*};)(z>eZ(iTmn_hq4ru5g>Z`t64Q+?^X?!D)} z^?eUH5#;aRvSD-I`g<@E9`0MeWy^#2Ztev&E$efcav`7X6$6iCLCBtg!NK&9DD-EG z#eSMm<0piNJMk@csfvYDzaXQ^&d(=JKpB>oa}hn&~$KhkPl&88kzv31ow) z3GL7pDQ0+FH6v#9kdX?{DQz)hD2>dPW}{{+#)_M5C~wKe8f&uA*(bAo+-EK@JJ71N zsg)0{qC^u$>KqT6UFJfxiQ~C#w)Y~XX{_#X!(3!8M!WW=@@qiJ67yP=FTiL^F^lWW z1fDv~>&<1DW2boo_+W(-*@{iNr;s1O&X)cb%XRtU#p;ujQ!hPJd+Jp6M{j;|>BZ@D zuS}nNO_t33@RjQ5iR#Zks2w>qJ$bD9?18Bx$Eu?fGe3G=cI?MudmqXh_}+zY@pdGp zYAb2QQZ3EGHjL|dGE7Q+rh(LN`j7^!py@xTf>nJWF;MD8$Li?G>ZzmEV=v4hZ);Ld z1&A3lVA^1*m=*aFs%V z>ePYi@#imJz>56v;ap~5-#`Y3Vg06uGt9kIlwUmra?^OlfR#?u`-|;J6wR+`D*pZu z^!ru(>E(`w{aIb_;24|wHr|T^5y*k_Gdr`6Z!r5vcCeo$QT;;;AHbIv{}yB{WkQ`) zCU6EO)rw9VVCyeI64@G3!&YBXPR=g7zm!Lp+KD%+AD@{1=-KMw3*m(O@iObh&SH=E zM71tCJqg*d_Q7cFz&lMXn9h3r)P+;k(Vu$tpSG=d=++gvTUVHg72oVxaZk^REuX62 zE$iD7-z{s~5`9TSXdueb^M$=muxN=LB)A;kkQm5YsJs@HVi_ejP_q!#7OhNfh_^>3 z*YN;@g`KoxMOq^z5L3&ojmwPMCN}n?5orKk$&KJBTbW>1-i<=gEy4bx}(k82gp)+Qit%;2O}(QVxf?a{?<9x=@O5jm-w z(Me5J9#n=`a(N4lkFoTL0MekXp(plhB4is>W5&$ZLyE2TDhJhyO1%ul3Xfb?m5NVQ z%B#(|6|sE>)kjqED;}>+K4ZT%vt7RFA;nx!@jdDrUMc^tQPyCb2jxsV>hB*@MVRQ= z9mQ-qk26(Ri7u+o-@;=zwq5&qZP(y!INA$KU&g9d$KI(v`DWv}{^Zi=CxTW5$FaGZSG2MhgsoB%OSp=3FRH8PZSlycn6lCryXyU66t zTOmM79q3RS|H;%VZ@6TYix#+}%tZSjfGAXG-&p~5ia@y)Oj=E#+Q3$Uagr+A6dIz!hf8nc?S2UgmoFHMXUWb**3>NP| zrrzH1ZO9E-BfJ((n{nx}>iP3M3Dj@ieDCHyIr+C=tB$`}ef(!LC*E#qOe*Hs)Z0g@ zA3a_>vcEQdMAlZ%9IK6vVe-Hz#@@_;RU+xy)Z)YUWFzWdS65XO@;j0(f;#a23Oc~z z8cG&Y5=7$YJF`~GUo36U4OmWR9Yru^N$(Yzp&s_8L3ss_~#)zHIgTn+2(T1<9%tk+3K4Mm!c*RXm_BddCm~98OUS-?4hqa2Y;vsoh)vN}vcqMThM} z`%bTCmmMGwL%o?RL$=@6iB4O$!jCB8Qwu54FXplEW~*f)HmS(x2Nft!e6aClt)XXQCA-wBmDCf^F$ zuOY=wCGLs=pdTmdXf8{66|R$KET3>&B<8ia-aCa4FxA=!C}yhV&McF(v2vI6>UiUS zyW`y;XJalu4l{I?az2-sfp;!flpX+g$g@CT_WV!e5%7+Wm-+Kgo@0=Fbo_KeH<^xa zz{=)|C6dhY9h-VDpT975`hurM#g|amCuR3?WCelxc}kH$ev!VU(xDRDrAmaYtxjqW z!15i|ObsGO$KfkjnZeD4y+s}gX0RaroYVx_C@F(X)j22639Z|i&1d%wi8~%a8}V02 z$~QNjxyCczpe^gYHp2AqL)crTq7!DJpC*)%iZCr{dS%gRbqC3svY=j@DGubDYGpx7 zvD8ndFRbxW-g2TlG6RF?DxYFSt1y%n+2Il}aMolwHebReu8?9$H&G(}kicG7 zFPxZp_JSagL%Oao)CZ&L3dswJdEELHDEL&pjiu+dCh;j7^d!VRXi_30%Vi}VIB|u< zPonzzyVK|1KqC^E;yXy301-U|uYxM>#xu)k$rOnaSzHO|_vu%ET6^)a>Vff3E*(gP zJV%6EFLeluXw)}g6PEz@olwcVSlDxVk>kq>Q4q;iW&|m@+e<^_8uq22sBe1wfqy9R^JCO?uJ? z%zaLXhF~<|X34m{s$w8H{(XFleMl6)p+=z?q8k18>xN23C2~mPoX7L;{VhxMc9bT7 zHlkWk?MB@ab)~)p>ZC)9sa;we>cy{?yFZW3v3T@O@jWC>rY9M+v|X%SUes|MS+hW& zkjX=Nm`rTK!0=?v;8^YAe{0E%8_3Xy_B(ngnfbh&od@CqzB>8rnC z3n7DV+CHq?dzGlIS@B0uB0jU?Hr!*_$!pD}tkXW@QLbZWLZZHVn5j0ruS_#-vjaBz z+pte2VU}W+-{V-(QjRfmT|8ez4vR9CqHM+b<2;n(ZvhIk$=<3 zkvF+o{VAqK7q|Zz)aWBae-Sm$eI7MH;5b{!m8D}isg0Um3U3(Ufa2A$pUxaUKK;(~ zGy5-2fAq}%6MK7Nt0y4`n}fX*__)%pj?OprdWb@hX)CrONd;k3l?E-K1PFTQI5B`{ z(pYlXL}>}~iQ0RUup6r{{i1s21No*o&;eF(w7(=oHgB%s+fj66#t;n1i>X z)9<_?DPoPi@e0ZL+H3o3?;phq)^#-@gT&N}hoqvOe&@s5yN}Vh2gYk}zv}WDZb6;Pa0Rieu{fB`4hh;8LB5w%r~n6n?9{?_oI! z$Hd62>Q6=b2Mfh)8dyhUlQD?{zE8bLk%|oU4oSu*IIx`xzfFaHlkYuJf$aj=75)kK zO^Yla3s8&92%`fh>aMEvM0igOALf{p+c5Hr_!bW#Q37?`KwgI!d=Wvl3-|$l@H}Yh zGCc@ffiFA~Vc-LJCA4-m$Vg!^ycJQs3+AYy8Cpe)1<)u2CA?PTqh<`!PwZ3wkSm?QD#MQi!4EQKRfZq_26I*# zkp{s9(+B@;fZZT|X?{Yn!#iukQ%j(OK|}~3!XRU?7bUFkRaYNf(o zK#HFgzB&Qe1NafLLr~9Aep?3vp+?a0XugAMI+@}wsA+f1{MYH-fE=d#cJe%{=0a}Q z?a_d{CIV%w0Qubj9kVoq0V$#kIyPH0uf+^HHWNj~_Zr@N4X+P@3?2SHRk5nSmpWcUi#F7@csu0G-{y)d@|YE~17?)n5Wj{SqO}r-Vc!ZxuZcqd25={!-wkF6AP6N)&vJ&9E9$H3 z&Zw8`ZnRs$8!aBU-DJnneb+e?ij$?M)t(tT>4L(mnIOvl9xm{J7w2z+0^GLXfHG0_LmY0O8vr% zGpFC0+5hCs>38AXubz4N@<&I#ex&~!B@|#ngYs@__W3Y@2Jxh1Ph#rgv$dDU8(;+0 ze-RK7yD)7rKnYo0tSTFgWJwlG)Xh{IL_+25+UHS4fx4LW9u{YcHIgnS6idxy*keFjiW>`q94l8MsF*F5=p)(IfEZF}AYsF94s;sj}J(|fWbPODtk zG!@XoI$`OXBCsLS)HXz~PLsb*?4tqZK#3g6evfaFI7W#84gdti2tL4_PzMhh07MLb zw`c?a2tafLKHSdW!4L2d2k_8bkU%boZmPjZ?H-_5iQfJ{66lDk=rZ3AR%H$A*iD+NN6z)fP`OiOTO-9kihpS zuu=UJ0a%`@2NHrK{|E&b1C$^ORX1Z+$d0+50Vpk3LB}hxM`KXX{9l5E-Q*W&JFZq* zxnDajf?FB%ERYtedXSz7w&R$6#BN8rNCK{WH`gqd?=Y{a zw7H&=-$3!*2gMJ+%E-UC^Ipqsmo&`#TAH`)1%Euxnf)y2U4L#|3wQ_C*lkeRw?omk zW4_3lOPl6gaqFtU|#YjL9R>wBdF}C873%%aR-^!>*wse zJ8RzCWpZza*X9plmB0z;b-}!$iJIlj)W}t`>-7A`)#%Bu#eGEM4iY5cb(+`j#^!kp zujTdDM~=Dcz2Cga9ZB->N^j*2cf{El>mtQ7PJYph^GjVibg|E+e-0h>of}>|%f-WM zy5YxmtT;$clm$6RkwaLgFl+;kS#cq^mHmAN|qnQ{1DzEpYw>c1*| z)_luCdI-@~)t6qrd}%^L8V?S6nZE>fc@n%QLGP08l9|b$dxH~fa^+_omQ}}&Pn|m3 z)Z#CLWp$NL}I|iN^vv2B;Hgf$=z6LL#b?ye$_^lS7QGQyOlWL4lW@I z#-L^#-RF&dFO7aXlEzv&J^{nSPgGw&ETL@e`E!_7?fnDNtp&;$Nzxo=4!&6(eL{{Y zUnfiEy-wcD&!NR#{QHO!8qJNT;pvDHl_se4>1U>%dqqxi4rM^qpoXqWPHS@C=@JNe zHU|hboeqgd@K5YTk_s~qF-N%ht#pByYVoe?_%4xfXe#C!1rq0tz}F-Fj_biW?8-T< zrJ@{lQY<2#gzi7-G!#n|p5huY1d~|+fb6L!Kd9|LfWRWQ0vg1ET!fxPGA@BBd$9

5ar3Z_G_$d`34D4dlRI7U>ih34ADIHCbl=_{(P-bMX zkTK^X&k6?l7DQ?BEHX-*98ob`ei808T&I$pB%bT@!6C|z%76*LN$<=5s3kt0xqWM8VhBBwx|!4pM%dsM*zhW z7JOoWJz-Y35ea+TvS?!&0(pHuP z9hNE>%GNOP1}IyE24ha`3_@L?il7Y&Lh=I=u7diF01C@k$qsJSiOW9eH8T*MSO>%l z$y+wu3%Kff%_t8w++F$=RuI;!w9_x2Kl-_5tf!kjz2Rl%YvnIiqa6AmH8H}faqZxZ zMr!y^E7>pdZ8YBav)pjK5Op`kQ8;&`SgHFoq)3qxM~c5SSQnOof{kpjmm=jQ$zJKn zASo&yqvQ}Jk5lplC5I_Fh(yZp#=$)SSBjus$~vj&$PwbY86+LJenIT<%yU0-;{^I( z)UBeC*#`d_<;ZqpnI)d2oM&|>qf%;JgDB)M(gLf0dTwiJ)(msYPww~O&!LUkNl63vFPsF zJ@O}wie7%eQYj+K#t$7#V?d?AW4N(>6$3%v2p>L37}vn@3gKrV_tCL4IrLcGPqT~I z2ipuS_-5J-M$VIa+CMUZ<3?e{WO@4F$P7?uF}I6(2d$MtiD7N=f3iHyRvfcbpO-@)ITFghHo+m63f;E z-{@G-S#oe=$`LM*)r=Xfo&+H+Q5v;SU=IA9cNW&C|3VB>I-H8F_ zo&`E*Ex4d8(iu?#aqzuVaTH;q{jWcO+aaj7Zm8Mv!bVg;%s*w)N`${_FP($WR9-(y!QpMcm-%zKYSpSedo%PC zhs5-c56BllWLDokQ9JX#H)tP@DG4CQFT;iEA)It{9lso4?(z{-yNQ;MPO=gR0)dp5 zHZIfr$fKrcHxk3NOe_Eh7s{X2;6TzH7cGpA<7P-~%vY{5K6)cVZFi9xBc{0x&4hyR zB!euW9Br>m$f=4SyC6F2%S1SwV)SDbwH$9Go2tpmxgM3tF8X~7xe?Q8C!9{_3T6p@ z#B8V?<#dc&Y<)Bhg%6tJw*H`cpd<$|@8(FL3$@MMN zqpJ;S@sN_IA8hzW9&Tu%kza^os3F5iuA-F&If}Ucq>rN>E}y?xJ^TxZx}NzzpeWr0 zO1z&N;MTkL!rPZWo~%CoA*4!8M&k3HQ-Cw=`E2|=8&Na+_e1G6Xk1z^L3`q~!41E? zkP+r4$V(wgLzd(hUTT&qz0`IC5=8C?a`KmD4Vxf2iGBn1HR7Z=8Y(*76O%qg%o_g0 zILe59s8u?hTqJE69{4F#$zK8a5n4~bQ{w_a2TPwL1oO z@=pLHa0t_Y#0ztN5SEH(_~%-y$vWa6en^fZf|(DPO7T2BUWLV+;5y0jcMyxPpu{=H XUldRwt}a*Odh7>^{JT9E3#$JI4F#O2 literal 0 HcmV?d00001 diff --git a/app/amazon/del_brand.py b/app/amazon/del_brand.py index cdce0fd..9bb2a94 100644 --- a/app/amazon/del_brand.py +++ b/app/amazon/del_brand.py @@ -233,7 +233,7 @@ class ZiniaoDriver: # 获取店铺列表 shop_ls = self.get_browser_list() self.store_id = None - # print(shop_ls) + print(shop_ls) for shop in shop_ls: if shop.get("browserName") == shop_name: self.store_id = shop.get('browserOauth') @@ -456,16 +456,20 @@ class AmazoneDriver(ZiniaoDriver): time.sleep(0.6) search_input = self.tab.ele("xpath://kat-input[contains(@class,'SearchBox-module__searchInput')]").sr( 'xpath://span[@class="container"]//input[@part="input"]') - search_input.input(asin) - + search_input.input(asin,clear=True) + sku_ls = [] for _ in range(3): search_btn = self.tab.ele("xpath://kat-icon[@name='search']") search_btn.click() load_ele = self.tab.ele("xpath://div[contains(@class,'Loader-module__loader')]") - load_ele.wait.hidden(timeout=20) + # load_ele.wait.hidden(timeout=3, raise_err=False) + load_ele.wait.deleted(timeout=3, raise_err=False) time.sleep(1) - sku_ls = self.tab.eles("//div[@data-sku]") + + sku_ls = self.tab.eles("xpath://div[@data-sku]") + if len(sku_ls) > 0: + break return sku_ls def del_action(self, sku_ele): @@ -473,7 +477,7 @@ class AmazoneDriver(ZiniaoDriver): dropdown.click() time.sleep(1) - del_btn = dropdown.ele("xpath:.//button[@role='menuitem' and text()='删除商品信息']") + del_btn = dropdown.sr("xpath:.//button[@role='menuitem' and @data-action='DeleteListing']") del_btn.wait.displayed(raise_err=False) del_btn.click() diff --git a/app/amazon/main.py b/app/amazon/main.py index 17aeaaf..fd8472c 100644 --- a/app/amazon/main.py +++ b/app/amazon/main.py @@ -3,6 +3,7 @@ import traceback import requests from datetime import datetime from typing import Dict, Any, List +from concurrent.futures import ThreadPoolExecutor, as_completed from config import JSON_TASK_QUEUE, runing_task, runing_shop, DELETE_BRAND_API_BASE, ZN_COMPANY, ZN_USERNAME, ZN_PASSWORD from amazon.del_brand import AmazoneDriver, kill_process @@ -20,6 +21,11 @@ class TaskMonitor: } self.chunk_index = 1 # 当前处理的分块索引 + self.max_workers = 5 # 线程池最大线程数 + self.executor = None # 线程池执行器 + + # 在提交新任务前杀掉旧进程(确保环境干净) + kill_process("v6") def log(self, message: str, level: str = "INFO"): """日志输出 @@ -32,28 +38,60 @@ class TaskMonitor: print(f"[{timestamp}] [{level}] {message}") def start(self): - """启动任务监控(阻塞式循环)""" - self.log("任务监控器启动,开始监听队列...") + """启动任务监控(使用线程池处理任务)""" + self.log(f"任务监控器启动,开始监听队列... (线程池大小: {self.max_workers})") - while self.running: - try: - # 阻塞式获取任务,避免CPU空转 - task_data = JSON_TASK_QUEUE.get(block=True, timeout=1) + # 创建线程池 + self.executor = ThreadPoolExecutor(max_workers=self.max_workers) + futures = [] # 保存所有提交的任务Future对象 + + try: + while self.running: + try: + # 使用较长超时时间等待任务,减少空等待异常 + # 超时后继续循环检查self.running状态,避免卡死 + task_data = JSON_TASK_QUEUE.get(block=True, timeout=30) + + # 检查任务类型 + task_type = task_data.get("type", "") + if task_type != "delete-brand-run": + self.log(f"未知任务类型: {task_type},跳过", "WARNING") + continue + + # 提交任务到线程池 + self.log(f"接收到删除品牌任务,提交到线程池处理...") + future = self.executor.submit(self._process_task_wrapper, task_data) + futures.append(future) + + # 清理已完成的future对象,避免内存累积 + futures = [f for f in futures if not f.done()] + + except Exception as e: + # 静默处理队列为空的超时,只记录真正的异常 + if "Empty" not in str(e): + self.log(f"任务监控异常: {str(e)}", "ERROR") + # 队列为空时不需要额外等待,直接继续循环 + + finally: + # 关闭监控时,等待所有任务完成 + self.log("正在关闭任务监控器,等待所有任务完成...") + if self.executor: + self.executor.shutdown(wait=True) + self.log("所有任务已完成,监控器已关闭") + + def _process_task_wrapper(self, task_data: Dict[str, Any]): + """任务处理包装器(用于线程池调用) + + Args: + task_data: 任务数据 + """ + try: + self.log(f"线程 {id(task_data)} 开始处理任务...") + self.process_task(task_data) + self.log(f"线程 {id(task_data)} 任务处理完成") + except Exception as e: + self.log(f"线程 {id(task_data)} 任务处理异常: {traceback.format_exc()}", "ERROR") - # 检查任务类型 - task_type = task_data.get("type", "") - if task_type != "delete-brand-run": - self.log(f"未知任务类型: {task_type},跳过", "WARNING") - continue - - # 处理任务 - self.log(f"接收到删除品牌任务,开始处理...") - self.process_task(task_data) - - except Exception as e: - if "Empty" not in str(e): # 忽略队列为空的超时异常 - self.log(f"任务处理异常: {traceback.format_exc()}", "ERROR") - time.sleep(0.1) # 短暂等待避免异常循环 def process_task(self, task_data: Dict[str, Any]): """处理单个任务 @@ -222,14 +260,41 @@ class TaskMonitor: self.log(f"开始处理国家: {country},共 {len(items)} 个ASIN") self.update_task_status(task_id, current_country=country) - # 切换国家 - try: - switch_success = driver.SwitchingCountries(country) - if not switch_success: - self.log(f"切换到国家 {country} 失败,跳过该国家", "ERROR") - return - except Exception as e: - self.log(f"切换国家 {country} 异常: {str(e)}", "ERROR") + # 切换国家,最多重试3次 + max_retries = 3 + switch_success = False + + for retry in range(max_retries): + try: + self.log(f"尝试切换到国家 {country} (第 {retry + 1}/{max_retries} 次)") + + # 如果不是第一次尝试,先刷新页面 + if retry > 0: + self.log("重试前刷新页面...") + try: + driver.tab.refresh() + time.sleep(3) + except Exception as e: + self.log(f"刷新页面失败: {str(e)}", "WARNING") + + switch_success = driver.SwitchingCountries(country) + if switch_success: + self.log(f"成功切换到国家 {country}") + break + else: + self.log(f"切换到国家 {country} 失败", "WARNING") + + except Exception as e: + self.log(f"切换国家 {country} 异常: {str(e)}", "ERROR") + + # 如果还有重试机会,等待后继续 + if retry < max_retries - 1: + time.sleep(2) + + # 如果切换失败,回传该国家所有ASIN为失败状态 + if not switch_success: + self.log(f"切换到国家 {country} 失败,已重试 {max_retries} 次,将所有ASIN标记为失败", "ERROR") + self._report_all_asins_failed(country, items, task_id, shop_data) return # 切换到库存管理页面 @@ -238,6 +303,8 @@ class TaskMonitor: self.log(f"已切换到库存管理页面") except Exception as e: self.log(f"切换页面失败: {str(e)}", "ERROR") + # 切换页面失败也回传所有ASIN为失败 + self._report_all_asins_failed(country, items, task_id, shop_data) return # 处理每个ASIN @@ -254,7 +321,7 @@ class TaskMonitor: try: self.log(f"[{idx}/{len(items)}] 处理ASIN: {asin_item.get('asin', '')}") self.process_asin(driver, asin_item, country, task_id, result_id, - file_key, source_filename, total_rows, idx) + file_key, source_filename, total_rows) except Exception as e: asin = asin_item.get("asin", "未知") self.log(f"处理ASIN {asin} 失败: {str(e)}", "ERROR") @@ -283,48 +350,89 @@ class TaskMonitor: self.update_task_status(task_id, current_asin=asin) status = "失败" + max_retries = 3 # 最多重试3次 - try: - # 搜索ASIN - sku_ls = driver.search(asin=asin) - self.log(f"搜索到 {len(sku_ls)} 个SKU") - - if len(sku_ls) == 0: - status = "查询不到" - self.log(f"ASIN {asin} 未找到商品", "WARNING") - else: - # 删除所有找到的SKU - success_count = 0 - for sku in sku_ls: + for retry in range(max_retries): + try: + self.log(f"处理ASIN {asin} (第 {retry + 1}/{max_retries} 次)") + + # 如果不是第一次尝试,先刷新页面 + if retry > 0: + self.log("重试前刷新页面...") try: - suc = driver.del_action(sku) - if suc: - success_count += 1 - self.log(f"SKU 删除成功") - else: - self.log(f"SKU 删除失败", "WARNING") + driver.tab.refresh() + time.sleep(3) except Exception as e: - self.log(f"删除SKU异常: {str(e)}", "ERROR") - - if success_count > 0: - status = "成功" - self.log(f"ASIN {asin} 删除成功 ({success_count}/{len(sku_ls)})") - # 更新成功计数 - if task_id in runing_task: - runing_task[task_id]["success_count"] += 1 + self.log(f"刷新页面失败: {str(e)}", "WARNING") + + # 搜索ASIN + sku_ls = driver.search(asin=asin) + self.log(f"搜索到 {len(sku_ls)} 个SKU") + + if len(sku_ls) == 0: + status = "查询不到" + self.log(f"ASIN {asin} 未找到商品", "WARNING") + break # 查询不到商品,无需重试 else: - status = "失败" - self.log(f"ASIN {asin} 所有SKU删除失败", "ERROR") - # 更新失败计数 + # 删除所有找到的SKU,如果任何一个失败则重新开始整个流程 + success_count = 0 + all_success = True # 标记是否所有SKU都删除成功 + total_sku_count = len(sku_ls) + + for sku in sku_ls: + try: + suc = driver.del_action(sku) + if suc: + success_count += 1 + self.log(f"SKU 删除成功 ({success_count}/{total_sku_count})") + else: + self.log(f"SKU 删除失败", "WARNING") + all_success = False + break # 任何一个失败,退出循环,准备重试整个流程 + except Exception as e: + self.log(f"删除SKU异常: {str(e)}", "ERROR") + all_success = False + break # 发生异常,退出循环,准备重试整个流程 + + # 判断删除结果 + if all_success and success_count == total_sku_count: + status = "成功" + self.log(f"ASIN {asin} 所有SKU删除成功 ({success_count}/{total_sku_count})") + # 更新成功计数 + if task_id in runing_task: + runing_task[task_id]["success_count"] += 1 + break # 全部成功,跳出重试循环 + else: + # 有失败的SKU + if retry < max_retries - 1: + self.log(f"有SKU删除失败,准备重试整个流程... ({retry + 1}/{max_retries})") + time.sleep(2) + continue # 继续下一次重试 + else: + # 所有重试都用完了 + if success_count > 0: + status = "部分成功" + self.log(f"ASIN {asin} 部分SKU删除成功 ({success_count}/{total_sku_count})", "WARNING") + if task_id in runing_task: + runing_task[task_id]["success_count"] += 1 + else: + status = "失败" + self.log(f"ASIN {asin} 所有SKU删除失败", "ERROR") + if task_id in runing_task: + runing_task[task_id]["failed_count"] += 1 + + except Exception as e: + status = "删除异常" + self.log(f"处理ASIN {asin} 异常: {str(e)}", "ERROR") + + # 如果还有重试机会,继续重试 + if retry < max_retries - 1: + self.log(f"发生异常,准备重试... ({retry + 1}/{max_retries})") + time.sleep(2) + else: + # 所有重试都失败了,更新失败计数 if task_id in runing_task: runing_task[task_id]["failed_count"] += 1 - - except Exception as e: - status = "删除异常" - self.log(f"处理ASIN {asin} 异常: {str(e)}", "ERROR") - # 更新失败计数 - if task_id in runing_task: - runing_task[task_id]["failed_count"] += 1 # 更新已处理ASIN计数 if task_id in runing_task: @@ -355,10 +463,65 @@ class TaskMonitor: self.post_result(task_id, payload) self.log(f"ASIN {asin} 结果已回传,状态: {status}") + self.chunk_index += 1 except Exception as e: self.log(f"回传结果失败: {str(e)}", "ERROR") + def _report_all_asins_failed(self, country: str, items: List[Dict[str, Any]], + task_id: int, shop_data: Dict[str, Any]): + """将国家下所有ASIN标记为失败并回传 + + Args: + country: 国家名称 + items: ASIN列表 + task_id: 任务ID + shop_data: 店铺数据 + """ + self.log(f"开始回传国家 {country} 下的 {len(items)} 个ASIN失败状态") + + file_key = shop_data.get("fileKey", "") + source_filename = shop_data.get("sourceFilename", "") + total_rows = shop_data.get("totalRows", 0) + + for asin_item in items: + asin = asin_item.get("asin", "") + + try: + # 更新失败计数 + if task_id in runing_task: + runing_task[task_id]["failed_count"] += 1 + runing_task[task_id]["processed_asins"] += 1 + + # 回传失败状态 + payload = { + "submissionId": "", + "files": [{ + "fileKey": file_key, + "sourceFilename": source_filename, + "chunkIndex": self.chunk_index, + "chunkTotal": total_rows, + "processedRows": self.chunk_index, + "totalRows": total_rows, + "currentCountry": country, + "currentAsin": asin, + "countries": [{ + "country": country, + "items": [{ + "asin": asin, + "status": "失败" + }] + }] + }] + } + + self.post_result(task_id, payload) + self.chunk_index += 1 + self.log(f"ASIN {asin} 失败状态已回传") + + except Exception as e: + self.log(f"回传ASIN {asin} 失败状态时出错: {str(e)}", "ERROR") + def post_result(self, task_id: int, payload: Dict[str, Any]): """回传结果到API @@ -366,7 +529,7 @@ class TaskMonitor: task_id: 任务ID payload: 结果数据 """ - url = f"{DELETE_BRAND_API_BASE}/newApi/api/delete-brand/tasks/{task_id}/result" + url = f"{DELETE_BRAND_API_BASE}/api/delete-brand/tasks/{task_id}/result" try: response = requests.post( @@ -376,7 +539,7 @@ class TaskMonitor: timeout=30, verify=False # 忽略SSL证书验证 ) - + print("【结果提交】:",payload) if response.status_code == 200: self.log(f"结果回传成功: {url}") else: @@ -404,7 +567,7 @@ class TaskMonitor: def main(): - """主函数:启动任务监控""" + """主函数:启动任务监控""" # 创建并启动任务监控器 monitor = TaskMonitor() @@ -413,7 +576,7 @@ def main(): except KeyboardInterrupt: monitor.log("接收到中断信号,正在停止...") monitor.stop() - except Exception as e: + except Exception: monitor.log(f"监控器异常退出: {traceback.format_exc()}", "ERROR") diff --git a/app/blueprints/__pycache__/main.cpython-39.pyc b/app/blueprints/__pycache__/main.cpython-39.pyc index c27011ec63142681471ddcbf7384da04103e3fb1..2026e60e54e528a752a5801d6cc3091dd5395804 100644 GIT binary patch delta 588 zcmZXSPiqrF6u{q`OtOEnv)y#lRt0-dp%hw^lS1{VH*W!hqR7^XN!GGyH`7AGtPxYF z3WeYvYasaul5Zd$z4!^d^rT) zL+)i{6QbJ`ggN+%kQfNVb(L#LLo-cy0$fGk7?Hiy_0)5&jV_mEBEmsnSmrh_bLZUr z0B7(DIpglBnV7;6OqydD<9i5QzH&|yOIdWbGKUB7{Khkq(4;8otT+emo!|uHO<9yy zNF}`w6Fl0KRudYanzKSLtW0naBj`O+9ageeI=sSHb?K7o^e?)q(9cU$k$z74y!sDm zD(W(Sb6s(3(zt*r$?0|Bs~Mn<@C}{U1bs&~1>b1oW2r-3Sq<2}lHOxGd$r)OTjQ;R zmT2wn$3etvBIv*F*Y*SPvNOE;|8b|=7rl5eyq+F0KYh!nmc={95I-&^ZV%D^ziA#eBh%j^o6mH52ps$tRW;@&ypMG@kCDeiHoIY`cqD8<&}E& ziuM0DRI{TrO+`E8rF9E;B=spox{onoL@{v;An}~!tEROA6^*K}P*MR~QZKBmKA1WD lVlHBL_|1R@=gTJJ}V4eT~ diff --git a/app/blueprints/main.py b/app/blueprints/main.py index a9717dc..82e5ce5 100644 --- a/app/blueprints/main.py +++ b/app/blueprints/main.py @@ -110,14 +110,16 @@ def proxy(path): if key.lower() in ['host', 'content-length', 'connection']: continue headers[key] = value - try: - print("=============================") - print("target_url:",target_url) - print("params:",params) - print("data:",request.get_data()) - print("=============================") - except Exception as e: - print("打印失败",e) + ignore_url = [f"{JAVA_API_BASE}/api/delete-brand/tasks/batch",f"{JAVA_API_BASE}/api/delete-brand/history"] + if target_url not in ignore_url: + try: + print("=============================") + print("target_url:",target_url) + print("params:",params) + print("data:",request.get_data()) + print("=============================") + except Exception as e: + print("打印失败",e) try: # 使用流式请求 diff --git a/app/config.py b/app/config.py index a371ba2..cf3f6fc 100644 --- a/app/config.py +++ b/app/config.py @@ -21,9 +21,11 @@ client_name=os.getenv("client_name") + ".exe" JAVA_API_BASE = os.getenv("java_api_base", "http://127.0.0.1:18080") # 紫鸟浏览器配置 +from urllib.parse import unquote ZN_COMPANY = os.getenv("zn_company", "") ZN_USERNAME = os.getenv("zn_username", "") -ZN_PASSWORD = os.getenv("zn_password", "") +ZN_USERNAME = unquote(ZN_USERNAME) +ZN_PASSWORD = os.getenv("zn_password", "#20zsg25") # 删除品牌API配置 DELETE_BRAND_API_BASE = os.getenv("DELETE_BRAND_API_BASE", JAVA_API_BASE) diff --git a/app/main.py b/app/main.py index 4cd2924..955832a 100644 --- a/app/main.py +++ b/app/main.py @@ -7,6 +7,7 @@ import json import sys import shutil import os +from amazon.main import TaskMonitor os.makedirs(cache_path,exist_ok=True) if not debug: today = datetime.datetime.now().strftime("%Y_%m_%d") @@ -301,11 +302,29 @@ def start_flask(): run_app(host='127.0.0.1', port=PORT) +def start_task_monitor(): + """启动任务监控器(在独立线程中运行)""" + monitor = TaskMonitor() + try: + print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 任务监控线程已启动") + monitor.start() + except Exception: + import traceback + print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 任务监控线程异常退出: {traceback.format_exc()}") + + def main(): if not os.path.exists(cache_path): os.makedirs(cache_path,exist_ok=True) + + # 启动 Flask 服务 t = threading.Thread(target=start_flask, daemon=True) t.start() + + # 启动任务监控线程 + monitor_thread = threading.Thread(target=start_task_monitor, daemon=True) + monitor_thread.start() + # 等待 Flask 启动 time.sleep(2)