modified: app/amazon/__pycache__/approve.cpython-39.pyc
modified: app/amazon/__pycache__/del_brand.cpython-39.pyc modified: app/amazon/__pycache__/match_action.cpython-39.pyc modified: app/amazon/__pycache__/price_match.cpython-39.pyc modified: app/amazon/approve.py modified: app/amazon/del_brand.py modified: app/amazon/match_action.py modified: app/amazon/price_match.py modified: app/assets/convert.js modified: app/assets/dedupe.js modified: app/assets/delete-brand.js modified: app/assets/split.js modified: app/new_web_source/convert.html modified: app/new_web_source/dedupe.html modified: app/new_web_source/delete-brand.html modified: app/new_web_source/split.html
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -923,46 +923,18 @@ class ApproveTask:
|
|||||||
runing_task[task_id]["status"] = "failed"
|
runing_task[task_id]["status"] = "failed"
|
||||||
runing_task[task_id]["error"] = str(e)
|
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 open_shop(self,max_retries,company_name,shop_name,iskill=False):
|
||||||
"""处理单个店铺
|
|
||||||
|
|
||||||
Args:
|
|
||||||
shop_item: 店铺信息
|
|
||||||
country_codes: 国家代码列表
|
|
||||||
task_id: 任务ID
|
|
||||||
risk_listing_filter: 风险商品筛选条件
|
|
||||||
"""
|
|
||||||
shop_name = shop_item.get("shopName", "未知店铺")
|
|
||||||
company_name = shop_item.get("companyName", "")
|
|
||||||
|
|
||||||
if not company_name:
|
|
||||||
self.log(f"店铺 {shop_name} 的公司名称为空,跳过", "WARNING")
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
if task_id in runing_task:
|
|
||||||
runing_task[task_id]["current_shop"] = shop_name
|
|
||||||
|
|
||||||
# 将店铺添加到正在执行中的店铺列表
|
|
||||||
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
runing_shop[shop_name] = start_time
|
|
||||||
self.log(f"店铺 {shop_name} 已添加到执行列表,账号: {company_name},开始时间: {start_time}")
|
|
||||||
|
|
||||||
# 店铺打开重试最多3次
|
|
||||||
driver = None
|
|
||||||
max_retries = 3
|
|
||||||
|
|
||||||
error_info = ""
|
error_info = ""
|
||||||
|
driver = None
|
||||||
for retry in range(max_retries):
|
for retry in range(max_retries):
|
||||||
try:
|
try:
|
||||||
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
||||||
|
|
||||||
# 如果不是第一次尝试,先杀进程
|
if iskill:
|
||||||
# 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 = {
|
||||||
@@ -974,7 +946,6 @@ class ApproveTask:
|
|||||||
|
|
||||||
if browser and browser != "店铺不存在":
|
if browser and browser != "店铺不存在":
|
||||||
self.log(f"成功打开店铺 {shop_name}")
|
self.log(f"成功打开店铺 {shop_name}")
|
||||||
# break
|
|
||||||
else:
|
else:
|
||||||
self.log(f"打开店铺失败: {browser}", "WARNING")
|
self.log(f"打开店铺失败: {browser}", "WARNING")
|
||||||
driver = None
|
driver = None
|
||||||
@@ -1012,6 +983,7 @@ class ApproveTask:
|
|||||||
driver = None
|
driver = None
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
self.log(f"打开店铺异常: {traceback.format_exc()}", "INFO")
|
self.log(f"打开店铺异常: {traceback.format_exc()}", "INFO")
|
||||||
@@ -1030,9 +1002,40 @@ class ApproveTask:
|
|||||||
# 从执行列表中移除
|
# 从执行列表中移除
|
||||||
if shop_name in runing_shop:
|
if shop_name in runing_shop:
|
||||||
del runing_shop[shop_name]
|
del runing_shop[shop_name]
|
||||||
|
return driver
|
||||||
|
return driver
|
||||||
|
|
||||||
|
|
||||||
|
def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str):
|
||||||
|
"""处理单个店铺
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shop_item: 店铺信息
|
||||||
|
country_codes: 国家代码列表
|
||||||
|
task_id: 任务ID
|
||||||
|
risk_listing_filter: 风险商品筛选条件
|
||||||
|
"""
|
||||||
|
shop_name = shop_item.get("shopName", "未知店铺")
|
||||||
|
company_name = shop_item.get("companyName", "")
|
||||||
|
|
||||||
|
if not company_name:
|
||||||
|
self.log(f"店铺 {shop_name} 的公司名称为空,跳过", "WARNING")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
|
||||||
|
if task_id in runing_task:
|
||||||
|
runing_task[task_id]["current_shop"] = shop_name
|
||||||
|
|
||||||
|
# 将店铺添加到正在执行中的店铺列表
|
||||||
|
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
runing_shop[shop_name] = start_time
|
||||||
|
self.log(f"店铺 {shop_name} 已添加到执行列表,账号: {company_name},开始时间: {start_time}")
|
||||||
|
|
||||||
|
# 店铺打开重试最多3次
|
||||||
|
driver = None
|
||||||
|
max_retries = 3
|
||||||
|
iskill = False
|
||||||
|
|
||||||
# 处理每个国家
|
# 处理每个国家
|
||||||
for country_code in country_codes:
|
for country_code in country_codes:
|
||||||
# 检查是否收到暂停请求
|
# 检查是否收到暂停请求
|
||||||
@@ -1040,26 +1043,32 @@ class ApproveTask:
|
|||||||
self.log(f"检测到任务 {task_id} 的暂停请求,停止处理国家", "WARNING")
|
self.log(f"检测到任务 {task_id} 的暂停请求,停止处理国家", "WARNING")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# 打开店铺
|
||||||
|
driver = self.open_shop(max_retries=max_retries, company_name=company_name,
|
||||||
|
shop_name=shop_name, iskill=iskill)
|
||||||
|
if driver is None:
|
||||||
|
self.log(f"任务 {task_id} 启动店铺失败,结束任务", "ERROR")
|
||||||
|
for country_code in country_codes:
|
||||||
|
self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
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)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR")
|
self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR")
|
||||||
self.log(traceback.format_exc(), "ERROR")
|
self.log(traceback.format_exc(), "ERROR")
|
||||||
|
if "与页面的连接已断开" in e:
|
||||||
|
iskill = True
|
||||||
|
|
||||||
|
# 更新已处理国家数
|
||||||
|
if task_id in runing_task:
|
||||||
|
runing_task[task_id]["processed_countries"] += 1
|
||||||
# 最后回传,标记完成
|
# 最后回传,标记完成
|
||||||
try:
|
try:
|
||||||
self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
||||||
finally:
|
|
||||||
# 关闭店铺
|
|
||||||
try:
|
|
||||||
if driver:
|
|
||||||
self.log(f"关闭店铺 {shop_name}")
|
|
||||||
driver.close_store()
|
|
||||||
time.sleep(2)
|
|
||||||
except Exception as e:
|
|
||||||
self.log(f"关闭店铺失败: {str(e)}", "WARNING")
|
|
||||||
|
|
||||||
# 从正在执行中的店铺列表中移除
|
# 从正在执行中的店铺列表中移除
|
||||||
if shop_name in runing_shop:
|
if shop_name in runing_shop:
|
||||||
@@ -1176,7 +1185,6 @@ class ApproveTask:
|
|||||||
self.log(f"国家 {country_name} 有 {len(sku_ls)} 个需要审批的商品,开始处理...")
|
self.log(f"国家 {country_name} 有 {len(sku_ls)} 个需要审批的商品,开始处理...")
|
||||||
|
|
||||||
# 处理所有需要审批的商品(通过yield获取结果)
|
# 处理所有需要审批的商品(通过yield获取结果)
|
||||||
try:
|
|
||||||
for asin, status in driver.run_page_action():
|
for asin, status in driver.run_page_action():
|
||||||
# 检查是否收到暂停请求
|
# 检查是否收到暂停请求
|
||||||
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
||||||
@@ -1204,14 +1212,6 @@ class ApproveTask:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
import traceback
|
|
||||||
self.log(f"处理审批商品异常: {str(e)}", "ERROR")
|
|
||||||
self.log(traceback.format_exc(), "ERROR")
|
|
||||||
|
|
||||||
# 更新已处理国家数
|
|
||||||
if task_id in runing_task:
|
|
||||||
runing_task[task_id]["processed_countries"] += 1
|
|
||||||
|
|
||||||
self.log(f"国家 {country_name} 处理完成")
|
self.log(f"国家 {country_name} 处理完成")
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ class ZiniaoDriver:
|
|||||||
"cookieTypeLoad": 0,
|
"cookieTypeLoad": 0,
|
||||||
"cookieTypeSave": cookieTypeSave,
|
"cookieTypeSave": cookieTypeSave,
|
||||||
"runMode": "1",
|
"runMode": "1",
|
||||||
"isLoadUserPlugin": False,
|
"isLoadUserPlugin": True,
|
||||||
"pluginIdType": 1,
|
"pluginIdType": 1,
|
||||||
"privacyMode": isprivacy
|
"privacyMode": isprivacy
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,48 +309,18 @@ class MatchTak:
|
|||||||
runing_task[task_id]["status"] = "failed"
|
runing_task[task_id]["status"] = "failed"
|
||||||
runing_task[task_id]["error"] = str(e)
|
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 open_shop(self,max_retries,company_name,shop_name,iskill=False):
|
||||||
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:
|
|
||||||
shop_item: 店铺信息
|
|
||||||
country_codes: 国家代码列表
|
|
||||||
task_id: 任务ID
|
|
||||||
risk_listing_filter: 风险商品筛选条件
|
|
||||||
"""
|
|
||||||
shop_name = shop_item.get("shopName", "未知店铺")
|
|
||||||
company_name = shop_item.get("companyName", "")
|
|
||||||
|
|
||||||
if not company_name:
|
|
||||||
self.log(f"店铺 {shop_name} 的公司名称为空,跳过", "WARNING")
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
if task_id in runing_task:
|
|
||||||
runing_task[task_id]["current_shop"] = shop_name
|
|
||||||
|
|
||||||
# 将店铺添加到正在执行中的店铺列表
|
|
||||||
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
runing_shop[shop_name] = start_time
|
|
||||||
self.log(f"店铺 {shop_name} 已添加到执行列表,账号: {company_name},开始时间: {start_time}")
|
|
||||||
|
|
||||||
# 店铺打开重试最多3次
|
|
||||||
driver = None
|
|
||||||
max_retries = 3
|
|
||||||
|
|
||||||
error_info = ""
|
error_info = ""
|
||||||
|
driver = None
|
||||||
for retry in range(max_retries):
|
for retry in range(max_retries):
|
||||||
try:
|
try:
|
||||||
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
|
||||||
|
|
||||||
# 如果不是第一次尝试,先杀进程
|
if iskill:
|
||||||
# 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 = {
|
||||||
@@ -418,9 +388,41 @@ class MatchTak:
|
|||||||
# 从执行列表中移除
|
# 从执行列表中移除
|
||||||
if shop_name in runing_shop:
|
if shop_name in runing_shop:
|
||||||
del runing_shop[shop_name]
|
del runing_shop[shop_name]
|
||||||
|
return driver
|
||||||
|
return driver
|
||||||
|
|
||||||
|
# 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,limit:str=None):
|
||||||
|
"""处理单个店铺
|
||||||
|
|
||||||
|
Args:
|
||||||
|
shop_item: 店铺信息
|
||||||
|
country_codes: 国家代码列表
|
||||||
|
task_id: 任务ID
|
||||||
|
risk_listing_filter: 风险商品筛选条件
|
||||||
|
"""
|
||||||
|
shop_name = shop_item.get("shopName", "未知店铺")
|
||||||
|
company_name = shop_item.get("companyName", "")
|
||||||
|
|
||||||
|
if not company_name:
|
||||||
|
self.log(f"店铺 {shop_name} 的公司名称为空,跳过", "WARNING")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
|
||||||
|
if task_id in runing_task:
|
||||||
|
runing_task[task_id]["current_shop"] = shop_name
|
||||||
|
|
||||||
|
# 将店铺添加到正在执行中的店铺列表
|
||||||
|
start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
runing_shop[shop_name] = start_time
|
||||||
|
self.log(f"店铺 {shop_name} 已添加到执行列表,账号: {company_name},开始时间: {start_time}")
|
||||||
|
|
||||||
|
# 店铺打开重试最多3次
|
||||||
|
driver = None
|
||||||
|
max_retries = 3
|
||||||
|
iskill = False
|
||||||
|
|
||||||
# 处理每个国家
|
# 处理每个国家
|
||||||
for country_code in country_codes:
|
for country_code in country_codes:
|
||||||
# 检查是否收到暂停请求
|
# 检查是否收到暂停请求
|
||||||
@@ -428,12 +430,28 @@ class MatchTak:
|
|||||||
self.log(f"检测到任务 {task_id} 的暂停请求,停止处理国家", "WARNING")
|
self.log(f"检测到任务 {task_id} 的暂停请求,停止处理国家", "WARNING")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# 打开店铺
|
||||||
|
driver = self.open_shop(max_retries=max_retries, company_name=company_name,
|
||||||
|
shop_name=shop_name, iskill=iskill)
|
||||||
|
if driver is None:
|
||||||
|
self.log(f"任务 {task_id} 启动店铺失败,结束任务", "ERROR")
|
||||||
|
for country_code in country_codes:
|
||||||
|
self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.process_country(driver, country_code, task_id, shop_name,risk_listing_filter,limit)
|
self.process_country(driver, country_code, task_id, shop_name,risk_listing_filter,limit)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR")
|
self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR")
|
||||||
self.log(traceback.format_exc(), "ERROR")
|
self.log(traceback.format_exc(), "ERROR")
|
||||||
|
if "与页面的连接已断开" in e:
|
||||||
|
iskill = True
|
||||||
|
|
||||||
|
# 更新已处理国家数
|
||||||
|
if task_id in runing_task:
|
||||||
|
runing_task[task_id]["processed_countries"] += 1
|
||||||
|
|
||||||
# 最后回传,标记完成
|
# 最后回传,标记完成
|
||||||
try:
|
try:
|
||||||
# self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
# self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
||||||
@@ -443,15 +461,7 @@ class MatchTak:
|
|||||||
self.post_stage_finished(task_id, user_id, stage_index)
|
self.post_stage_finished(task_id, user_id, stage_index)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
||||||
finally:
|
|
||||||
# 关闭店铺
|
|
||||||
try:
|
|
||||||
if driver:
|
|
||||||
self.log(f"关闭店铺 {shop_name}")
|
|
||||||
driver.close_store()
|
|
||||||
time.sleep(2)
|
|
||||||
except Exception as e:
|
|
||||||
self.log(f"关闭店铺失败: {str(e)}", "WARNING")
|
|
||||||
|
|
||||||
# 从正在执行中的店铺列表中移除
|
# 从正在执行中的店铺列表中移除
|
||||||
if shop_name in runing_shop:
|
if shop_name in runing_shop:
|
||||||
@@ -487,6 +497,15 @@ class MatchTak:
|
|||||||
for retry in range(max_retries):
|
for retry in range(max_retries):
|
||||||
try:
|
try:
|
||||||
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
|
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
|
||||||
|
if retry > 1:
|
||||||
|
# 刷新不行就重新打开店铺
|
||||||
|
self.log("重试前重新打开店铺...")
|
||||||
|
try:
|
||||||
|
driver.close_store()
|
||||||
|
time.sleep(3)
|
||||||
|
driver.open_shop(shop_name)
|
||||||
|
except Exception as e:
|
||||||
|
self.log(f"关闭重新打开店铺: {str(e)}", "WARNING")
|
||||||
|
|
||||||
# 如果不是第一次尝试,先刷新页面
|
# 如果不是第一次尝试,先刷新页面
|
||||||
if retry > 0:
|
if retry > 0:
|
||||||
@@ -559,7 +578,6 @@ class MatchTak:
|
|||||||
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
|
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
|
||||||
|
|
||||||
# 处理所有需要审批的商品(通过yield获取结果)
|
# 处理所有需要审批的商品(通过yield获取结果)
|
||||||
try:
|
|
||||||
for asin, status in driver.run_page_action():
|
for asin, status in driver.run_page_action():
|
||||||
# 检查是否收到暂停请求
|
# 检查是否收到暂停请求
|
||||||
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
||||||
@@ -581,14 +599,7 @@ class MatchTak:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
import traceback
|
|
||||||
self.log(f"处理审批商品异常: {str(e)}", "ERROR")
|
|
||||||
self.log(traceback.format_exc(), "ERROR")
|
|
||||||
|
|
||||||
# 更新已处理国家数
|
|
||||||
if task_id in runing_task:
|
|
||||||
runing_task[task_id]["processed_countries"] += 1
|
|
||||||
|
|
||||||
self.log(f"国家 {country_name} 处理完成")
|
self.log(f"国家 {country_name} 处理完成")
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,30 @@ def calculate_standard_competitor_pricing(my_current_price, target_competitor_pr
|
|||||||
class ChromeAmzone:
|
class ChromeAmzone:
|
||||||
mark_name = "亚马逊详情采集"
|
mark_name = "亚马逊详情采集"
|
||||||
|
|
||||||
|
country_info = {
|
||||||
|
"英国": {
|
||||||
|
"url": "https://www.amazon.co.uk/dp/B0CJ8SNXXV",
|
||||||
|
"zip_code": "SW1A 1AA",
|
||||||
|
"mark": "SW1A 1"
|
||||||
|
},
|
||||||
|
"德国": {
|
||||||
|
"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,tab):
|
def __init__(self,tab):
|
||||||
self.tab = tab
|
self.tab = tab
|
||||||
|
|
||||||
@@ -136,8 +160,92 @@ class ChromeAmzone:
|
|||||||
if len(accept_btn) > 0:
|
if len(accept_btn) > 0:
|
||||||
accept_btn[0].click()
|
accept_btn[0].click()
|
||||||
|
|
||||||
|
def _set_zip_code(self, zip_code, mark=None):
|
||||||
|
"""
|
||||||
|
设置邮编
|
||||||
|
|
||||||
def run(self):
|
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 mark is not None and mark in current_text:
|
||||||
|
print(f"邮编检测到标识: {mark},无需修改")
|
||||||
|
return True
|
||||||
|
# 检查是否已经包含目标邮编
|
||||||
|
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()
|
||||||
|
|
||||||
|
continue_btn = self.tab.eles('xpath://div[@class="a-popover-footer"]//input[@id="GLUXConfirmClose"]',
|
||||||
|
timeout=10)
|
||||||
|
if len(continue_btn) > 0:
|
||||||
|
continue_btn[0].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 run(self,country):
|
||||||
"""
|
"""
|
||||||
运行亚马逊详情采集任务
|
运行亚马逊详情采集任务
|
||||||
|
|
||||||
@@ -149,6 +257,20 @@ class ChromeAmzone:
|
|||||||
dict: 包含采集到的数据
|
dict: 包含采集到的数据
|
||||||
"""
|
"""
|
||||||
try:
|
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"]
|
||||||
|
mark = country_config.get("mark")
|
||||||
|
|
||||||
|
self._set_zip_code(zip_code,mark)
|
||||||
|
|
||||||
|
self.tab.wait.doc_loaded(timeout=5,raise_err=False)
|
||||||
|
|
||||||
|
|
||||||
self.tab.wait.doc_loaded(timeout=30, raise_err=False)
|
self.tab.wait.doc_loaded(timeout=30, raise_err=False)
|
||||||
|
|
||||||
@@ -379,7 +501,15 @@ class AmzonePriceMatch(AmamzonBase):
|
|||||||
print("当前操作的tab_id",self.tab.tab_id)
|
print("当前操作的tab_id",self.tab.tab_id)
|
||||||
self.browser.close_tabs(close_tab)
|
self.browser.close_tabs(close_tab)
|
||||||
|
|
||||||
def run_page_action(self,current_shop_name:str,appoint_asin:str=None,skip_asin:list=[]):
|
def run_page_action(self,current_shop_name:str,mode:str,
|
||||||
|
appoint_asin:str=None,skip_asin:list=[],miniprice_info:dict={}):
|
||||||
|
"""
|
||||||
|
|
||||||
|
miniprice_info :
|
||||||
|
{
|
||||||
|
"B0DSJGJBWV" : "19.81" # asin : 最低价
|
||||||
|
}
|
||||||
|
"""
|
||||||
print(f"【{self.mark_name}】,开始执行")
|
print(f"【{self.mark_name}】,开始执行")
|
||||||
num = 0
|
num = 0
|
||||||
retry_num = 0
|
retry_num = 0
|
||||||
@@ -453,6 +583,24 @@ class AmzonePriceMatch(AmamzonBase):
|
|||||||
if len(current_price_ele) > 0:
|
if len(current_price_ele) > 0:
|
||||||
current_price = current_price_ele[0].attr("value")
|
current_price = current_price_ele[0].attr("value")
|
||||||
print("获取到当前价格为 ->",current_price)
|
print("获取到当前价格为 ->",current_price)
|
||||||
|
# 如果紫鸟里当前价格低于最低价则删除,否则跳过
|
||||||
|
if mode == "status" and miniprice_info.get(asin):
|
||||||
|
backend_price = float(miniprice_info.get(asin))
|
||||||
|
if float(current_price) < backend_price:
|
||||||
|
yield (asin, {
|
||||||
|
"statu": "删除(当前价格低于最低价)",
|
||||||
|
"deleteSkipAsin": True,
|
||||||
|
"removeAsin": asin,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
yield (asin, {
|
||||||
|
"statu": "跳过(当前价格不低于最低价)",
|
||||||
|
"deleteSkipAsin": True,
|
||||||
|
"removeAsin": asin,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"{self.mark_name} 没有获取到当前价格")
|
print(f"{self.mark_name} 没有获取到当前价格")
|
||||||
yield (asin, {
|
yield (asin, {
|
||||||
@@ -476,10 +624,16 @@ class AmzonePriceMatch(AmamzonBase):
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
pass
|
pass
|
||||||
chrome = ChromeAmzone(tab=new_tab)
|
chrome = ChromeAmzone(tab=new_tab)
|
||||||
front_end_data = chrome.run()
|
front_end_data = chrome.run(country=current_country)
|
||||||
chrome.close()
|
chrome.close()
|
||||||
print(self.mark_name,"亚马逊前台抓取到数据",front_end_data)
|
print(self.mark_name,"亚马逊前台抓取到数据",front_end_data)
|
||||||
#
|
if front_end_data is None or len(front_end_data.get("top_sellers")) == 0:
|
||||||
|
yield (asin, {
|
||||||
|
"statu": "失败(第一名获取失败,请检查卖家精灵是否启用)",
|
||||||
|
"currentPrice": current_price,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
|
||||||
cart_seller = front_end_data.get("cart_seller")
|
cart_seller = front_end_data.get("cart_seller")
|
||||||
if cart_seller == current_shop_name and len(front_end_data.get("top_sellers"))< 2:
|
if cart_seller == current_shop_name and len(front_end_data.get("top_sellers"))< 2:
|
||||||
yield (asin, {
|
yield (asin, {
|
||||||
@@ -769,6 +923,8 @@ class PriceTask:
|
|||||||
shopMallName = shop_item.get("shopMallName","")
|
shopMallName = shop_item.get("shopMallName","")
|
||||||
skip_asins_by_country = shop_item.get("skip_asins_by_country",{})
|
skip_asins_by_country = shop_item.get("skip_asins_by_country",{})
|
||||||
asin_rows_by_country = shop_item.get("asin_rows_by_country",{})
|
asin_rows_by_country = shop_item.get("asin_rows_by_country",{})
|
||||||
|
skip_asin_details_by_country = shop_item.get("skip_asin_details_by_country",{})
|
||||||
|
mode = shop_item.get("mode")
|
||||||
|
|
||||||
if not company_name:
|
if not company_name:
|
||||||
self.log(f"店铺 {shop_name} 的公司名称为空,跳过", "WARNING")
|
self.log(f"店铺 {shop_name} 的公司名称为空,跳过", "WARNING")
|
||||||
@@ -785,30 +941,42 @@ class PriceTask:
|
|||||||
# 店铺打开重试最多3次
|
# 店铺打开重试最多3次
|
||||||
driver = None
|
driver = None
|
||||||
max_retries = 3
|
max_retries = 3
|
||||||
|
iskill = False
|
||||||
|
# 处理每个国家
|
||||||
|
for country_code in country_codes:
|
||||||
# 打开店铺
|
# 打开店铺
|
||||||
driver = self.open_shop(max_retries=max_retries,company_name=company_name,shop_name=shop_name)
|
driver = self.open_shop(max_retries=max_retries, company_name=company_name,
|
||||||
|
shop_name=shop_name,iskill=iskill)
|
||||||
if driver is None:
|
if driver is None:
|
||||||
self.log(f"任务 {task_id} 启动店铺失败,结束任务", "ERROR")
|
self.log(f"任务 {task_id} 启动店铺失败,结束任务", "ERROR")
|
||||||
for country_code in country_codes:
|
for country_code in country_codes:
|
||||||
self.post_result(task_id, shop_name, country_code, "", {}, shopMallName, is_done=True)
|
self.post_result(task_id, shop_name, country_code, "", {}, shopMallName, is_done=True)
|
||||||
return
|
return
|
||||||
try:
|
|
||||||
# 处理每个国家
|
|
||||||
for country_code in country_codes:
|
|
||||||
# 检查是否收到暂停请求
|
# 检查是否收到暂停请求
|
||||||
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
||||||
self.log(f"检测到任务 {task_id} 的暂停请求,停止处理国家", "WARNING")
|
self.log(f"检测到任务 {task_id} 的暂停请求,停止处理国家", "WARNING")
|
||||||
break
|
break
|
||||||
skip_asin = skip_asins_by_country.get(country_code,[]) #需要跳过的asin
|
skip_asin = skip_asins_by_country.get(country_code,[]) #需要跳过的asin
|
||||||
appoint_asin = asin_rows_by_country.get(country_code,[])
|
appoint_asin = asin_rows_by_country.get(country_code,[])
|
||||||
|
miniprice_info = {i.get('asin'):i.get('minimumPrice') for i in skip_asin_details_by_country.get(country_code,[])}
|
||||||
try:
|
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,
|
||||||
shopMallName=shopMallName,skip_asin=skip_asin, limit=limit,appoint_asin=appoint_asin)
|
shopMallName=shopMallName,skip_asin=skip_asin, limit=limit,
|
||||||
|
appoint_asin=appoint_asin,mode=mode,miniprice_info=miniprice_info)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR")
|
self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR")
|
||||||
self.log(traceback.format_exc(), "ERROR")
|
self.log(traceback.format_exc(), "ERROR")
|
||||||
|
if "与页面的连接已断开" in e:
|
||||||
|
iskill = True
|
||||||
|
# 更新已处理国家数
|
||||||
|
if task_id in runing_task:
|
||||||
|
runing_task[task_id]["processed_countries"] += 1
|
||||||
|
|
||||||
|
# 关闭店铺
|
||||||
|
driver.close_store()
|
||||||
|
|
||||||
# 最后回传,标记完成
|
# 最后回传,标记完成
|
||||||
try:
|
try:
|
||||||
# self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
# self.post_result(task_id, shop_name, country_code, "", "", is_done=True)
|
||||||
@@ -818,15 +986,6 @@ class PriceTask:
|
|||||||
self.post_stage_finished(task_id, user_id, stage_index)
|
self.post_stage_finished(task_id, user_id, stage_index)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
||||||
finally:
|
|
||||||
# 关闭店铺
|
|
||||||
try:
|
|
||||||
if driver:
|
|
||||||
self.log(f"关闭店铺 {shop_name}")
|
|
||||||
driver.close_store()
|
|
||||||
time.sleep(2)
|
|
||||||
except Exception as e:
|
|
||||||
self.log(f"关闭店铺失败: {str(e)}", "WARNING")
|
|
||||||
|
|
||||||
# 从正在执行中的店铺列表中移除
|
# 从正在执行中的店铺列表中移除
|
||||||
if shop_name in runing_shop:
|
if shop_name in runing_shop:
|
||||||
@@ -834,7 +993,9 @@ class PriceTask:
|
|||||||
self.log(f"店铺 {shop_name} 已从执行列表中移除")
|
self.log(f"店铺 {shop_name} 已从执行列表中移除")
|
||||||
|
|
||||||
def process_country(self, driver: AmzonePriceMatch, country_code: str, task_id: int, shop_name: str,
|
def process_country(self, driver: AmzonePriceMatch, country_code: str, task_id: int, shop_name: str,
|
||||||
risk_listing_filter: str,shopMallName:str,skip_asin:list,appoint_asin:list,limit: str = None):
|
risk_listing_filter: str,shopMallName:str,skip_asin:list,appoint_asin:list,mode:str,
|
||||||
|
miniprice_info:dict={},
|
||||||
|
limit: str = None):
|
||||||
"""处理单个国家的审批任务
|
"""处理单个国家的审批任务
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -864,7 +1025,7 @@ class PriceTask:
|
|||||||
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
|
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
|
||||||
if retry > 1:
|
if retry > 1:
|
||||||
# 刷新不行就重新打开店铺
|
# 刷新不行就重新打开店铺
|
||||||
self.log("重试前刷新页面...")
|
self.log("重试前重新打开店铺...")
|
||||||
try:
|
try:
|
||||||
driver.close_store()
|
driver.close_store()
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
@@ -946,7 +1107,7 @@ class PriceTask:
|
|||||||
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
|
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
|
||||||
|
|
||||||
# 处理所有需要审批的商品(通过yield获取结果)
|
# 处理所有需要审批的商品(通过yield获取结果)
|
||||||
try:
|
|
||||||
# 指定 asin
|
# 指定 asin
|
||||||
max_range = max(1,len(appoint_asin))
|
max_range = max(1,len(appoint_asin))
|
||||||
for i in range(max_range):
|
for i in range(max_range):
|
||||||
@@ -959,7 +1120,8 @@ class PriceTask:
|
|||||||
|
|
||||||
for asin, status in driver.run_page_action(
|
for asin, status in driver.run_page_action(
|
||||||
current_shop_name=_shopMallName,
|
current_shop_name=_shopMallName,
|
||||||
appoint_asin=ap_asin,skip_asin=skip_asin
|
appoint_asin=ap_asin,skip_asin=skip_asin,mode=mode,
|
||||||
|
miniprice_info=miniprice_info
|
||||||
):
|
):
|
||||||
# 检查是否收到暂停请求
|
# 检查是否收到暂停请求
|
||||||
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
||||||
@@ -981,15 +1143,6 @@ class PriceTask:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
self.log(f"回传结果失败: {str(e)}", "ERROR")
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
import traceback
|
|
||||||
self.log(f"处理审批商品异常: {str(e)}", "ERROR")
|
|
||||||
self.log(traceback.format_exc(), "ERROR")
|
|
||||||
|
|
||||||
# 更新已处理国家数
|
|
||||||
if task_id in runing_task:
|
|
||||||
runing_task[task_id]["processed_countries"] += 1
|
|
||||||
|
|
||||||
|
|
||||||
self.log(f"国家 {country_name} 处理完成")
|
self.log(f"国家 {country_name} 处理完成")
|
||||||
|
|
||||||
@@ -1073,6 +1226,8 @@ class PriceTask:
|
|||||||
"secondPlace": status.get("secondPlace") if status.get("secondPlace") else "",
|
"secondPlace": status.get("secondPlace") if status.get("secondPlace") else "",
|
||||||
"cartShopName": status.get("cartShopName") if status.get("cartShopName") else "",
|
"cartShopName": status.get("cartShopName") if status.get("cartShopName") else "",
|
||||||
"priceChangeStatus": "UPDATED",
|
"priceChangeStatus": "UPDATED",
|
||||||
|
"deleteSkipAsin": status.get("deleteSkipAsin",False),
|
||||||
|
"removeAsin": status.get("removeAsin") if status.get("removeAsin") else "",
|
||||||
# "modifyCount": "2",
|
# "modifyCount": "2",
|
||||||
"status": status.get("statu")
|
"status": status.get("statu")
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,8 +5,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>格式转换 - 数富AI</title>
|
<title>格式转换 - 数富AI</title>
|
||||||
<script type="module" crossorigin src="/assets/convert.js"></script>
|
<script type="module" crossorigin src="/assets/convert.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-DuyK2jB1.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-9YBa--7x.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-CKgrwtni.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-CRmRvyGD.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/convert-7wWJ02Tw.css">
|
<link rel="stylesheet" crossorigin href="/assets/convert-7wWJ02Tw.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>数据去重 - 数富AI</title>
|
<title>数据去重 - 数富AI</title>
|
||||||
<script type="module" crossorigin src="/assets/dedupe.js"></script>
|
<script type="module" crossorigin src="/assets/dedupe.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-DuyK2jB1.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-9YBa--7x.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-CKgrwtni.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-CRmRvyGD.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/dedupe-BpNHwt51.css">
|
<link rel="stylesheet" crossorigin href="/assets/dedupe-BpNHwt51.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>删除品牌 - 数富AI</title>
|
<title>删除品牌 - 数富AI</title>
|
||||||
<script type="module" crossorigin src="/assets/delete-brand.js"></script>
|
<script type="module" crossorigin src="/assets/delete-brand.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-DuyK2jB1.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-9YBa--7x.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-CKgrwtni.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-CRmRvyGD.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/delete-brand-CWLpe7lu.css">
|
<link rel="stylesheet" crossorigin href="/assets/delete-brand-CWLpe7lu.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>数据拆分 - 数富AI</title>
|
<title>数据拆分 - 数富AI</title>
|
||||||
<script type="module" crossorigin src="/assets/split.js"></script>
|
<script type="module" crossorigin src="/assets/split.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-DuyK2jB1.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-9YBa--7x.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-CKgrwtni.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-CRmRvyGD.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-D74jYk6P.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/split-CRUIYKS6.css">
|
<link rel="stylesheet" crossorigin href="/assets/split-CRUIYKS6.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
Reference in New Issue
Block a user