From 4e5ed76cb6bd7000ae6aa76833501f78af31f928 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: Fri, 17 Apr 2026 10:04:24 +0800 Subject: [PATCH] modified: amazon/__pycache__/approve.cpython-39.pyc modified: amazon/__pycache__/del_brand.cpython-39.pyc modified: amazon/__pycache__/match_action.cpython-39.pyc modified: amazon/approve.py modified: amazon/del_brand.py modified: amazon/match_action.py new file: amazon/price_match.py modified: main.py --- app/amazon/__pycache__/approve.cpython-39.pyc | Bin 26738 -> 32208 bytes .../__pycache__/del_brand.cpython-39.pyc | Bin 17688 -> 18508 bytes .../__pycache__/match_action.cpython-39.pyc | Bin 18706 -> 18786 bytes app/amazon/approve.py | 413 +++++++++++++++--- app/amazon/del_brand.py | 68 ++- app/amazon/match_action.py | 17 +- app/amazon/price_match.py | 333 ++++++++++++++ app/main.py | 16 - 8 files changed, 749 insertions(+), 98 deletions(-) create mode 100644 app/amazon/price_match.py diff --git a/app/amazon/__pycache__/approve.cpython-39.pyc b/app/amazon/__pycache__/approve.cpython-39.pyc index 694a2d5cee1d64f680bb9ed8b176ebd347fd507a..b1fa641ac02a44dcd9e42eaa70b176431d6509a6 100644 GIT binary patch delta 11734 zcmbVS3v^S*nZEbxX<3%#w=o86V}osM^9V@@?{^>shd`Q#2vv;df@RCHJ(2=euPUfX zfCPfcC25*Ku?cxLKoZl0LI~SUcem}HZIf*GG}U(7o(=M2&o-qtV=%fByO3Gyi|}-|x%s|55g|x_xf(Y!BvidVHSFTwks%#oU0FgI=Ha zJ}FdemDIf6$7G5Ane6jEAcd4U((Mv+Fy}srIo0ewi!UF~STA0Et z`y`*0j7Cv2>dfANkFrZnI49!4#)!v%N~^DwunWuL2B*}g-0S*mP3k+ z*aqy(lNh`f(8_WR;69*Y?zkNvN5sLrWSsr>Z9j}VV@=pnVRHHenZ*n!xbavtg>@TbQaX_CKJpc$^G8m4JRM zi8wTSdK~1~w*76~JDay7TfYiX%r=xecB7%t4%-t&=W)7+CJ$fXQ&$zjlF(w#JNZ_hb z%M)B#S(BDGkjG{Z3NdMUuNA3E&Lwf zo~ISE(h^eBO(!fFyPr$u36)tAJ==x7meN4KrH9Ug`3N9c{g&QmOq(qOn)zBbqMd7@wWmB(YN$z&EO>ytgwuiaQ(}wTOjVTveW@u4E58F9E+pcwQ}d#P>BW-( zuvl2?k`opzh0B)g7iVh16Y2|FC(Zixu6KJ;f91wF#zm1o}8OtCOTTeyE zH9>)Z$!xV6Ta}5e%KB~L#W=)FqT-Xa$*?S8kO?zrPBz4-S;z~(R}oudN@j{SB@JxC zPBCBw)LIj2sy6jHW1V^f#@kKUY1*_5#)J{Gry0N*+SeJjN;C^0r?;udK&%&Ps>rAb zJ)tH$KO|P%VcJ2ZRtY~TjaO+^<{lLHMR-O$oR`olOkAg3A#hiQt?T$EA$;B=^g)(gPBX1PjB7 zjU#~$0?acz?l9{TICtzgfvoK|ijFKx9O1ZQ;;4RBEX&3?EhDc)029j&;E*>~5#w%S zm3dKQq5Ozrz{zqbqNT?)iFttWKLy6z2^bS;l*#`GO0T&8HVLG8CQ>WN#HEqQ#9`W` zS-+T2XxW<(YLcOVo}`PStDwxp`1jWxm=8oOI|s_B`kZDW+Da4$7v zBx8kW)^fVXak?OXR!cYw+UDOgrr_#?eA_ysh4Gc{mBmH6G%T};h-YQ)`^uC;s#nh)^_FGV9MPT3<{@t?jH7s@o^ zmJ4N<;zIx0Cl}^Cm~+csxk|siU{Ru#u%!P;V3chLM+>3@gM+J?DS`21%_Io2Mz;?e zoU1J&i)10wXEwU}iQ9=7gI8cgBoOdIBY_ZOUTMIV2?NgN&l;hmLjAqMX?9ZZXKT#I zBK=fRc|z#hP<5SsT+B&lYg-`5xEeB23L)zfEU#0N*{#8@uW}lIH4Uz8PP!4-FgBKd3F7LDtOF za7UyI7R|d@v^CP$-4zV9_EuH2gjmJ6`Bn8cB5Mh%jl64D<1Qm0R&9fau)xFDSm}*m zjUhE0QJIO6wM1GPBN$10(--W&O-MFMnbz>}SBDS2n>z6N@R9zj7f+{7?;GiVGg35!-#L^za&ENm^4Q>a$4;CY*4Gp^CUUVR+Jwe%`kO~ydNS4jMC#GQ!>8o6h|(g!r^6J5tg9DaLdZ%T9vnHiA0cb_sn^H$ANeZ) zQwP2|I{5M!MV=^f)r4fQH+<;e*pp`sIi-#q9XWO~b>x}Up{Ge^dyfyFIb{ekHqYR= zY4+@vt;=sv4_8~l;JQ-{hbiO*1EG$pZFTY=DEoylM~*+9di2uBB|Y`T$<%WLbVepH zWBiftQWAR;KH*|CnzYRmhhH9$9+vLKN!z1{vsP{v!Q>SssWi>uYXMj%CmrUN<8daL zwRoo*Qg?UrrH$C(x2))lb%oRw-LT(>)uy(VkiNB`JduH{vqUEYE_L9ou{{Hj z_UO46M=m`&y7$<~&?Vz^h>~-To_R5K^s&^Fhf-$`LR;nxtsFgna`gQ3S1(??apgTx z;tpDj(sckTqb{xaII-vG)eDEmj_(r6)-9~rxUgo60W6#&<7nnSbc`AM=${r$Nkr)^8Gdtc`1nD?nXZG+F#72`d-)c? z@_W!kDRhzbzx4R9e)#I;<56KiqC4{JzV)jsu3oqZ(vnJm@lCXq2O+*Yk^#3rL@-i$_vN-cI}9=Nd@HODh_eMu}=5n4uRJ z*RS9dfBw?ik;mQ|-m@2L`8t9Y>>Pj9kdnTo__kZ4b1)A6zi@M58-|&UTzYBb(itF& ztq^QnD1Ls6(AAf%7xFjmL(vLjudzJ`haZ3C>Xj2PIpS}E`2qKBg2TR&!YVrXf%lBN1DN@k+eK{uhg$;Np|dL2J4jf=|oRb(E`(rGC!B|?-*OG~JilUkCN zP}i=cGaTVNB7shoe}&-4g_G8nNQCnSdR|G-HoyeBIo%w%T&J%tot2nwRPo66lG$x) z3k#~oU5BnPoDLhG$2(|g z*_*VPtIR;=4+avU4qtXiRhb`qhk~ll74AlWWpowr<(Syf2cD#B{E^>F)RFvr+1RhO z!{6E!4MluK*Ip6)>h3@|5>CqPJda>qVy8f;I~wu1BP|@^8#lW^1p8(O!v0mrZUUjG zN*qS~VE_OOUy*3oA5^!iLH(oB+jHoaSEul8L_xk@KWSRx19Zdl=(LeN)7`mVg>H3j z-0JYHcx-g@^T>+g5jQG;-cuZD2f}yba8Y6C?8|hRxFq83TpK%;;gp`+vj!~8 zjf#vDuuc>9f`)97s-STvYE=s6kS%Hsl+1E9XZjrg>SA+{t7YT$19G!C-m##xE-|l` zmCn@?AT#UhS)!JUZ%WnxkS8iQ2DvC0GdogQ7sd-gfU8eV7uAd>kWOz@?!Y{%c$5P6 zJ9{L}#fq8q2*?*T-w)&2TK0gO;T;M{b_2Fl6s{7J%mSap%EZ8A<5_cS7E#$f9g)O% zxlzYMaphh%g-yj9ooQ6%jpwk60hELx9S=6Juu9ECB|2mB3Yx(WA_B_1*a4@r!Bn{-#Vo?LY3n!qp#Lk=cQco{Rpa9e#Mdg<0 zw(#?8mX-$zS@?sjAzo<0R%(UBy}6>uB)<`LY%7&mN$WwSGf+*&stw{8quaHnzFO=g=uvp*d3-xq%FU4oQyBAd^?{5 zehS+sSpEPkpOgpUeIn@Ed!IImUykiIAhO&UnLJRAQic`2geRDwD5YkCiCH~EASRn0 zf0`Jcv2f${J^=L1B1+z6FcVuSg#B0eRl1aJa-R{rqj$yx!EgF9!B22MmnrP=Lrv*- za8!oHiI6_r?uNZkW!uX(4<_XJbhbsCj!5Rz!>~lr z5`7eu9~kNqt zjf|_pNZ9(1kDNb+Of$9r;+73xG17Ye003hzW>Q0{(9lJ(B@hbN>WxmPzM-mEudU)U zV&3{||0-hn71xg7Nq@F#bK;ufH|6ypM(^Y;aW%gd&S|lM~`Gn=D(qjeFN~{od z1c$FnRo%99u6JWm1$*DP!mJKa-U3p; za8csgO1QDAVPYbVQcG5V`MbD36zxQjFbIrM%AdXr?R8j1H2{f!rsiSh_%};K>uOFa zwlPY%hel=&D*99P^H;`-ZZI+$h?1>W808WpC;eP78q|MO|8Al}HtD zxTydoa!Dhrq_i+;ZSM+%kXtflEQgyD6*0W|IExhB6!PrQU^xn)wA>?QRKhzsOf9gY@t2_Ia-# zOgcj9Ewb72o2e4dHGtr0!exUlFWBzEELBD-@U5wePONdJe%scl{) za!$pIT#Hf~yONJzyihJi=^Eu+F3fq5^iXDnL{FrA^zEh|vNbPW47e1l31t-t9l<(9 z+DENN@#0^f))Yh*<<$>0)hhTp-{iN*e4U;5ChHMkb>`>+0U(52bA^E0L&XPrhhkoWG3I}x#Bi(&U zzqn|QyjUM(1$GL)|ELUE7gx%~1L!9mJQ@nYp^+O?nT`+Wi#ZO%-PZt5QQ4#h!IG1A|DhPwT&4I#XF{8FDWBr3J4Dp|aG< zEA~MGen@|H)vI=j?);+eTfJ6(Lmyl{O@2}T{^~{c=VNdf(FjsPSVY78P1}(49z^iDIjz~+m!;hleYczC*nzMTG z+G@M7*fxFn+KQ45D1(L{#*}2I)S9XR__yl2);{9>VP@Iii?u`U+iUElm#P0Q8oUib zm`81=8AWy|dHu%uMdvWyoOHKFIo_Y3GSSx64c?#J*kSN~MYlBnzMP~bii&tUFmm#R zv18vFJ$H`3KzLu(3+_5;f1bJ<^b2<_b6iAsEx)Ay`L5;i-{{LWEs~q{uWedhH!d46 zAc^YI1O?&DFwdwB74femo^a+c;bn|}|{VQSnm>MB?+M1soTD9djcKLO^8owZXPG9f4#rsb* zE^O)w^?srs@hz0k=|A?h*o82g^|@OY$lul5w}#}K^j~bs$Wguj!5Nir z(?Fb>3us|aDo@>L^3)>AL@Hqxl#eHyK}jhb3&4pH+Fnz{nRkgt=1-4KKRoo$sHe+jUpCoHgM zWchD|z%Xs}d^sxTI2qHgA}B!na=5w!`f2lvIew5iwT6`J>cdhX!vA-=O|wy?*uUA;c|Q$}(U7QB23kASNLv>(+>dN3X=Q=d z2tP$|#3=u<7^KT0pG(i=BjTXoq_3oPyREA$$UmY3#KDhZ=!&9`s8!jU3loyJI3eZm zqk7?vC98i*(9URB<-&2v6Gh2>tnbX#i_eZeHpHJK2<72he0i%?T(^UpaK3L=JMmtM ziI+5?=Glj@da4MzENTHM~tv?WoW3Wp($qL?c~0qXGV&p0{&t;y5A9Uf z#UpFT5)HbENJBsa)FYdw-xQE{^m1YG|4ic|p8N-TK6`ir5J3dnObUuZ<*Hq$<7%$4`GmA=F`dT)TgzT=F%BE*F!D+Z`4<|&2UwV K6W)s#*8c}iTn^a) delta 6645 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 Hj}ZAkw)#T5 diff --git a/app/amazon/__pycache__/del_brand.cpython-39.pyc b/app/amazon/__pycache__/del_brand.cpython-39.pyc index 8551dba5aa59039591424dd554590a51627a4257..0e5aacf5b2585477caac6042e2f30e0c05800b31 100644 GIT binary patch delta 1821 zcmZ`(Yj9IV6y9_5x=GVCw6r`dCB`z)p`|z~l=3RH)^Qve2UM_HJ83u2gmRPay_ZKx zNg?uVYtvbVM^Tzk@HNgvCg?Cw&|i)}9Dn)8j{YM_%MY1h^uK3sQYcz;C*R#Y-#OwELw=A#bN0(2+Cf?xLoK`&#C1^^2_`(E0XIfg3kf1j` zvGNswb?M(${qBIl^mi}ql_1BLt!s)Fp}Z)(8UN}DiIBL|*Mb$2AOs}}RHCL~32Qe| za|~LLZTI$@4BkvF)Jko`QomWVM3ANOelyz{F;jU730fmS?bIP+3*JQqZwq=`3f_Iu zh()+3Y)MjZd`7kq>MVp>HJKJ;xHSebQIs( zMMW_ct_s?#`)vg=H?CT&O2(86P(pqph9e;smsn{;HdYD2B3e49M$^`Z&N-Q>x zo=lfS>_*8h(K!*7D8ZgbiLwx#mI|l*vzt)+E=8j88B0%~bK>8|C={apdqBKbs5uv!7tbUJBB#${1NB+H*tR+;!&!ch& zi{qJWWv3Bm%n1DEdN26+uj?D5`c9l54*Rt3#`=0S>{H%v3ee47pRRS+seXmlb%m(k z>+vbn(-l%QUqDe;sTvC?`&O-~sr6YVcI*T0sVQxCv6~%YH>V)j<_d*1V^Z+68-41)KCym2T3*&^=?U!% zD1QUqWqU9y0W*|>3*6wuw?KSLZnQW-#=iq9B*D7yS%~v$wC35omzX&v^pxBt*<@tl zsST}FpJHlO@+`Q;^d!QWbXnsq2`=)VHtm7>^z)mYU`wFAm5uY4w+zC0{==5>@{7X# zNjF(iOfPV4>l(PmC$_$U7x<&CL4a}Iw(VMx!5W)%k=wTSz`I=A{&wSpi1|#wmjdRi z=PDAlc&V#04rDM(@&dVnUFA}9tM!Tq8sj^f>)|>-*8C$}<*&A^-gsS@M-bYq>Y)x* o?_y7NZV1je2gd0zsr&W3_7{RE5nI6Tx4dZ4+)=WE|J73c58j0q4FCWD delta 904 zcmZWmO-vI}5Pq}WZI}LRTae#?63_@r0DmA*ApE!f1TRQX)R2aU($bW=-Kr(6B-Vrk z0@h4K1e6w{5>M)yc=6)JlZi(YHC#-H@n|@D@VyNXjhpQE=FNOF-A#qV%f z3H#pO{Tf)h^h)U~g%bU8sRC|UGf^c4KoTTq=_l3B^;DUFKM%YE4?Zt%)3?fh2+*#7 zs$2)~>(N6Q0V4Xoss?}t{Y>>YE6nJJPhJ&Z3+Edt@vre@#kpG|-|cOQ@t z{}%)@NT}3LG)G3Et}L6{^?9Sszw%D+CJvHtqewJo8sZ)xNeht4QYPlsTp4$k6cRr4 zL2{8$7Q1Z;v&ZqXp41a+WsAr?hgx$5jNrp&4~9ME4)qpWpjVw{sHX9SXF%M~OiZ*? zm~zzueA!Y`H^XDI9O4YV4rZP8&$_Rp_2jjohShrmDXacX)P*H zCL-GK$OIi74um5yO|9X)B;L=@Ym!0}*8<^fw%KSc^AZrj1$M|a&<=LNWmrWZvr}df z{xb%CZ}q|w9%(CuY2DZMMt}wU-F^Yw`ap*rERR_!ueyR`owIY5@lR-HQd2UDlr_5c6? diff --git a/app/amazon/__pycache__/match_action.cpython-39.pyc b/app/amazon/__pycache__/match_action.cpython-39.pyc index 7d422023faded7214fc3bb0dfdbbc3754ccf5aed..cf06b6fd20432e633a659bf27e05cf3dd8f4a495 100644 GIT binary patch delta 5297 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 X?3`r>JM$o-67lS|CgK$DZ)^G=2FNxW delta 5238 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 diff --git a/app/amazon/approve.py b/app/amazon/approve.py index 5577327..ce9a933 100644 --- a/app/amazon/approve.py +++ b/app/amazon/approve.py @@ -2,6 +2,7 @@ import time import re import traceback from DrissionPage._pages.chromium_tab import ChromiumTab +import pyautogui from config import runing_task, runing_shop from datetime import datetime @@ -86,10 +87,11 @@ class AmzoneApprove(AmamzonBase): def wait_loaded(self): # 等待加载完成 try: - load_ele = self.tab.ele( + load_ele = self.tab.eles( 'xpath://kat-panel[@data-testid="kat-panel-ActionPanelContent"]//div[contains(@class,"Loader-module__loader")]|//kat-panel[@data-testid="kat-panel-ActionPanelContent"]/div[@data-f1-component]//div[contains(@class,"==")]/div[contains(@class,"==")]/span', timeout=3) - load_ele.wait.deleted(timeout=5, raise_err=False) + if len(load_ele) > 0: + load_ele[0].wait.deleted(timeout=5, raise_err=False) except Exception as e: print("等待加载中消失出错", e) @@ -106,67 +108,317 @@ class AmzoneApprove(AmamzonBase): print("当前操作的tab_id",self.tab.tab_id) self.browser.close_tabs(close_tab) - def handle_repair_product(self,tab:ChromiumTab): - def wait_loaded(): + # def handle_repair_product_old(self,tab:ChromiumTab): + # def wait_loaded(): + # # 等待加载完成 + # try: + # load_ele = tab.ele( + # 'xpath://div[@class="contentWrapper"]//div[contains(@class,"==")]/span[not(node())]', + # timeout=3) + # load_ele.wait.deleted(timeout=5, raise_err=False) + # except Exception as e: + # print("等待加载中消失出错", e) + + # tab.wait.doc_loaded(timeout=60,raise_err=False) + # submit_btn_ls = tab.eles('xpath://div[@id="ahd-product-policies-table"]/div//div[@data-testid="nextStepsMetricWrapper"]//kat-button[@label="提交"]',timeout=10) + # if len(submit_btn_ls) == 0: + # print("未找到提交按钮,可能页面未加载完成或者页面结构发生变化") + # # return + # for _ in range(3): + # try: + # # todo 遍历 + # for index in range(len(submit_btn_ls)): + # if index > len(submit_btn_ls)-1: + # print("提交按钮数量发生变化,停止处理") + # break + + # submit_btn = submit_btn_ls[index] + + # # 关闭按钮 + # close_x_btn = tab.eles('xpath://div[@class="flyoutPanelContent"]/span',timeout=5) + # if len(close_x_btn) > 0: + # close_x_btn[0].wait.displayed(timeout=5, raise_err=False) + # close_x_btn[0].click() + + # submit_btn.wait.enabled(timeout=5, raise_err=False) + # submit_btn.click() + # time.sleep(0.5) + # self.wait_loaded() + + # wait_loaded() + + # need_input = tab.eles('xpath://div[@class="contentWrapper"]//kat-input[@data-testid="kat-input-dew:ump_epr_resgitration_number_title"]',timeout=5) + # if len(need_input) > 0: + # print("需要输入注册号,不符合操作要求,跳过...") + # continue + + # target_title = tab.eles('xpath://div[@class="contentWrapper"]//section//h4[text()="警告和安全信息"]') + # if len(target_title) > 0: + # not_start = tab.eles('xpath://div[@class="contentWrapper"]//div[@aria-label="安全证明"]//kat-label[@text="未开始"]') + # if len(not_start) == 0: + # print("安全证明-不是未开始状态,不需要操作") + # continue + + # not_start.wait.displayed(timeout=5, raise_err=False) + # not_start.click() + + # wait_loaded() + + # check = tab.ele('xpath://div[@class="contentWrapper"]//kat-checkbox[@name="value"]') + # check.wait.displayed(timeout=5, raise_err=False) + # check.click() + + # save_btn = tab.ele('xpath://div[@class="contentWrapper"]//kat-button[@variant="primary"]') + # save_btn.wait.enabled(timeout=5, raise_err=False) + # save_btn.click() + + # wait_loaded() + + # close_btn = tab.ele('xpath://div[@class="contentWrapper"]//kat-button[@label="关闭"]',timeout=10) + # close_btn.wait.displayed(timeout=5, raise_err=False) + # close_btn.click() + + # save_btn_ls = tab.eles('xpath://div[@class="contentWrapper"]//kat-button[@variant="primary"]',timeout=5) + # if len(save_btn_ls) > 0: + # option_selection_ls = tab.eles('xpath://div[@class="contentWrapper"]//div[@role="option"]',timeout=5) + # for option in option_selection_ls: + # option.click() + # time.sleep(0.5) + + # save_btn_ls[0].wait.enabled(timeout=5, raise_err=False) + # save_btn_ls[0].click() + + # wait_loaded() + + # close_btn = tab.ele('xpath://div[@class="contentWrapper"]//kat-button[@label="关闭"]',timeout=10) + # close_btn.wait.displayed(timeout=5, raise_err=False) + # close_btn.wait.enabled(timeout=5, raise_err=False) + # close_btn.click() + + # submit_btn_ls = tab.eles('xpath://div[@id="ahd-product-policies-table"]/div//div[@data-testid="nextStepsMetricWrapper"]//kat-button[@label="提交"]',timeout=10) + # break + # except Exception as e: + # print(f"【handle_repair_product】处理修复商品信息异常", traceback.format_exc()) + # tab.refresh() + # tab.wait.doc_loaded(timeout=60,raise_err=False) + # time.sleep(2) + + # print("修复商品信息处理完成!") + # tab.close() + + def handle_repair_product(self,tab:ChromiumTab,headle_type=""): + def wait_loaded(wait_type="contentWrapper"): # 等待加载完成 - try: - load_ele = tab.ele( - 'xpath://div[@class="contentWrapper"]//div[contains(@class,"==")]/span[not(node())]', - timeout=3) - load_ele.wait.deleted(timeout=5, raise_err=False) - except Exception as e: - print("等待加载中消失出错", e) + print("------------【修复商品信息】等待加载完成-----------------") + for _ in range(3): + try: + load_ele = tab.eles( + 'xpath://div[@class="contentWrapper"]//div[contains(@class,"==")]/span[not(node())]|//kat-spinner[@id="ahd-app-page-spinner"]', + timeout=10) + if len(load_ele) > 0: + load_ele[0].wait.deleted(timeout=20, raise_err=False) + if wait_type == "contentWrapper": + contentWrapper = tab.eles('xpath://div[@class="contentWrapper"]//h4',timeout=20) + if len(contentWrapper) > 0: + contentWrapper[0].wait.displayed(timeout=20, raise_err=True) + break + else: + time.sleep(2) + break + if wait_type == "content": + contentWrapper = tab.eles('xpath://div[@class="contentWrapper"]',timeout=20) + if len(contentWrapper) > 0: + contentWrapper[0].wait.displayed(timeout=20, raise_err=True) + break + else: + time.sleep(2) + break + if wait_type == "section": + section = tab.eles('xpath://section[@class="ahd-product-policy__section"]',timeout=20) + if len(section) > 0: + section[0].wait.displayed(timeout=20, raise_err=True) + break + except Exception as e: + print("等待加载中消失出错", e) + print("-----------------------------") + + def close_contentWrapper(): + # 关闭按钮 + # close_x_btn = tab.eles('xpath://div[@class="flyoutPanelContent"]/span',timeout=5) + # if len(close_x_btn) > 0: + # close_x_btn[0].wait.displayed(timeout=5, raise_err=False) + # close_x_btn[0].click() + # time.sleep(1) + # close_x_btn[0].wait.deleted(timeout=5, raise_err=False) + close_x_btn = tab.eles('xpath://div[@class="flyoutPanelContent"]/span',timeout=5) + contentWrapper = tab.eles('xpath://div[@class="flyoutPanelContent"]/div[@class="contentWrapper"]',timeout=5) + if len(close_x_btn) > 0 and len(contentWrapper)>0 : + if len(contentWrapper[0].children()) > 0: + close_x_btn[0].wait.displayed(timeout=5, raise_err=False) + close_x_btn[0].click() tab.wait.doc_loaded(timeout=60,raise_err=False) - submit_btn_ls = tab.eles('xpath://div[@id="ahd-product-policies-table"]/div//div[@data-testid="nextStepsMetricWrapper"]//kat-button[@label="提交"]',timeout=10) - if len(submit_btn_ls) == 0: - print("未找到提交按钮,可能页面未加载完成或者页面结构发生变化") + + wait_loaded(wait_type="section") + # product_policy = tab.eles('xpath://div[@data-testid="ahd-desktop-product-policy"]') + product_policy = tab.eles('xpath://div[@data-testid="ahd-desktop-product-policy" and @class="ahd-product-policy-table-row-wrapper"]|//div[@class="ahd-product-policy-table-row-wrapper-nested" and @data-testid="ahd-desktop-product-policy"]') + if len(product_policy) == 0: + print("未找到提交处理区域,可能页面未加载完成或者页面结构发生变化") + time.sleep(5) + # product_policy = tab.eles('xpath://div[@data-testid="ahd-desktop-product-policy"]') + product_policy = tab.eles('xpath://div[@data-testid="ahd-desktop-product-policy" and @class="ahd-product-policy-table-row-wrapper"]|//div[@class="ahd-product-policy-table-row-wrapper-nested" and @data-testid="ahd-desktop-product-policy"]') + + print("等待5秒后再次查找提交处理区域:",len(product_policy)) + + # submit_btn_ls = tab.eles('xpath://div[@id="ahd-product-policies-table"]/div//div[@data-testid="nextStepsMetricWrapper"]//kat-button[@label="提交"]',timeout=10) + # if len(submit_btn_ls) == 0: # return + now_index = 0 + submit_index = 0 for _ in range(3): + try: + # 先全部点开 + chevron_right_ls = tab.eles('xpath://kat-icon[@name="chevron-right"]',timeout=10) + for ele in chevron_right_ls: + ele.click() + time.sleep(0.5) + except Exception as e: + print("【修复商品信息】全部点开失败",e) try: # todo 遍历 - for index in range(len(submit_btn_ls)): - if index > len(submit_btn_ls)-1: - print("提交按钮数量发生变化,停止处理") + print("【商品修复产品卡片】数量",len(product_policy)) + print("【商品修复产品卡片】列表",product_policy) + for index in range(now_index,len(product_policy)): + product_policy = tab.eles('xpath://div[@data-testid="ahd-desktop-product-policy" and @class="ahd-product-policy-table-row-wrapper"]|//div[@class="ahd-product-policy-table-row-wrapper-nested" and @data-testid="ahd-desktop-product-policy"]') + now_index = index + if index > len(product_policy)-1: + print("提交按钮区域发生变化,停止处理") break + message_link = product_policy[index].eles('xpath:.//a[@data-testid="ahd-reason-message-link"]') + message = message_link[0].text - submit_btn = submit_btn_ls[index] + if headle_type in ['查看冻结原因']: + need_heanle = any(i in message for i in ["制造商详细联系信息","警告和安全信息","负责人详细联系信息"]) + else: + need_heanle = True + + if not need_heanle: + print(f"【{headle_type}】【{message}】不需要处理,跳过") + continue + spread_btn = product_policy[index].eles('xpath:.//kat-icon[@name="chevron-right"]',timeout=5) + print(f"{index},是否存在展开按钮",len(spread_btn)) + if len(spread_btn) > 0: + spread_btn[0].click() + time.sleep(0.5) + + submit_btn_ls = product_policy[index].eles('xpath:.//kat-button[@label="提交"]',timeout=5) + # try: + # submit_btn = product_policy[index].ele('xpath:.//kat-button[@label="提交"]',timeout=5) + # except Exception as e: + # print(datetime.now().strftime("%Y-%m-%d %H:%M:%S") ,"没有查找到提交按钮,跳过!") + # close_contentWrapper() + # continue + # print("提交按钮数量:",len(submit_btn)) + print("提交按钮列表:",submit_btn_ls) + if len(submit_btn_ls) > 0: + print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"),"查找到提交按钮") + # submit_btn = submit_btn[0] + submit_btn = tab.eles('xpath://kat-button[@label="提交"]',timeout=10)[submit_index] + submit_index += 1 + else: + print(datetime.now().strftime("%Y-%m-%d %H:%M:%S") ,"没有查找到提交按钮,跳过!") + close_contentWrapper() + continue + # time.sleep(10) # 关闭按钮 close_x_btn = tab.eles('xpath://div[@class="flyoutPanelContent"]/span',timeout=5) - if len(close_x_btn) > 0: - close_x_btn[0].wait.displayed(timeout=5, raise_err=False) - close_x_btn[0].click() + contentWrapper = tab.eles('xpath://div[@class="flyoutPanelContent"]/div[@class="contentWrapper"]',timeout=5) + if len(close_x_btn) > 0 and len(contentWrapper)>0 : + if len(contentWrapper[0].children()) > 0: + close_x_btn[0].wait.displayed(timeout=5, raise_err=False) + close_x_btn[0].click() + print("开始操作",submit_btn) + print("操作的ID 为:",submit_btn.attr('id')) submit_btn.wait.enabled(timeout=5, raise_err=False) submit_btn.click() - time.sleep(0.5) - self.wait_loaded() + + # 先直接等待contentWrapper出现,不要进行复杂的元素查找 + try: + # 使用简单的等待,避免触发页面事件 + for check_count in range(10): + contentWrapper_exists = tab.eles('xpath://div[@class="contentWrapper"]', timeout=2) + if len(contentWrapper_exists) > 0: + print(f"【弹窗已出现】第{check_count+1}次检查") + time.sleep(2) # 再等待2秒让内容完全加载 + break + time.sleep(1) + except Exception as e: + print(f"等待弹窗出现异常: {e}") + + # wait_loaded(wait_type="content") + + for j in range(4): + if j == 0: + timeout = 5 + else: + timeout = 10 + contentWrapper = tab.eles('xpath://div[@class="contentWrapper"]//h4',timeout=timeout) + print("标题的数量",len(contentWrapper)) + if len(contentWrapper) > 0: + contentWrapper[0].wait.displayed(timeout=20, raise_err=True) + break + else: + # 关闭按钮 + close_contentWrapper() + time.sleep(2) + submit_btn.click() - wait_loaded() need_input = tab.eles('xpath://div[@class="contentWrapper"]//kat-input[@data-testid="kat-input-dew:ump_epr_resgitration_number_title"]',timeout=5) if len(need_input) > 0: print("需要输入注册号,不符合操作要求,跳过...") + close_contentWrapper() continue - target_title = tab.eles('xpath://div[@class="contentWrapper"]//section//h4[text()="警告和安全信息"]') + target_title = tab.eles('xpath://div[@class="contentWrapper"]//section//h4[text()="警告和安全信息"]',timeout=10) + print(f"序号{index}【警告和安全信息】数量 ->",len(target_title)) if len(target_title) > 0: - not_start = tab.eles('xpath://div[@class="contentWrapper"]//div[@aria-label="安全证明"]//kat-label[@text="未开始"]') - if len(not_start) == 0: - print("安全证明-不是未开始状态,不需要操作") + is_Continue = False + for j in range(10): + not_start = tab.eles('xpath://div[@class="contentWrapper"]//div[@aria-label="安全证明"]//kat-label',timeout=2) + if len(not_start) > 0: + status_lebel = not_start[0].attr("text") + print(f"序号{index}【警告和安全信息】状态 ->",status_lebel) + if status_lebel and str(status_lebel).strip() != "" and status_lebel != "未开始": + print("安全证明-不是未开始状态,不需要操作") + close_contentWrapper() + continue + if status_lebel == "未开始": + is_Continue = True + break + else: + time.sleep(1) + # not_start = tab.eles('xpath://div[@class="contentWrapper"]//div[@aria-label="安全证明"]//kat-label[@text="未开始"]',timeout=10) + # if len(not_start) == 0: + # print("安全证明-不是未开始状态,不需要操作") + # close_contentWrapper() + # continue + print(f"序号{index}【警告和安全信息】是否需要继续 ->",is_Continue) + if not is_Continue: continue - + not_start = not_start[0] not_start.wait.displayed(timeout=5, raise_err=False) not_start.click() wait_loaded() - check = tab.ele('xpath://div[@class="contentWrapper"]//kat-checkbox[@name="value"]') + check = tab.ele('xpath://div[@class="contentWrapper"]//kat-checkbox[@name="value"]',timeout=10) check.wait.displayed(timeout=5, raise_err=False) check.click() - save_btn = tab.ele('xpath://div[@class="contentWrapper"]//kat-button[@variant="primary"]') + save_btn = tab.ele('xpath://div[@class="contentWrapper"]//kat-button[@variant="primary"]',timeout=10) save_btn.wait.enabled(timeout=5, raise_err=False) save_btn.click() @@ -175,8 +427,11 @@ class AmzoneApprove(AmamzonBase): close_btn = tab.ele('xpath://div[@class="contentWrapper"]//kat-button[@label="关闭"]',timeout=10) close_btn.wait.displayed(timeout=5, raise_err=False) close_btn.click() + print(f"序号{index}【警告和安全信息】操作完成") + continue - save_btn_ls = tab.eles('xpath://div[@class="contentWrapper"]//kat-button[@variant="primary"]',timeout=5) + save_btn_ls = tab.eles('xpath://div[@class="contentWrapper"]//kat-button[@variant="primary"]',timeout=10) + print(f"序号{index}【选择保存】是否存在->",len(save_btn_ls)) if len(save_btn_ls) > 0: option_selection_ls = tab.eles('xpath://div[@class="contentWrapper"]//div[@role="option"]',timeout=5) for option in option_selection_ls: @@ -187,29 +442,39 @@ class AmzoneApprove(AmamzonBase): save_btn_ls[0].click() wait_loaded() + close_contentWrapper() + print(f"序号{index}【选择保存】操作完成") + continue - close_btn = tab.ele('xpath://div[@class="contentWrapper"]//kat-button[@label="关闭"]',timeout=10) - close_btn.wait.displayed(timeout=5, raise_err=False) - close_btn.wait.enabled(timeout=5, raise_err=False) - close_btn.click() submit_btn_ls = tab.eles('xpath://div[@id="ahd-product-policies-table"]/div//div[@data-testid="nextStepsMetricWrapper"]//kat-button[@label="提交"]',timeout=10) + if len(submit_btn_ls) > 0: + submit_btn_ls[0].click() + submit_btn_ls[0].wait.deleted(timeout=5, raise_err=False) + wait_loaded() + + close_contentWrapper() break except Exception as e: print(f"【handle_repair_product】处理修复商品信息异常", traceback.format_exc()) tab.refresh() tab.wait.doc_loaded(timeout=60,raise_err=False) time.sleep(2) + wait_loaded(wait_type="section") + # product_policy = tab.eles('xpath://div[@data-testid="ahd-desktop-product-policy"]') + product_policy = tab.eles('xpath://div[@data-testid="ahd-desktop-product-policy" and @class="ahd-product-policy-table-row-wrapper"]|//div[@class="ahd-product-policy-table-row-wrapper-nested" and @data-testid="ahd-desktop-product-policy"]') + print("修复商品信息处理完成!") tab.close() + def run_page_action(self): print("开始执行") num = 0 retry_num = 0 already_asin = set() - + now_page = None while retry_num < 3: # 最多重试3次 # if num > 3: #测试 # return @@ -235,20 +500,27 @@ class AmzoneApprove(AmamzonBase): print(f"当前页码: {current_page} / 总页数: {total_page}") except Exception as e: print("获取页码失败", e) - + print("==========商品风险解决==============") sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=10) print(f"获取到 {len(sku_ls)}") + if len(sku_ls) == 0: + print(f"搜索不出SKU停止") + break # for sku_ele in sku_ls[0:2]: - for sku_ele in sku_ls: + for sku_ele in sku_ls[1:]: # solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]') asin = sku_ele.ele('xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]').text + print(f"*************** {asin} *****************") print(f"ASIN {asin} 存在问题,正在点击解决...") if asin in already_asin: print(f"{asin} 已经处理过了,跳过") continue - solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]|.//kat-link[@label="修复被禁止显示的商品"]|.//kat-link[@label="解决商品移除风险"]') + xp_ls = [f'.//kat-link[@label="{i}"]' for i in ["解决商品信息问题","修复被禁止显示的商品","解决商品移除风险","查看冻结原因"]] + xp = "|".join(xp_ls) + # solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]|.//kat-link[@label="修复被禁止显示的商品"]|.//kat-link[@label="解决商品移除风险"]') + solve_problem = sku_ele.eles(f'xpath:{xp}') if len(solve_problem) == 0: # //a[text0=”添加缺失的商品详情”] lack_info = sku_ele.eles('xpath:.//a[text()="添加缺失的商品详情"]') @@ -264,6 +536,9 @@ class AmzoneApprove(AmamzonBase): solve_problem[0].click() + label = solve_problem[0].attr('label') + print("【当前类别】",label) + action_panel = self.tab.ele('xpath://kat-panel-wrapper[@data-testid="kat-panel-wrapper-ActionPanelContent"]') action_panel.wait.displayed(timeout=5, raise_err=False) @@ -274,15 +549,34 @@ class AmzoneApprove(AmamzonBase): # 如果存在“请求批准”按钮,则直接返回跳过 request_approval_btn = self.tab.eles('xpath://kat-panel-wrapper[@data-testid="kat-panel-wrapper-ActionPanelContent"]//div[@data-testid="section-header" and contains(string(.),"移除")]',timeout=5) if len(request_approval_btn) > 0: - print(f"ASIN {asin} 存在请求批准按钮,不需要处理,跳过...") - panel_close_btn = self.tab.ele('xpath://kat-panel[@data-testid="kat-panel-ActionPanelContent"]', - timeout=5).sr('xpath:.//button[@class="close"]') - panel_close_btn.click() + # 新增"查看冻结原因" 的情况下,需要点击修复商品详情 + if "查看冻结原因" in label: + repair_produ_btn_xp = '//kat-panel-wrapper[@data-testid="kat-panel-wrapper-ActionPanelContent"]//kat-button[@variant="secondary" and @class="action-button" and contains(string(.),"修复商品信息")]' + repair_produ_btn = self.tab.eles(f'xpath:{repair_produ_btn_xp}',timeout=5) + print(f"{asin}【查看冻结原因】修复商品信息数量",len(repair_produ_btn)) + if len(repair_produ_btn) > 0: + print(f"{asin}【查看冻结原因】修复商品信息数量,开始操作") + self.clear_tab() + repair_produ_btn[0].click() + time.sleep(3) + # 获取最新的tab + new_tab = self.browser.latest_tab + if isinstance(new_tab, str): + new_tab = self.browser.get_tab(id_or_num=new_tab) - action_panel.wait.deleted(timeout=3, raise_err=False) - yield (asin,"请求批准") - already_asin.add(asin) - continue + self.handle_repair_product(new_tab,headle_type="查看冻结原因") + else: + print(f"{i}【查看冻结原因】修复商品信息没有,不需要操作") + else: + print(f"ASIN {asin} 存在请求批准按钮,不需要处理,跳过...") + panel_close_btn = self.tab.ele('xpath://kat-panel[@data-testid="kat-panel-ActionPanelContent"]', + timeout=5).sr('xpath:.//button[@class="close"]') + panel_close_btn.click() + + action_panel.wait.deleted(timeout=3, raise_err=False) + yield (asin,"请求批准") + already_asin.add(asin) + continue # 无需采取任何操作 not_operate = self.tab.eles('xpath://kat-alert[contains(@description,"如果您之前已提交更改,则这些更改当前正在处理中") and not(@dismissed)]',timeout=3) @@ -310,7 +604,7 @@ class AmzoneApprove(AmamzonBase): # TODO 增加 “出现警告和安全信息时候下滑” 的情况 safe_handle_ls = self.tab.eles('xpath://kat-panel[@data-testid="kat-panel-ActionPanelContent"]//div[@aria-label="安全证明"]//kat-label[@text="未开始"]', - timeout=5) + timeout=15) if len(safe_handle_ls) > 0: print("存在安全证明,正在处理...") safe_handle_ls[0].click() @@ -329,17 +623,19 @@ class AmzoneApprove(AmamzonBase): time.sleep(0.5) # 点击保存 - confirm_btn = self.tab.ele('xpath://kat-panel-wrapper[@data-testid="kat-panel-wrapper-ActionPanelContent"]//kat-button[@variant="primary"]') - confirm_btn.wait.displayed(timeout=3, raise_err=False) - confirm_btn.click() + confirm_btn = self.tab.eles('xpath://kat-panel-wrapper[@data-testid="kat-panel-wrapper-ActionPanelContent"]//kat-button[@variant="primary"]') + if len(confirm_btn) > 0: + confirm_btn[0].wait.displayed(timeout=3, raise_err=False) + confirm_btn[0].click() self.wait_loaded() # 等待关闭按钮出现 try: - close_btn = self.tab.ele('xpath://kat-panel[@data-testid="kat-panel-ActionPanelContent"]//kat-button[@label="关闭"]', + close_btn = self.tab.eles('xpath://kat-panel[@data-testid="kat-panel-ActionPanelContent"]//kat-button[@label="关闭"]', timeout=10) - close_btn.click() + if len(close_btn)>0: + close_btn[0].click() except Exception as e: print("点击关闭按钮失败",e) @@ -351,7 +647,7 @@ class AmzoneApprove(AmamzonBase): print(f"ASIN {asin} 存在修复商品信息按钮,正在点击...") self.clear_tab() repair_product[0].click() - time.sleep(1) + time.sleep(3) # 获取最新的tab new_tab = self.browser.latest_tab if isinstance(new_tab, str): @@ -366,6 +662,7 @@ class AmzoneApprove(AmamzonBase): yield (asin,"处理完成") already_asin.add(asin) + print(f"*********************{asin} 处理完成*************************") try: # 全部操作完成,关闭 @@ -556,6 +853,8 @@ class ApproveTask: items = data.get("items", []) country_codes = data.get("country_codes", []) risk_listing_filter = data.get("risk_listing_filter", "") + # 测试 + # risk_listing_filter = "DetailPageRemoved" #data.get("risk_listing_filter", "") if not task_id: self.log("任务ID为空,跳过", "WARNING") @@ -993,14 +1292,14 @@ if __name__ == '__main__': "username": "自动化_Robot", "password": "#20zsg25" } - shop_name = "魏振峰" + shop_name = "郭亚芳" country = "德国" kill_process('v6') driver = AmzoneApprove(user_info) browser = driver.open_shop(shop_name) sw_suc = driver.SwitchingCountries(country) driver.SwitchPage() - risk_listing_filter = "ApprovalRequired" + risk_listing_filter = "DetailPageRemoved" for _ in range(3): try: sku_ls = driver.search(filter_type=risk_listing_filter) diff --git a/app/amazon/del_brand.py b/app/amazon/del_brand.py index f5e453b..fd0a311 100644 --- a/app/amazon/del_brand.py +++ b/app/amazon/del_brand.py @@ -314,6 +314,7 @@ class ZiniaoDriver: self.open_launcher_page(ret_json.get("launcherPage"), self.browser) else: print("IP检测不通过") + raise RuntimeError("IP检测不通过,可能是因为网络环境变化导致的,为了店铺安全不打开店铺") return self.browser def close_store(self, browser_oauth=None): @@ -499,26 +500,55 @@ class AmamzonBase(ZiniaoDriver): def login(self,password,username=""): try: - pwd_input = self.tab.eles('xpath://input[@type="password"]',timeout=10) - if len(pwd_input) > 0: - pwd_input[0].input(password,clear=True) - submit_btn = self.tab.eles('xpath://input[@id="signInSubmit"]',timeout=10) - if len(submit_btn) > 0: - submit_btn[0].click() - self.tab.wait.doc_loaded() + self.tab.wait.doc_loaded(timeout=30,raise_err=False) + for _ in range(4): + pwd_input = self.tab.eles('xpath://input[@type="password"]',timeout=5) + if len(pwd_input) > 0: + pwd_input[0].input(password,clear=True) + submit_btn = self.tab.eles('xpath://input[@id="signInSubmit"]',timeout=5) + if len(submit_btn) > 0: + submit_btn[0].click() + self.tab.wait.doc_loaded(timeout=30,raise_err=False) - opt_code_input = self.tab.ele('xpath://input[@name="otpCode"]',timeout=60) - opt_code_input.wait.displayed(timeout=10,raise_err=False) - for _ in range(30): - if opt_code_input.value is not None and opt_code_input.value.strip() != "": - print("检测到验证码输入完成") - submit_btn = self.tab.ele('xpath://input[@id="auth-signin-button"]',timeout=10) - submit_btn.click() - self.tab.wait.doc_loaded() - return True - time.sleep(1) - submit_btn = self.tab.ele('xpath://input[@id="auth-signin-button"]',timeout=10) - submit_btn.click() + send_code = self.tab.eles('xpath://span[@id="auth-send-code" and contains(string(.),"发送一次性密码")]',timeout=5) + print("发送一次性密码:",len(send_code)) + if len(send_code) > 0: + print("检测到 发送一次性密码") + send_code[0].click() + time.sleep(1) + self.tab.wait.doc_loaded(timeout=30,raise_err=False) + else: + for j in range(5): + opt_code_input = self.tab.eles('xpath://input[@name="otpCode"]',timeout=30) + print("验证码输入框:",len(opt_code_input)) + if len(opt_code_input) > 0: + opt_code_input[0].wait.displayed(timeout=10,raise_err=False) + for _ in range(30): + if opt_code_input[0].value is not None and opt_code_input[0].value.strip() != "": + print("检测到验证码输入完成") + submit_btn = self.tab.eles('xpath://input[@id="auth-signin-button"]',timeout=10) + if len(submit_btn) > 0: + submit_btn[0].click() + self.tab.wait.doc_loaded(timeout=20,raise_err=False) + # return True + error_mes = self.tab.eles('xpath://div[@id="auth-error-message-box"]',timeout=10) + if len(error_mes)>0: + print("验证码输入错误") + print("验证码输入错误提示:",error_mes[0].text) + self.tab.refresh() + else: + print("登入成功") + return True + + time.sleep(1) + + else: + break + + + submit_btn = self.tab.eles('xpath://input[@id="auth-signin-button"]',timeout=10) + if len(submit_btn) > 0: + submit_btn[0].click() except Exception as e: print("登录过程中发生异常:" + traceback.format_exc()) return False diff --git a/app/amazon/match_action.py b/app/amazon/match_action.py index 1ade99a..e2b2b96 100644 --- a/app/amazon/match_action.py +++ b/app/amazon/match_action.py @@ -127,13 +127,13 @@ class AmzoneMatchAction(AmamzonBase): for sku_ele in sku_ls: # solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]') - asin = sku_ele.ele('xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]',timeout=10).text + asin = sku_ele.ele('xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]',timeout=3).text print(f"【{self.mark_name}】ASIN {asin} 找到....") if asin in already_asin: print(f"【{self.mark_name}】{asin} 已经处理过了,跳过") continue - price_match = sku_ele.eles('xpath:.//div[@data-test-id="FeaturedOfferPrice"]//a[text()="匹配"]',timeout=5) + price_match = sku_ele.eles('xpath:.//div[@data-test-id="FeaturedOfferPrice"]//a[text()="匹配"]',timeout=2) if len(price_match) == 0: print(f"【{self.mark_name}】{asin},没有推荐价格匹配按钮") yield (asin,"无需处理") @@ -230,6 +230,9 @@ class MatchTak: stage_index = data.get("stage_index") final_stage = bool(data.get("final_stage", True)) + # 用于测试 + limit = data.get("limit",None) + if not task_id: self.log("任务ID为空,跳过", "WARNING") return @@ -272,7 +275,7 @@ class MatchTak: show_notification(f"开始处理店铺: {shop_name}", "info") try: - self.process_shop(shop_item, country_codes, task_id, risk_listing_filter, user_id, stage_index, final_stage) + self.process_shop(shop_item, country_codes, task_id, risk_listing_filter, user_id, stage_index, final_stage,limit=limit) # self.process_shop(shop_item, country_codes, task_id,risk_listing_filter) # 更新已处理店铺数 @@ -302,7 +305,8 @@ class MatchTak: runing_task[task_id]["error"] = str(e) # def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str): - def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str, user_id=None, stage_index=None, final_stage: bool = True): + def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str, + user_id=None, stage_index=None, final_stage: bool = True,limit:str=None): """处理单个店铺 Args: @@ -420,7 +424,7 @@ class MatchTak: break try: - self.process_country(driver, country_code, task_id, shop_name,risk_listing_filter) + self.process_country(driver, country_code, task_id, shop_name,risk_listing_filter,limit) except Exception as e: import traceback self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR") @@ -449,7 +453,7 @@ class MatchTak: del runing_shop[shop_name] self.log(f"店铺 {shop_name} 已从执行列表中移除") - def process_country(self, driver: AmzoneMatchAction, country_code: str, task_id: int, shop_name: str, risk_listing_filter: str): + def process_country(self, driver: AmzoneMatchAction, country_code: str, task_id: int, shop_name: str, risk_listing_filter: str,limit:str=None): """处理单个国家的审批任务 Args: @@ -582,6 +586,7 @@ class MatchTak: runing_task[task_id]["processed_countries"] += 1 self.log(f"国家 {country_name} 处理完成") + def post_stage_finished(self, task_id: int, user_id, stage_index): import requests from config import DELETE_BRAND_API_BASE diff --git a/app/amazon/price_match.py b/app/amazon/price_match.py new file mode 100644 index 0000000..d02ee8d --- /dev/null +++ b/app/amazon/price_match.py @@ -0,0 +1,333 @@ +import time +import re +import traceback + +from config import runing_task, runing_shop +from datetime import datetime +from amazon.del_brand import AmamzonBase, kill_process + +from amazon.tool import show_notification,get_shop_info + +class ChromeAmzone: + mark_name = "亚马逊详情采集" + country_info = { + "英国": { + "url": "https://www.amazon.co.uk/dp/B0CJ8SNXXV", + "zip_code": "SW1A 1AA" + }, + "德国": { + "url": "https://www.amazon.de/dp/B0CC8CW9G2?th=1", + "zip_code": "10115" + }, + "法国": { + "url": "https://www.amazon.fr/dp/B0FRG1MJ8H?th=1", + "zip_code": "75001" + }, + "西班牙": { + "url": "https://www.amazon.es/dp/B08ZXVNYNN", + "zip_code": "28001" + }, + "意大利": { + "url": "https://www.amazon.it/dp/B0D1P17T2Q", + "zip_code": "20121" + } + } + + def __init__(self): + """ + 杀死当前谷歌浏览器进程,并使用 drissionpage 启动谷歌浏览器,使用系统安装的浏览器默认用户文件夹 + """ + # 杀死现有的Chrome进程 + print("正在关闭现有的Chrome浏览器进程...") + import os + os.system('taskkill /f /t /im chrome.exe') + time.sleep(2) + + # 使用 DrissionPage 启动浏览器(使用默认用户数据) + from DrissionPage import Chromium, ChromiumOptions + print("正在启动Chrome浏览器...") + + # 配置浏览器选项 + co = ChromiumOptions() + # 使用系统默认的用户数据目录 + # co.set_user_data_path(r'C:\Users\{}\AppData\Local\Google\Chrome\User Data'.format(os.getenv('USERNAME'))) + + self.browser = Chromium(co) + self.tab = self.browser.latest_tab + print("Chrome浏览器启动成功") + + def accept_cookie(self): + pass + + def run(self, country, asin): + """ + 运行亚马逊详情采集任务 + + Args: + country: 国家名称(如:英国、德国、法国、西班牙、意大利) + asin: 亚马逊商品ASIN码 + + Returns: + dict: 包含采集到的数据 + """ + try: + # 验证国家是否支持 + if country not in self.country_info: + error_msg = f"不支持的国家: {country},支持的国家有: {list(self.country_info.keys())}" + print(error_msg) + show_notification(error_msg, "error") + return None + + # 获取国家配置 + country_config = self.country_info[country] + zip_code = country_config["zip_code"] + + # 1. 根据国家和ASIN拼接链接 + base_url = country_config["url"] + # 提取域名部分 + domain = base_url.split("/dp/")[0] + # 拼接新的URL + product_url = f"{domain}/dp/{asin}" + print(f"正在访问: {product_url}") + + # 打开链接 + self.tab.get(product_url) + time.sleep(3) # 等待页面初步加载 + self.tab.wait.doc_loaded(timeout=30, raise_err=False) + + # 2. 切换国家/设置邮编 + print(f"正在检查并设置邮编: {zip_code}") + self._set_zip_code(zip_code) + + # 3. 抓取数据 + print("正在抓取商品数据...") + data = self._scrape_data() + + # 添加基本信息 + data['country'] = country + data['asin'] = asin + data['url'] = product_url + data['timestamp'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + print(f"数据抓取完成: {data}") + return data + + except Exception as e: + error_msg = f"运行出错: {traceback.format_exc()}" + print(error_msg) + show_notification(f"采集失败: {str(e)}", "error") + return None + + def _set_zip_code(self, zip_code): + """ + 设置邮编 + + Args: + zip_code: 目标邮编 + """ + try: + # 检查当前邮编 + zip_display = self.tab.ele('xpath://div[@id="glow-ingress-block"]', timeout=10) + + if zip_display: + current_text = zip_display.text + print(f"当前地址信息: {current_text}") + + # 检查是否已经包含目标邮编 + if zip_code in current_text: + print(f"邮编已经设置为: {zip_code},无需修改") + return True + + # 需要设置邮编 + print(f"正在设置邮编为: {zip_code}") + + # 点击地址选择按钮 + location_link = self.tab.ele('xpath://a[@id="nav-global-location-popover-link"]', timeout=10) + if not location_link: + print("找不到地址设置按钮") + return False + + location_link.click() + time.sleep(1) + + # 等待邮编输入框出现 + zip_input = self.tab.ele('xpath://input[@id="GLUXZipUpdateInput"]', timeout=10) + if not zip_input: + print("找不到邮编输入框") + return False + + # 输入邮编 + zip_input.input(zip_code, clear=True) + time.sleep(0.5) + + # 点击提交按钮 + submit_btn = self.tab.ele('xpath://input[@aria-labelledby="GLUXZipUpdate-announce"]', timeout=10) + if not submit_btn: + print("找不到提交按钮") + return False + + submit_btn.click() + + # 等待提交按钮消失(表示请求已发送) + print("等待邮编更新...") + time.sleep(2) + + # 等待页面加载完成 + self.tab.wait.doc_loaded(timeout=30, raise_err=False) + time.sleep(2) + + # 验证邮编是否设置成功 + zip_display_after = self.tab.ele('xpath://div[@id="glow-ingress-block"]', timeout=10) + if zip_display_after: + updated_text = zip_display_after.text + print(f"更新后的地址信息: {updated_text}") + + if zip_code in updated_text: + print(f"邮编设置成功: {zip_code}") + return True + else: + print(f"邮编设置可能失败,当前显示: {updated_text}") + return False + + return True + + except Exception as e: + print(f"设置邮编时出错: {traceback.format_exc()}") + return False + + def _scrape_data(self): + """ + 抓取商品数据 + + Returns: + dict: 抓取到的数据 + """ + data = { + 'top_sellers': [], # 前两名卖家信息 + 'cart_seller': None # 购物车所属卖家 + } + + try: + # 等待页面加载 + time.sleep(3) + + # 1. 获取前两名卖家的价格和库存(来自 sellersprite 插件) + print("正在抓取卖家排名数据...") + surplus_tables = self.tab.eles( + 'xpath://div[@id="sellersprite-extension-Inventory-surplus-count"]//div[@class="surplus-table"]', + timeout=10 + ) + + if surplus_tables: + print(f"找到 {len(surplus_tables)} 个卖家数据") + # 只获取前两个 + for idx, table in enumerate(surplus_tables[:2]): + try: + # 提取价格和库存信息 + # 注意:需要根据实际的HTML结构调整选择器 + text_content = table.text + print(f"第{idx+1}名卖家数据: {text_content}") + + seller_info = { + 'rank': idx + 1, + 'raw_data': text_content + } + + # 尝试提取更结构化的数据 + # 这里需要根据实际HTML结构来解析 + # 可能需要查找子元素 + try: + # 示例:查找价格和库存的具体子节点 + price_ele = table.ele('xpath:.//span[contains(@class,"price")]', timeout=2) + stock_ele = table.ele('xpath:.//span[contains(@class,"stock")]', timeout=2) + + if price_ele: + seller_info['price'] = price_ele.text.strip() + if stock_ele: + seller_info['stock'] = stock_ele.text.strip() + except: + # 如果找不到具体元素,就使用原始文本 + pass + + data['top_sellers'].append(seller_info) + + except Exception as e: + print(f"解析第{idx+1}名卖家数据失败: {str(e)}") + else: + print("未找到 sellersprite 插件数据,可能插件未启用") + + # 2. 获取购物车所属卖家 + print("正在抓取购物车卖家信息...") + cart_seller = self.tab.ele( + 'xpath://div[@data-csa-c-content-id="desktop-merchant-info"]//a[@id="sellerProfileTriggerId"]', + timeout=10 + ) + + if cart_seller: + seller_name = cart_seller.text.strip() + print(f"购物车所属卖家: {seller_name}") + data['cart_seller'] = seller_name + else: + print("未找到购物车卖家信息") + # 尝试其他可能的选择器 + try: + alt_seller = self.tab.ele('xpath://a[@id="sellerProfileTriggerId"]', timeout=5) + if alt_seller: + data['cart_seller'] = alt_seller.text.strip() + print(f"购物车所属卖家(备用方式): {data['cart_seller']}") + except: + pass + + return data + + except Exception as e: + print(f"抓取数据时出错: {traceback.format_exc()}") + return data + + def close(self): + """关闭浏览器""" + try: + if self.browser: + self.browser.quit() + print("浏览器已关闭") + except Exception as e: + print(f"关闭浏览器时出错: {str(e)}") + + +class AmzonePriceMatch(AmamzonBase): + mark_name = "跟价" + + pass + + +if __name__ == '__main__': + # 使用示例 + print("=" * 50) + print("亚马逊详情采集示例") + print("=" * 50) + + try: + # 创建ChromeAmzone实例 + chrome = ChromeAmzone() + + # 示例1:采集英国站点的商品信息 + print("\n示例1:采集英国站点商品") + result1 = chrome.run(country="英国", asin="B0CJ8SNXXV") + if result1: + print(f"采集成功: {result1}") + + # 示例2:采集德国站点的商品信息 + print("\n示例2:采集德国站点商品") + result2 = chrome.run(country="德国", asin="B0CC8CW9G2") + if result2: + print(f"采集成功: {result2}") + + # 关闭浏览器 + print("\n正在关闭浏览器...") + chrome.close() + + except Exception as e: + print(f"运行出错: {traceback.format_exc()}") + + + diff --git a/app/main.py b/app/main.py index dac45e7..0286722 100644 --- a/app/main.py +++ b/app/main.py @@ -19,7 +19,6 @@ import threading import time import requests import subprocess -import atexit from amazon.del_brand import kill_process @@ -40,21 +39,6 @@ print(f""" BASE_DIR = os.path.dirname(os.path.abspath(__file__)) APP_URL = "http://127.0.0.1:5123" PORT = 5123 -_shutdown_started = False - - -def cleanup_before_exit(): - global _shutdown_started - if _shutdown_started: - return - _shutdown_started = True - try: - kill_process('v6') - except Exception as e: - print(f"【退出前】关闭紫鸟浏览器进程异常: {str(e)}") - - -atexit.register(cleanup_before_exit) def generate_images(params):