diff --git a/app/amazon/__pycache__/approve.cpython-39.pyc b/app/amazon/__pycache__/approve.cpython-39.pyc index 694a2d5..b1fa641 100644 Binary files a/app/amazon/__pycache__/approve.cpython-39.pyc and b/app/amazon/__pycache__/approve.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/del_brand.cpython-39.pyc b/app/amazon/__pycache__/del_brand.cpython-39.pyc index 8551dba..0e5aacf 100644 Binary files a/app/amazon/__pycache__/del_brand.cpython-39.pyc and b/app/amazon/__pycache__/del_brand.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/match_action.cpython-39.pyc b/app/amazon/__pycache__/match_action.cpython-39.pyc index 7d42202..cf06b6f 100644 Binary files a/app/amazon/__pycache__/match_action.cpython-39.pyc and b/app/amazon/__pycache__/match_action.cpython-39.pyc differ 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):