modified: amazon/__pycache__/approve.cpython-39.pyc
modified: amazon/__pycache__/del_brand.cpython-39.pyc modified: amazon/__pycache__/main.cpython-39.pyc modified: amazon/approve.py new file: amazon/backend_api.py modified: amazon/del_brand.py modified: amazon/main.py modified: blueprints/main.py modified: main.py
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,6 +7,12 @@ from config import runing_task, runing_shop
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from amazon.del_brand import AmamzonBase, kill_process
|
from amazon.del_brand import AmamzonBase, kill_process
|
||||||
|
|
||||||
|
# 导入 webview 用于前端通知
|
||||||
|
try:
|
||||||
|
import webview
|
||||||
|
except ImportError:
|
||||||
|
webview = None
|
||||||
|
|
||||||
class AmzoneApprove(AmamzonBase):
|
class AmzoneApprove(AmamzonBase):
|
||||||
|
|
||||||
def SwitchPage(self):
|
def SwitchPage(self):
|
||||||
@@ -190,8 +196,8 @@ class AmzoneApprove(AmamzonBase):
|
|||||||
num = 0
|
num = 0
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
if num > 3: #测试
|
# if num > 3: #测试
|
||||||
return
|
# return
|
||||||
# 等待加载完成
|
# 等待加载完成
|
||||||
load_ele = self.tab.eles("xpath://div[contains(@class,'Loader-module__loader')]",timeout=5)
|
load_ele = self.tab.eles("xpath://div[contains(@class,'Loader-module__loader')]",timeout=5)
|
||||||
if len(load_ele) > 0:
|
if len(load_ele) > 0:
|
||||||
@@ -202,14 +208,17 @@ class AmzoneApprove(AmamzonBase):
|
|||||||
page_pamel = self.tab.eles('xpath://kat-pagination',timeout=5)
|
page_pamel = self.tab.eles('xpath://kat-pagination',timeout=5)
|
||||||
if len(page_pamel) > 0:
|
if len(page_pamel) > 0:
|
||||||
current_page = page_pamel[0].sr('xpath:.//ul[@class="pages"]//li[@aria-current="true"]').text
|
current_page = page_pamel[0].sr('xpath:.//ul[@class="pages"]//li[@aria-current="true"]').text
|
||||||
print(f"开始处理,当前页码: {current_page}")
|
print(f"【current_page】开始处理,当前页码: {current_page}")
|
||||||
|
|
||||||
sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=3)
|
sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=10)
|
||||||
print(f"获取到 {len(sku_ls)}")
|
print(f"获取到 {len(sku_ls)}")
|
||||||
for sku_ele in sku_ls[0:2]:
|
# for sku_ele in sku_ls[0:2]:
|
||||||
|
for sku_ele in sku_ls:
|
||||||
# solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]')
|
# solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]')
|
||||||
solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]|.//kat-link[@label="修复被禁止显示的商品"]|//kat-link[@label="解决商品移除风险"]')
|
solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]|.//kat-link[@label="修复被禁止显示的商品"]|.//kat-link[@label="解决商品移除风险"]')
|
||||||
if len(solve_problem) == 0:
|
if len(solve_problem) == 0:
|
||||||
|
print(f"{asin},没有处理入口,不处理")
|
||||||
|
yield (asin,"没有处理入口,不处理")
|
||||||
continue
|
continue
|
||||||
asin = sku_ele.ele('xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]').text
|
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 {asin} 存在问题,正在点击解决...")
|
print(f"ASIN {asin} 存在问题,正在点击解决...")
|
||||||
@@ -235,9 +244,9 @@ class AmzoneApprove(AmamzonBase):
|
|||||||
|
|
||||||
# 解决商品信息违规问题 按钮
|
# 解决商品信息违规问题 按钮
|
||||||
kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]',timeout=5)
|
kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]',timeout=5)
|
||||||
if len(kat_box_ls) == 0:
|
# if len(kat_box_ls) == 0:
|
||||||
print(f"ASIN {asin} 的操作面板中未找到选项,跳过...")
|
# print(f"ASIN {asin} 的操作面板中未找到选项,跳过...")
|
||||||
continue
|
# continue
|
||||||
print(f"ASIN {asin}需要处理的有:{len(kat_box_ls)}个问题.")
|
print(f"ASIN {asin}需要处理的有:{len(kat_box_ls)}个问题.")
|
||||||
# 解决商品信息违规问题 按钮,有多少个都要处理
|
# 解决商品信息违规问题 按钮,有多少个都要处理
|
||||||
for i in range(len(kat_box_ls)):
|
for i in range(len(kat_box_ls)):
|
||||||
@@ -284,7 +293,7 @@ class AmzoneApprove(AmamzonBase):
|
|||||||
kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]', timeout=5)
|
kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]', timeout=5)
|
||||||
|
|
||||||
# 修复商品信息
|
# 修复商品信息
|
||||||
repair_product = action_panel.eles('xpath://kat-button[@class="action-button"]',timeout=5)
|
repair_product = action_panel.eles('xpath:.//kat-button[@class="action-button"]',timeout=5)
|
||||||
if len(repair_product) > 0:
|
if len(repair_product) > 0:
|
||||||
print(f"ASIN {asin} 存在修复商品信息按钮,正在点击...")
|
print(f"ASIN {asin} 存在修复商品信息按钮,正在点击...")
|
||||||
self.clear_tab()
|
self.clear_tab()
|
||||||
@@ -316,7 +325,7 @@ class AmzoneApprove(AmamzonBase):
|
|||||||
break
|
break
|
||||||
next_page_btn.click()
|
next_page_btn.click()
|
||||||
num += 1
|
num += 1
|
||||||
print(f"正在翻页,已翻 {num} 页...")
|
# print(f"正在翻页,已翻 {num} 页...")
|
||||||
|
|
||||||
|
|
||||||
class ApproveTask:
|
class ApproveTask:
|
||||||
@@ -330,6 +339,75 @@ class ApproveTask:
|
|||||||
"UK": "英国"
|
"UK": "英国"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def show_notification(message: str, message_type: str = "error"):
|
||||||
|
"""显示 pywebview 顶层通知(5秒后自动消失)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: 通知消息
|
||||||
|
message_type: 消息类型 (success/warning/error/info)
|
||||||
|
"""
|
||||||
|
if webview and webview.windows:
|
||||||
|
try:
|
||||||
|
# 转义单引号,防止 JavaScript 语法错误
|
||||||
|
safe_message = message.replace("'", "\\'").replace('"', '\\"').replace('\n', '\\n')
|
||||||
|
|
||||||
|
# 设置通知样式颜色
|
||||||
|
color_map = {
|
||||||
|
'success': '#67C23A',
|
||||||
|
'warning': '#E6A23C',
|
||||||
|
'error': '#F56C6C',
|
||||||
|
'info': '#909399'
|
||||||
|
}
|
||||||
|
bg_color = color_map.get(message_type, '#F56C6C')
|
||||||
|
|
||||||
|
# 构建前端通知的 JavaScript 代码 - 创建原生 HTML 通知
|
||||||
|
js_code = f"""
|
||||||
|
(function() {{
|
||||||
|
// 移除已存在的通知
|
||||||
|
var existingNotif = document.getElementById('pywebview-notification');
|
||||||
|
if (existingNotif) {{
|
||||||
|
existingNotif.remove();
|
||||||
|
}}
|
||||||
|
|
||||||
|
// 创建通知容器
|
||||||
|
var notif = document.createElement('div');
|
||||||
|
notif.id = 'pywebview-notification';
|
||||||
|
notif.style.cssText = 'position: fixed; top: 20px; left: 50%; transform: translateX(-50%); ' +
|
||||||
|
'background: {bg_color}; color: white; padding: 12px 20px; border-radius: 4px; ' +
|
||||||
|
'box-shadow: 0 2px 12px rgba(0,0,0,0.3); z-index: 99999; font-size: 14px; ' +
|
||||||
|
'max-width: 600px; word-wrap: break-word; display: flex; align-items: center; gap: 10px;';
|
||||||
|
|
||||||
|
// 添加消息内容
|
||||||
|
var msgSpan = document.createElement('span');
|
||||||
|
msgSpan.textContent = '{safe_message}';
|
||||||
|
notif.appendChild(msgSpan);
|
||||||
|
|
||||||
|
// 添加关闭按钮
|
||||||
|
var closeBtn = document.createElement('span');
|
||||||
|
closeBtn.innerHTML = '×';
|
||||||
|
closeBtn.style.cssText = 'cursor: pointer; font-size: 18px; font-weight: bold; margin-left: 10px;';
|
||||||
|
closeBtn.onclick = function() {{ notif.remove(); }};
|
||||||
|
notif.appendChild(closeBtn);
|
||||||
|
|
||||||
|
// 添加到页面
|
||||||
|
document.body.appendChild(notif);
|
||||||
|
|
||||||
|
// 5秒后自动消失
|
||||||
|
setTimeout(function() {{
|
||||||
|
if (notif && notif.parentNode) {{
|
||||||
|
notif.style.transition = 'opacity 0.3s';
|
||||||
|
notif.style.opacity = '0';
|
||||||
|
setTimeout(function() {{ notif.remove(); }}, 300);
|
||||||
|
}}
|
||||||
|
}}, 5000);
|
||||||
|
}})();
|
||||||
|
"""
|
||||||
|
# 在第一个窗口中执行 JavaScript
|
||||||
|
webview.windows[0].evaluate_js(js_code)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"显示通知失败: {str(e)}")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
处理流程:
|
处理流程:
|
||||||
1、解析任务json,提取对应的companyName 组组装user_info实例化 AmzoneApprove
|
1、解析任务json,提取对应的companyName 组组装user_info实例化 AmzoneApprove
|
||||||
@@ -385,6 +463,8 @@ class ApproveTask:
|
|||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
if level == "ERROR":
|
||||||
|
self.show_notification(message, "error")
|
||||||
print(f"[{timestamp}] [ApproveTask] [{level}] {message}")
|
print(f"[{timestamp}] [ApproveTask] [{level}] {message}")
|
||||||
|
|
||||||
def process_task(self, task_data: dict):
|
def process_task(self, task_data: dict):
|
||||||
@@ -439,6 +519,7 @@ class ApproveTask:
|
|||||||
|
|
||||||
shop_name = shop_item.get("shopName", "未知店铺")
|
shop_name = shop_item.get("shopName", "未知店铺")
|
||||||
self.log(f"[{idx}/{len(items)}] 开始处理店铺: {shop_name}")
|
self.log(f"[{idx}/{len(items)}] 开始处理店铺: {shop_name}")
|
||||||
|
self.show_notification(f"开始处理店铺: {shop_name}", "info")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.process_shop(shop_item, country_codes, task_id,risk_listing_filter)
|
self.process_shop(shop_item, country_codes, task_id,risk_listing_filter)
|
||||||
@@ -502,11 +583,11 @@ class ApproveTask:
|
|||||||
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
||||||
|
|
||||||
# 如果不是第一次尝试,先杀进程
|
# 如果不是第一次尝试,先杀进程
|
||||||
if retry > 0:
|
# if retry > 0:
|
||||||
self.log("重试前先杀掉浏览器进程...")
|
# self.log("重试前先杀掉浏览器进程...")
|
||||||
kill_process("v6")
|
# kill_process("v6")
|
||||||
kill_process("v5")
|
# kill_process("v5")
|
||||||
time.sleep(2)
|
# time.sleep(2)
|
||||||
|
|
||||||
# 组装用户信息并创建驱动
|
# 组装用户信息并创建驱动
|
||||||
user_info = {
|
user_info = {
|
||||||
@@ -522,6 +603,7 @@ class ApproveTask:
|
|||||||
else:
|
else:
|
||||||
self.log(f"打开店铺失败: {browser}", "WARNING")
|
self.log(f"打开店铺失败: {browser}", "WARNING")
|
||||||
driver = None
|
driver = None
|
||||||
|
# 判断是否需要登录
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
@@ -535,7 +617,8 @@ class ApproveTask:
|
|||||||
|
|
||||||
# 检查是否成功打开
|
# 检查是否成功打开
|
||||||
if not driver or not browser or browser == "店铺不存在":
|
if not driver or not browser or browser == "店铺不存在":
|
||||||
self.log(f"店铺 {shop_name} 打开失败,已重试 {max_retries} 次,跳过该店铺", "ERROR")
|
error_msg = f"店铺 {shop_name} 打开失败,已重试 {max_retries} 次,跳过该店铺"
|
||||||
|
self.log(error_msg, "ERROR")
|
||||||
# 从执行列表中移除
|
# 从执行列表中移除
|
||||||
if shop_name in runing_shop:
|
if shop_name in runing_shop:
|
||||||
del runing_shop[shop_name]
|
del runing_shop[shop_name]
|
||||||
@@ -589,7 +672,9 @@ class ApproveTask:
|
|||||||
|
|
||||||
# 转换国家代码为中文名称
|
# 转换国家代码为中文名称
|
||||||
country_name = self.country_info.get(country_code, country_code)
|
country_name = self.country_info.get(country_code, country_code)
|
||||||
self.log(f"开始处理国家: {country_name} ({country_code})")
|
info_mes = f"开始处理国家: {country_name} ({country_code})"
|
||||||
|
self.log(info_mes)
|
||||||
|
self.show_notification(info_mes, "info")
|
||||||
|
|
||||||
# 更新当前处理的国家
|
# 更新当前处理的国家
|
||||||
if task_id in runing_task:
|
if task_id in runing_task:
|
||||||
@@ -630,7 +715,8 @@ class ApproveTask:
|
|||||||
|
|
||||||
# 如果切换失败,直接返回
|
# 如果切换失败,直接返回
|
||||||
if not switch_success:
|
if not switch_success:
|
||||||
self.log(f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家", "ERROR")
|
error_message = f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
|
||||||
|
self.log(error_message, "ERROR")
|
||||||
if task_id in runing_task:
|
if task_id in runing_task:
|
||||||
runing_task[task_id]["processed_countries"] += 1
|
runing_task[task_id]["processed_countries"] += 1
|
||||||
return
|
return
|
||||||
@@ -819,3 +905,5 @@ if __name__ == '__main__':
|
|||||||
print(f"ASIN {asin} 的处理结果: {status}")
|
print(f"ASIN {asin} 的处理结果: {status}")
|
||||||
print("已完成操作")
|
print("已完成操作")
|
||||||
|
|
||||||
|
# for i in range(0):
|
||||||
|
# print(i)
|
||||||
0
app/amazon/backend_api.py
Normal file
0
app/amazon/backend_api.py
Normal file
@@ -10,6 +10,7 @@ import os
|
|||||||
from typing import Literal
|
from typing import Literal
|
||||||
from DrissionPage import Chromium
|
from DrissionPage import Chromium
|
||||||
from DrissionPage.common import By
|
from DrissionPage.common import By
|
||||||
|
from DrissionPage._pages.chromium_tab import ChromiumTab
|
||||||
|
|
||||||
|
|
||||||
def kill_process(version: Literal["v5", "v6"]):
|
def kill_process(version: Literal["v5", "v6"]):
|
||||||
@@ -33,7 +34,7 @@ class ZiniaoDriver:
|
|||||||
self.socket_port = socket_port
|
self.socket_port = socket_port
|
||||||
self.client_path = None
|
self.client_path = None
|
||||||
self.browser = None
|
self.browser = None
|
||||||
self.tab = None
|
self.tab: ChromiumTab = None
|
||||||
self.store_id = None
|
self.store_id = None
|
||||||
|
|
||||||
def get_zinaio_exe(self, protocol_name: str = "superbrowser"):
|
def get_zinaio_exe(self, protocol_name: str = "superbrowser"):
|
||||||
@@ -136,11 +137,11 @@ class ZiniaoDriver:
|
|||||||
print(r)
|
print(r)
|
||||||
return r.get("browserList")
|
return r.get("browserList")
|
||||||
elif str(r.get("statusCode")) == "-10003":
|
elif str(r.get("statusCode")) == "-10003":
|
||||||
print(f"login Err {json.dumps(r, ensure_ascii=False)}")
|
print(f"【get_browser_list】登录失败 {json.dumps(r, ensure_ascii=False)}")
|
||||||
exit()
|
# exit()
|
||||||
else:
|
else:
|
||||||
print(f"Fail {json.dumps(r, ensure_ascii=False)} ")
|
print(f"【get_browser_list】失败 {json.dumps(r, ensure_ascii=False)} ")
|
||||||
exit()
|
# exit()
|
||||||
|
|
||||||
def open_store(self, store_info, isWebDriverReadOnlyMode=0, isprivacy=0,
|
def open_store(self, store_info, isWebDriverReadOnlyMode=0, isprivacy=0,
|
||||||
isHeadless=0, cookieTypeSave=0, jsInfo=""):
|
isHeadless=0, cookieTypeSave=0, jsInfo=""):
|
||||||
@@ -188,11 +189,11 @@ class ZiniaoDriver:
|
|||||||
if str(r.get("statusCode")) == "0":
|
if str(r.get("statusCode")) == "0":
|
||||||
return r
|
return r
|
||||||
elif str(r.get("statusCode")) == "-10003":
|
elif str(r.get("statusCode")) == "-10003":
|
||||||
print(f"login Err {json.dumps(r, ensure_ascii=False)}")
|
raise RuntimeError(f"【open_store】登录失败 {json.dumps(r, ensure_ascii=False)}")
|
||||||
exit()
|
# exit()
|
||||||
else:
|
else:
|
||||||
print(f"Fail {json.dumps(r, ensure_ascii=False)} ")
|
raise RuntimeError(f"【open_store】失败 {json.dumps(r, ensure_ascii=False)} ")
|
||||||
exit()
|
# exit()
|
||||||
|
|
||||||
def get_browser(self, port) -> Chromium:
|
def get_browser(self, port) -> Chromium:
|
||||||
"""
|
"""
|
||||||
@@ -209,6 +210,17 @@ class ZiniaoDriver:
|
|||||||
|
|
||||||
def start_client(self):
|
def start_client(self):
|
||||||
"""启动紫鸟客户端"""
|
"""启动紫鸟客户端"""
|
||||||
|
|
||||||
|
# 检查端口是否已经启动
|
||||||
|
try:
|
||||||
|
url = f'http://127.0.0.1:{self.socket_port}'
|
||||||
|
response = requests.get(url, timeout=2)
|
||||||
|
print(f"端口 {self.socket_port} 已经启动,跳过启动客户端操作")
|
||||||
|
return
|
||||||
|
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
|
||||||
|
# 端口未启动,继续执行启动操作
|
||||||
|
print(f"端口 {self.socket_port} 未启动,开始启动客户端")
|
||||||
|
|
||||||
self.client_path = self.get_zinaio_exe("superbrowserv6").strip('"')
|
self.client_path = self.get_zinaio_exe("superbrowserv6").strip('"')
|
||||||
print(self.client_path)
|
print(self.client_path)
|
||||||
cmd = [self.client_path, '--run_type=web_driver', '--ipc_type=http',
|
cmd = [self.client_path, '--run_type=web_driver', '--ipc_type=http',
|
||||||
@@ -261,7 +273,8 @@ class ZiniaoDriver:
|
|||||||
print("ip检测页地址为空,请升级紫鸟浏览器到最新版")
|
print("ip检测页地址为空,请升级紫鸟浏览器到最新版")
|
||||||
print(f"=====关闭店铺:{shop_name}=====")
|
print(f"=====关闭店铺:{shop_name}=====")
|
||||||
self.close_store(self.store_id)
|
self.close_store(self.store_id)
|
||||||
exit()
|
# exit()
|
||||||
|
raise RuntimeError("没有IP检测地址,为了店铺安全不打开店铺")
|
||||||
ip_usable = self.open_ip_check(self.browser, ip_check_url)
|
ip_usable = self.open_ip_check(self.browser, ip_check_url)
|
||||||
if ip_usable:
|
if ip_usable:
|
||||||
print("ip检测通过,打开店铺平台主页")
|
print("ip检测通过,打开店铺平台主页")
|
||||||
@@ -298,11 +311,11 @@ class ZiniaoDriver:
|
|||||||
if str(r.get("statusCode")) == "0":
|
if str(r.get("statusCode")) == "0":
|
||||||
return r
|
return r
|
||||||
elif str(r.get("statusCode")) == "-10003":
|
elif str(r.get("statusCode")) == "-10003":
|
||||||
print(f"login Err {json.dumps(r, ensure_ascii=False)}")
|
raise RuntimeError(f"【close_store】登录失败 {json.dumps(r, ensure_ascii=False)}")
|
||||||
exit()
|
# exit()
|
||||||
else:
|
else:
|
||||||
print(f"Fail {json.dumps(r, ensure_ascii=False)} ")
|
raise RuntimeError(f"【close_store】失败: {json.dumps(r, ensure_ascii=False)} ")
|
||||||
exit()
|
# exit()
|
||||||
|
|
||||||
def open_launcher_page(self, launcher_page: str, browser: Chromium = None):
|
def open_launcher_page(self, launcher_page: str, browser: Chromium = None):
|
||||||
"""
|
"""
|
||||||
@@ -436,8 +449,38 @@ class AmamzonBase(ZiniaoDriver):
|
|||||||
print(f"切换国家时发生异常:{traceback.format_exc()}")
|
print(f"切换国家时发生异常:{traceback.format_exc()}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def need_login(self):
|
||||||
|
"""
|
||||||
|
判断是否需要登录,部分国家可能需要登录后才能切换国家
|
||||||
|
处理流程:
|
||||||
|
|
||||||
|
"""
|
||||||
|
need_login_ele = self.tab.eles('xpath://h1[@class="a-spacing-small"]',timeout=5)
|
||||||
|
if len(need_login_ele) >0:
|
||||||
|
print("检测到需要登录元素")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def login(self,password,username=""):
|
||||||
|
try:
|
||||||
|
pwd_input = self.tab.ele('xpath://input[@type="password"]',timeout=10)
|
||||||
|
pwd_input.input(password)
|
||||||
|
submit_btn = self.tab.ele('xpath://input[@id="signInSubmit"]',timeout=10)
|
||||||
|
submit_btn.click()
|
||||||
|
self.tab.wait.doc_loaded()
|
||||||
|
|
||||||
|
opt_code_input = self.tab.ele('xpath://input[@name="otpCode"]',timeout=10)
|
||||||
|
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
|
||||||
|
except Exception as e:
|
||||||
|
print("登录过程中发生异常:" + traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
class AmazoneDriver(AmamzonBase):
|
class AmazoneDriver(AmamzonBase):
|
||||||
"""亚马逊专用驱动类,继承自ZiniaoDriver"""
|
"""亚马逊专用驱动类,继承自ZiniaoDriver"""
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class TaskMonitor:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 静默处理队列为空的超时,只记录真正的异常
|
# 静默处理队列为空的超时,只记录真正的异常
|
||||||
if "Empty" not in str(e):
|
if str(e) and "Empty" not in str(e):
|
||||||
self.log(f"任务监控异常: {str(e)}", "ERROR")
|
self.log(f"任务监控异常: {str(e)}", "ERROR")
|
||||||
# 队列为空时不需要额外等待,直接继续循环
|
# 队列为空时不需要额外等待,直接继续循环
|
||||||
|
|
||||||
@@ -210,11 +210,11 @@ class TaskMonitor:
|
|||||||
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
||||||
|
|
||||||
# 如果不是第一次尝试,先杀进程
|
# 如果不是第一次尝试,先杀进程
|
||||||
if retry > 0:
|
# if retry > 0:
|
||||||
self.log("重试前先杀掉浏览器进程...")
|
# self.log("重试前先杀掉浏览器进程...")
|
||||||
kill_process("v6")
|
# kill_process("v6")
|
||||||
kill_process("v5")
|
# kill_process("v5")
|
||||||
time.sleep(2)
|
# time.sleep(2)
|
||||||
|
|
||||||
# 创建驱动并打开店铺
|
# 创建驱动并打开店铺
|
||||||
user_info = {
|
user_info = {
|
||||||
@@ -231,6 +231,27 @@ class TaskMonitor:
|
|||||||
self.log(f"打开店铺失败: {browser}", "WARNING")
|
self.log(f"打开店铺失败: {browser}", "WARNING")
|
||||||
driver = None
|
driver = None
|
||||||
|
|
||||||
|
# 判断是否需要登录
|
||||||
|
# need_login = driver.need_login()
|
||||||
|
# if need_login:
|
||||||
|
# self.log(f"店铺 {shop_name} 需要登录,正在登录...")
|
||||||
|
# password = ""
|
||||||
|
|
||||||
|
# login_success = driver.login(password)
|
||||||
|
# if login_success:
|
||||||
|
# self.log(f"店铺 {shop_name} 登录成功,正在重新打开店铺...")
|
||||||
|
# browser = driver.open_shop(shop_name)
|
||||||
|
# if browser and browser != "店铺不存在":
|
||||||
|
# self.log(f"成功打开店铺 {shop_name} 登录后")
|
||||||
|
# break
|
||||||
|
# else:
|
||||||
|
# self.log(f"登录后打开店铺失败: {browser}", "WARNING")
|
||||||
|
# driver = None
|
||||||
|
# else:
|
||||||
|
# self.log(f"店铺 {shop_name} 登录失败", "WARNING")
|
||||||
|
# driver = None
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"打开店铺异常: {str(e)}", "ERROR")
|
self.log(f"打开店铺异常: {str(e)}", "ERROR")
|
||||||
driver = None
|
driver = None
|
||||||
|
|||||||
@@ -110,7 +110,10 @@ def proxy(path):
|
|||||||
if key.lower() in ['host', 'content-length', 'connection']:
|
if key.lower() in ['host', 'content-length', 'connection']:
|
||||||
continue
|
continue
|
||||||
headers[key] = value
|
headers[key] = value
|
||||||
ignore_url = [f"{JAVA_API_BASE}/api/delete-brand/tasks/batch",f"{JAVA_API_BASE}/api/delete-brand/history"]
|
ignore_url = [f"{JAVA_API_BASE}/api/delete-brand/tasks/batch",
|
||||||
|
f"{JAVA_API_BASE}/api/delete-brand/history",
|
||||||
|
f"{JAVA_API_BASE}/api/product-risk-resolve/tasks/batch",
|
||||||
|
]
|
||||||
if target_url not in ignore_url:
|
if target_url not in ignore_url:
|
||||||
try:
|
try:
|
||||||
print("=============================")
|
print("=============================")
|
||||||
|
|||||||
39
app/main.py
39
app/main.py
@@ -11,7 +11,7 @@ from amazon.main import TaskMonitor
|
|||||||
os.makedirs(cache_path,exist_ok=True)
|
os.makedirs(cache_path,exist_ok=True)
|
||||||
if not debug:
|
if not debug:
|
||||||
today = datetime.datetime.now().strftime("%Y_%m_%d")
|
today = datetime.datetime.now().strftime("%Y_%m_%d")
|
||||||
sys.stdout = open(os.path.join(cache_path,f'{today}.log'), 'a',buffering=1)
|
sys.stdout = open(os.path.join(cache_path,f'{today}.log'), 'a',buffering=1,encoding='utf-8')
|
||||||
sys.stderr = sys.stdout
|
sys.stderr = sys.stdout
|
||||||
|
|
||||||
import webview
|
import webview
|
||||||
@@ -20,6 +20,9 @@ import time
|
|||||||
import requests
|
import requests
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from amazon.del_brand import kill_process
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
with open("version.txt","w",encoding="utf-8") as file:
|
with open("version.txt","w",encoding="utf-8") as file:
|
||||||
file.write(json.dumps({"version":version}))
|
file.write(json.dumps({"version":version}))
|
||||||
@@ -54,8 +57,15 @@ class WindowAPI:
|
|||||||
window.events.restored += lambda _: setattr(self, '_is_maximized', False)
|
window.events.restored += lambda _: setattr(self, '_is_maximized', False)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
sys.exit(1)
|
try:
|
||||||
self._window.destroy()
|
kill_process('v6')
|
||||||
|
except Exception as e:
|
||||||
|
print(f"【退出前】关闭紫鸟浏览器进程异常: {str(e)}")
|
||||||
|
try:
|
||||||
|
self._window.destroy()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
os._exit(0) # 强制退出整个进程,包括所有线程
|
||||||
|
|
||||||
def minimize(self):
|
def minimize(self):
|
||||||
self._window.minimize()
|
self._window.minimize()
|
||||||
@@ -358,6 +368,23 @@ def start_task_monitor():
|
|||||||
print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 任务监控线程异常退出: {traceback.format_exc()}")
|
print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 任务监控线程异常退出: {traceback.format_exc()}")
|
||||||
|
|
||||||
|
|
||||||
|
def on_window_closing():
|
||||||
|
"""窗口关闭事件处理器,执行与 close() 方法相同的清理逻辑"""
|
||||||
|
# 先关闭窗口,避免看起来卡着
|
||||||
|
try:
|
||||||
|
if webview.windows:
|
||||||
|
webview.windows[0].destroy()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 然后执行清理逻辑
|
||||||
|
try:
|
||||||
|
kill_process('v6')
|
||||||
|
except Exception as e:
|
||||||
|
print(f"【退出前】关闭紫鸟浏览器进程异常: {str(e)}")
|
||||||
|
os._exit(0) # 强制退出整个进程,包括所有线程
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if not os.path.exists(cache_path):
|
if not os.path.exists(cache_path):
|
||||||
os.makedirs(cache_path,exist_ok=True)
|
os.makedirs(cache_path,exist_ok=True)
|
||||||
@@ -384,11 +411,15 @@ def main():
|
|||||||
frameless=False,
|
frameless=False,
|
||||||
easy_drag=True
|
easy_drag=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 为窗口关闭事件添加处理器
|
||||||
|
window.events.closing += on_window_closing
|
||||||
|
|
||||||
api = WindowAPI(window)
|
api = WindowAPI(window)
|
||||||
window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder,
|
window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder,
|
||||||
api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new)
|
api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new)
|
||||||
webview.start(
|
webview.start(
|
||||||
debug=True,
|
debug=False,
|
||||||
storage_path=cache_path,
|
storage_path=cache_path,
|
||||||
private_mode=False
|
private_mode=False
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user