modified: amazon/approve.py modified: amazon/chrome_base.py modified: amazon/detail_spider.py modified: amazon/main.py modified: amazon/price_match.py modified: amazon/similar_asin.py modified: amazon/tool.py new file: amazon/user_data/chrome_data/BrowserMetrics-spare.pma new file: amazon/user_data/chrome_data/BrowserMetrics/BrowserMetrics-69FAEF07-558.pma
863 lines
36 KiB
Python
863 lines
36 KiB
Python
# encoding utf-8
|
||
|
||
import json
|
||
from pathlib import Path
|
||
import base64
|
||
import time
|
||
import re
|
||
import traceback
|
||
from datetime import datetime
|
||
from collections import defaultdict
|
||
import requests
|
||
from urllib.parse import quote
|
||
|
||
from curl_cffi import requests as requests_frp
|
||
|
||
|
||
|
||
from amazon.tool import show_notification,get_shop_info,remove_special_characters,split_currency_values
|
||
from amazon.amazon_base import TaskBase
|
||
|
||
from amazon.chrome_base import ChromeAmzoneBase
|
||
|
||
from config import runing_task, runing_shop,base_dir,DELETE_BRAND_API_BASE
|
||
|
||
try:
|
||
from config import proxy_url as CONFIG_PROXY_URL, proxy_mode as CONFIG_PROXY_MODE
|
||
except ImportError:
|
||
CONFIG_PROXY_URL = None
|
||
CONFIG_PROXY_MODE = 1
|
||
|
||
# Forbidden 后 3 分钟内统一使用代理:记录代理生效截止时间与当前代理
|
||
_FORBIDDEN_PROXY_UNTIL = 0.0
|
||
_FORBIDDEN_PROXY_DICT = None
|
||
_FORBIDDEN_PROXY_MINUTES = 0.5
|
||
|
||
|
||
class ChromeAmzone(ChromeAmzoneBase):
|
||
mark_name = "亚马逊相似ASIN"
|
||
|
||
def wait_load_complete(self):
|
||
try:
|
||
for _ in range(3):
|
||
content_area = self.tab.eles('xpath://div[@class="ap-sbi-aside__main"]', timeout=20)
|
||
if len(content_area) > 0:
|
||
pass
|
||
|
||
except Exception as e:
|
||
self.log(f"等待加载完成失败,{e},等待5秒")
|
||
time.sleep(5)
|
||
|
||
def encode_custom(self,input_):
|
||
if not input_ or not isinstance(input_, str):
|
||
return ""
|
||
|
||
transformed = ""
|
||
|
||
# 第一个字符:字符编码 + 字符串长度
|
||
transformed += chr((ord(input_[0]) + len(input_)) & 0xFFFF)
|
||
|
||
# 后续字符:当前字符编码 + 前一个字符编码
|
||
for i in range(1, len(input_)):
|
||
current_code = ord(input_[i])
|
||
prev_code = ord(input_[i - 1])
|
||
transformed += chr((current_code + prev_code) & 0xFFFF)
|
||
|
||
# 对应 JavaScript 的 encodeURIComponent
|
||
encoded = quote(transformed, safe="-_.!~*'()")
|
||
|
||
# 对应 replace(/[!'()*]/g, ...)
|
||
for ch in ["!", "'", "(", ")", "*"]:
|
||
encoded = encoded.replace(ch, "%" + format(ord(ch), "x"))
|
||
return encoded
|
||
|
||
def image_to_base64(self,image_source: str) -> str:
|
||
"""
|
||
将图片链接(URL 或本地文件路径)转换为 Base64 编码的字符串。
|
||
"""
|
||
if image_source.startswith(('http://', 'https://')):
|
||
response = requests.get(image_source, timeout=10)
|
||
response.raise_for_status() # 非 2xx 状态码将抛出异常
|
||
image_data = response.content
|
||
else:
|
||
path = Path(image_source)
|
||
if not path.is_file():
|
||
raise ValueError(f"本地文件不存在: {image_source}")
|
||
with open(path, 'rb') as f:
|
||
image_data = f.read()
|
||
base64_str = base64.b64encode(image_data).decode('utf-8')
|
||
return base64_str
|
||
|
||
def get_aliprice_data(self,page=1,title="",domain="",category="",imageBase64=""):
|
||
"""
|
||
"fishkeeper Quick Aquarium Siphon Pump Gravel Cleaner - 256GPH Adjustable Powerful Fish Tank Vacuum Gravel Cleaning Kit for Aquarium Water Changer, Sand Cleaner, Dirt Removal : Amazon.co.uk: Pet Supplies"
|
||
标题 :
|
||
|
||
"""
|
||
global _FORBIDDEN_PROXY_UNTIL, _FORBIDDEN_PROXY_DICT
|
||
|
||
headers = {
|
||
"accept": "application/json, text/plain, */*",
|
||
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8",
|
||
"browser": "chrome",
|
||
"cache-control": "no-cache",
|
||
"channel": "chrome_offline",
|
||
"content-type": "application/json;charset=UTF-8",
|
||
"ext-id": "10100",
|
||
"ext_id": "10100",
|
||
"origin": "chrome-extension://ephkdklmdkaakeleplfpahjphaokcllh",
|
||
"platform": "1688",
|
||
"pragma": "no-cache",
|
||
"priority": "u=1, i",
|
||
"sec-fetch-dest": "empty",
|
||
"sec-fetch-mode": "cors",
|
||
"sec-fetch-site": "none",
|
||
"sec-fetch-storage-access": "active",
|
||
"version": "3.7.4",
|
||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36"
|
||
}
|
||
cookie = {
|
||
"e-info": "[{\"e-name\":\"1688\",\"adid\":\"100\",\"version\":\"3.7.4\",\"ext_id\":\"10100\"}]",
|
||
"language": "chinese",
|
||
"province_code": "Guangdong",
|
||
"_ct": "czg",
|
||
"_e_ct": "czg",
|
||
"is_reto": "1",
|
||
"crossborder": "1",
|
||
"agent": "0",
|
||
"first_view": "1",
|
||
"currency": "USD",
|
||
"is_coo": "1",
|
||
"plugin": "chrome",
|
||
"plugin_ext": "100,129",
|
||
"m-info": "[{\"platform\":\"1688\",\"version\":\"3.7.4\",\"browser\":\"chrome\",\"m\":\"uni\",\"t\":1777902599578}]"
|
||
}
|
||
url = "https://api.aliprice.com/index.php/chrome/items/imageAnalysis"
|
||
itemTitle = f"{title} : {domain}: {category}"
|
||
|
||
params = {"page": page, "size": 20, "website": "1688_lite2", "language": "zh-CN", "currency": "USD", "from": "",
|
||
"itemTitle": itemTitle, "domain": domain }
|
||
params['sign'] = self.encode_custom(json.dumps(params, ensure_ascii=False))
|
||
data = {
|
||
"imageBase64" : imageBase64
|
||
}
|
||
|
||
proxies = None
|
||
|
||
# 若 3 分钟内曾出现 Forbidden,则直接使用当时保存的代理
|
||
now = time.time()
|
||
if now < _FORBIDDEN_PROXY_UNTIL and _FORBIDDEN_PROXY_DICT:
|
||
proxies = _FORBIDDEN_PROXY_DICT
|
||
print("处于 Forbidden 代理窗口内,直接使用代理", proxies)
|
||
|
||
# 发送第一次请求
|
||
try:
|
||
response = requests_frp.post(url, headers=headers, params=params, json=data, impersonate="chrome101",
|
||
cookies=cookie, proxies=proxies)
|
||
response.encoding = "utf-8"
|
||
|
||
# 检查响应状态码和数据有效性
|
||
need_retry = False
|
||
if response.status_code != 200:
|
||
print(f"请求失败,状态码: {response.status_code}")
|
||
need_retry = True
|
||
else:
|
||
try:
|
||
result_data = response.json()
|
||
# 检查是否获取到有效数据
|
||
if not result_data or not isinstance(result_data, dict):
|
||
print("返回数据为空或格式不正确")
|
||
need_retry = True
|
||
elif "Forbidden" in response.text or "Too Many Requests" in response.text:
|
||
print("返回Forbidden或Too Many Requests")
|
||
need_retry = True
|
||
except Exception as e:
|
||
print(f"解析响应JSON失败: {e}")
|
||
need_retry = True
|
||
|
||
# 如果需要重试且配置了代理URL
|
||
if need_retry and CONFIG_PROXY_URL and not proxies:
|
||
try:
|
||
proxy_resp = requests.get(CONFIG_PROXY_URL, timeout=10)
|
||
print("代理请求结果->:", proxy_resp.text)
|
||
|
||
# 模式 2:账号密码代理,接口返回 JSON
|
||
if CONFIG_PROXY_MODE == 2:
|
||
resp_json = proxy_resp.json()
|
||
proxy_list = resp_json.get("data", {}).get("list") or []
|
||
first_item = proxy_list[0] if proxy_list else None
|
||
if first_item:
|
||
ip = first_item.get("ip")
|
||
port = first_item.get("port")
|
||
account = first_item.get("account")
|
||
password = first_item.get("password")
|
||
if ip and port and account and password:
|
||
auth_proxy = f"{account}:{password}@{ip}:{port}"
|
||
print("获取到账号密码代理:", auth_proxy)
|
||
proxies = {
|
||
"http": f"http://{auth_proxy}",
|
||
"https": f"http://{auth_proxy}",
|
||
}
|
||
# 默认模式 1:普通 IP:port 文本
|
||
if CONFIG_PROXY_MODE != 2:
|
||
proxy_ip = (proxy_resp.text or "").strip()
|
||
if proxy_ip:
|
||
proxies = {
|
||
"http": f"http://{proxy_ip}",
|
||
"https": f"http://{proxy_ip}",
|
||
}
|
||
|
||
if proxies:
|
||
print("使用代理重试请求")
|
||
response = requests_frp.post(url, headers=headers, params=params, json=data,
|
||
impersonate="chrome101", cookies=cookie, proxies=proxies)
|
||
response.encoding = "utf-8"
|
||
print("代理重试结果状态码:", response.status_code)
|
||
|
||
# 记录 3 分钟内都使用该代理
|
||
_FORBIDDEN_PROXY_UNTIL = now + _FORBIDDEN_PROXY_MINUTES * 60
|
||
_FORBIDDEN_PROXY_DICT = proxies
|
||
|
||
except Exception as e:
|
||
print("获取代理或重试失败:", e)
|
||
|
||
# 返回最终结果
|
||
result_data = response.json()
|
||
return result_data
|
||
|
||
except Exception as e:
|
||
print(f"get_aliprice_data 请求异常: {e}")
|
||
return {}
|
||
|
||
def _scrape_data(self):
|
||
"""
|
||
抓取商品数据
|
||
|
||
Returns:
|
||
dict: 抓取到的数据
|
||
"""
|
||
data = {
|
||
'image_url': "",
|
||
'title': "",
|
||
'category' : '',
|
||
'success' : True
|
||
}
|
||
|
||
try:
|
||
# 等待页面加载
|
||
# time.sleep(3)
|
||
title_ele = self.tab.ele('xpath://h1[@id="title"]',timeout=30)
|
||
title = title_ele.text
|
||
data["title"] = title
|
||
imge_ele = self.tab.ele('xpath://div[@id="imgTagWrapperId"]//img',timeout=20)
|
||
image_url = imge_ele.attr("src")
|
||
data["image_url"] = image_url
|
||
category = self.tab.eles('xpath://div[@id="wayfinding-breadcrumbs_feature_div"]//span[@class="a-list-item"]//a[@class="a-link-normal a-color-tertiary"]',timeout=20)
|
||
if len(category) > 0:
|
||
data["category"] = category[0].text
|
||
return data
|
||
|
||
except Exception as e:
|
||
print(f"抓取数据时出错: {traceback.format_exc()}")
|
||
data["success"] = False
|
||
return data
|
||
|
||
def run(self, country, asin,total_page=2):
|
||
"""
|
||
运行亚马逊详情采集任务
|
||
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"]
|
||
mark = country_config.get("mark")
|
||
|
||
# 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)
|
||
|
||
self.close_init_popup()
|
||
# 2. 切换国家/设置邮编
|
||
print(f"正在检查并设置邮编: {zip_code},标识: {mark}")
|
||
self._set_zip_code(zip_code, mark)
|
||
|
||
# self.tab.wait.doc_loaded(timeout=5, raise_err=False)
|
||
|
||
# 3. 抓取数据
|
||
print("正在抓取商品数据...")
|
||
data = self._scrape_data()
|
||
|
||
# 判断是否采集标题出错
|
||
new_size = "220,220"
|
||
pattern = r"\._(?:[A-Z]+)?(\d+)_\."
|
||
replacement = f"._{new_size}_.jpg"
|
||
# 使用正则替换
|
||
image_new_url = re.sub(pattern, lambda m: replacement, data["image_url"])
|
||
|
||
iamge_base64 = self.image_to_base64(image_new_url)
|
||
|
||
similar_data = []
|
||
# 4、请求获取插件的数据
|
||
for page_num in range(total_page):
|
||
resp_data = self.get_aliprice_data(
|
||
page=page_num,
|
||
title = data["title"],
|
||
domain=domain,
|
||
category=data["category"],
|
||
imageBase64 = iamge_base64
|
||
)
|
||
self.log(f"Aliprice 扩展数据获取:{resp_data}")
|
||
if len(resp_data.get("data",[])) > 0:
|
||
for i in resp_data.get("data"):
|
||
similar_data.append(i)
|
||
|
||
data["similar_data"] = similar_data
|
||
|
||
# 添加基本信息
|
||
data['country'] = country
|
||
data['asin'] = asin
|
||
data['url'] = product_url
|
||
data['timestamp'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||
|
||
print(f"数据抓取完成: {json.dumps(data)}")
|
||
return data
|
||
|
||
except Exception as e:
|
||
error_msg = f"运行出错: {traceback.format_exc()}"
|
||
print(error_msg)
|
||
show_notification(f"采集失败: {str(e)}", "error")
|
||
return {}
|
||
|
||
|
||
class SimilarAsinTask(TaskBase):
|
||
mark_name = "相似ASIN采集"
|
||
|
||
@staticmethod
|
||
def group_by_id_prefix(data):
|
||
"""
|
||
根据每条数据的 id 字段进行分组:
|
||
- 如果 id 为 "2_1",则取 "_" 前面的 "2" 作为分组 key
|
||
- 如果 id 为 "1",则分组 key 就是 "1"
|
||
- 相同 key 的数据归为同一组
|
||
|
||
:param data: 原始列表数据
|
||
:return: 二维列表,按 id 前缀分组
|
||
"""
|
||
grouped = defaultdict(list)
|
||
|
||
for item in data:
|
||
item_id = str(item.get("id", ""))
|
||
group_key = item_id.split("_")[0]
|
||
grouped[group_key].append(item)
|
||
|
||
return list(grouped.values())
|
||
|
||
@staticmethod
|
||
def normalize_groups(data):
|
||
groups = data.get("groups")
|
||
if isinstance(groups, list) and len(groups) > 0:
|
||
return groups
|
||
|
||
rows = data.get("rows") or data.get("items") or []
|
||
if not isinstance(rows, list) or len(rows) == 0:
|
||
return []
|
||
|
||
normalized = []
|
||
for items in SimilarAsinTask.group_by_id_prefix(rows):
|
||
first = items[0] if items else {}
|
||
item_id = str(first.get("id") or first.get("displayId") or "")
|
||
base_id = item_id.split("_")[0] if item_id else ""
|
||
normalized.append({
|
||
"sourceFileKey": first.get("sourceFileKey", ""),
|
||
"sourceFilename": first.get("sourceFilename", ""),
|
||
"groupKey": first.get("groupKey") or base_id,
|
||
"baseId": first.get("baseId") or base_id,
|
||
"displayId": first.get("displayId") or item_id,
|
||
"items": items,
|
||
})
|
||
return normalized
|
||
|
||
def process_task(self, task_data: dict):
|
||
"""处理审批任务主入口
|
||
|
||
Args:
|
||
task_data: 任务数据
|
||
"""
|
||
try:
|
||
data = task_data.get("data", {})
|
||
task_id = data.get("taskId")
|
||
groups = self.normalize_groups(data)
|
||
|
||
if not task_id:
|
||
self.log("任务ID为空,跳过", "WARNING")
|
||
return
|
||
|
||
|
||
self.log(f"开始处理爬取任务 {task_id},{len(groups)} 个任务")
|
||
|
||
if not groups:
|
||
self.log("appearance-patent groups/rows is empty, skip", "WARNING")
|
||
return
|
||
|
||
runing_task[task_id] = {
|
||
"status": "running",
|
||
"start_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||
"total_shops": 1,
|
||
"processed_shops": 0,
|
||
"total_countries": len(groups) ,
|
||
"processed_countries": 0,
|
||
"total_asins": 0,
|
||
"processed_asins": 0,
|
||
"success_count": 0,
|
||
"failed_count": 0,
|
||
"stop_requested": False
|
||
}
|
||
|
||
# 检查是否收到暂停请求
|
||
if task_id in runing_task and runing_task[task_id].get("stop_requested", False):
|
||
self.log(f"检测到任务 {task_id} 的暂停请求,停止处理", "WARNING")
|
||
runing_task[task_id]["status"] = "stopped"
|
||
return
|
||
max_retry = 3
|
||
show_notification(f"开始爬取数据", "info")
|
||
chrome = ChromeAmzone()
|
||
try:
|
||
# 数据整理
|
||
# new_items = self.group_by_id_prefix(items)
|
||
result = []
|
||
for gp_index,gp in enumerate(groups):
|
||
items = gp.get("items", [])
|
||
return_data = {}
|
||
|
||
for index,value in enumerate(items):
|
||
|
||
return_data = {
|
||
'image_url': "",
|
||
'title': ""
|
||
}
|
||
asin = value.get("asin")
|
||
country = value.get("country")
|
||
return_data = None
|
||
for _ in range(max_retry):
|
||
try:
|
||
return_data = chrome.run(country, asin) or {}
|
||
self.log(f"抓取结果->{return_data}")
|
||
break
|
||
except Exception as e:
|
||
if "与页面的连接已断开" in str(e):
|
||
chrome = ChromeAmzone()
|
||
if not isinstance(return_data, dict):
|
||
return_data = {}
|
||
if return_data.get("image_url"):
|
||
break
|
||
|
||
group_item = [
|
||
{
|
||
"sourceFileKey": i.get("sourceFileKey"),
|
||
"sourceFilename": i.get("sourceFilename"),
|
||
"rowToken": i.get("rowToken"),
|
||
"groupKey": i.get("groupKey"),
|
||
"id": i.get("id"),
|
||
"asin": i.get("asin"),
|
||
"country": i.get("country"),
|
||
"url": return_data.get("image_url"),
|
||
"title": return_data.get("title"),
|
||
"done": False,
|
||
"urls" : [i.get("ori_picture") for i in return_data.get("similar_data")]
|
||
}
|
||
for i in items
|
||
]
|
||
# task_id: int, chunkIndex:int,chunkTotal: int, country_code: str, asin: str, status: dict,error:str="",
|
||
# item_data:dict={},
|
||
res = {
|
||
"sourceFileKey": gp.get("sourceFileKey"),
|
||
"sourceFilename": gp.get("sourceFilename"),
|
||
"groupKey": gp.get("groupKey"),
|
||
"baseId": gp.get("baseId"),
|
||
"displayId": gp.get("displayId"),
|
||
"items": group_item
|
||
}
|
||
|
||
print("================")
|
||
result.append(res)
|
||
print("================")
|
||
is_done = gp_index == len(groups)-1
|
||
if len(result) > 20 or is_done:
|
||
self.post_result(task_id=task_id,chunkIndex=gp_index+1,chunkTotal=len(groups),
|
||
asin=asin,item_data=result,is_done=is_done)
|
||
result = []
|
||
|
||
|
||
if len(result) > 0:
|
||
is_done = True
|
||
self.post_result(task_id=task_id, chunkIndex=len(groups), chunkTotal=len(groups),
|
||
asin=asin, item_data=result, is_done=is_done)
|
||
|
||
try:
|
||
chrome.close()
|
||
except Exception as e:
|
||
print("退出浏览器出错",e)
|
||
# 更新已处理店铺数
|
||
if task_id in runing_task:
|
||
runing_task[task_id]["processed_shops"] += 1
|
||
except Exception as e:
|
||
self.log(f"处理店铺 {task_id} 失败: {str(e)}", "ERROR")
|
||
self.log(traceback.format_exc(), "ERROR")
|
||
|
||
# 更新任务状态
|
||
if task_id in runing_task:
|
||
if runing_task[task_id].get("stop_requested", False):
|
||
runing_task[task_id]["status"] = "stopped"
|
||
self.log(f"任务 {task_id} 已被暂停!")
|
||
else:
|
||
runing_task[task_id]["status"] = "completed"
|
||
self.log(f"任务 {task_id} 处理完成!")
|
||
|
||
except Exception as e:
|
||
self.log(f"任务处理失败: {traceback.format_exc()}", "ERROR")
|
||
if task_id:
|
||
if task_id in runing_task:
|
||
runing_task[task_id]["status"] = "failed"
|
||
runing_task[task_id]["error"] = str(e)
|
||
|
||
def post_result(self, task_id: int, chunkIndex:int,chunkTotal: int, asin: str, error:str="",
|
||
item_data:dict={},
|
||
is_done: bool = False):
|
||
"""回传处理结果到API
|
||
"""
|
||
|
||
url = f"{DELETE_BRAND_API_BASE}/api/similar-asin/tasks/{task_id}/result"
|
||
|
||
payload ={
|
||
"submissionId": f"{task_id}",
|
||
"chunkIndex": chunkIndex,
|
||
"chunkTotal": chunkTotal,
|
||
"error": error,
|
||
"groups": item_data,
|
||
"done": is_done
|
||
}
|
||
|
||
max_retries = 3
|
||
for retry in range(max_retries):
|
||
try:
|
||
request_timeout = 300 if is_done else 30
|
||
print("================【详情采集】=====================")
|
||
self.log(f"尝试回传结果 (第 {retry + 1}/{max_retries} 次)")
|
||
self.log(f"回传URL: {url}")
|
||
self.log(f"回传数据: {payload}")
|
||
response = requests.post(
|
||
url,
|
||
json=payload,
|
||
headers={"Content-Type": "application/json"},
|
||
timeout=request_timeout,
|
||
verify=False
|
||
)
|
||
self.log(f"回传结果: {response.text}")
|
||
data = response.json() if response.text else {}
|
||
if response.status_code == 200 and isinstance(data, dict) and data.get("success"):
|
||
self.log(f"结果回传成功: {asin} - {item_data}")
|
||
return
|
||
else:
|
||
self.log(f"结果回传失败,状态码: {response.status_code}", "WARNING")
|
||
print("=====================================")
|
||
|
||
except Exception as e:
|
||
self.log(f"调用API异常: {str(e)}", "ERROR")
|
||
print("=====================================")
|
||
|
||
# 如果还有重试机会,等待后继续
|
||
if retry < max_retries - 1:
|
||
time.sleep(2)
|
||
|
||
self.log(f"已达到最大重试次数,结果回传最终失败", "ERROR")
|
||
raise RuntimeError("已达到最大重试次数,结果回传最终失败")
|
||
|
||
if __name__ == '__main__':
|
||
spide = ChromeAmzone()
|
||
|
||
resp = spide.run(
|
||
country="英国", asin="B0F79LCB3X", total_page=2
|
||
)
|
||
print(resp)
|
||
# task_data = {
|
||
# "type": "similar-asin-run",
|
||
# "ts": 1778038688824,
|
||
# "data": {
|
||
# "taskId": 6850,
|
||
# "api_key": "sk-EMcDFg36zCeWUtCbRzKUbrRZyNeC6M4KBhY6fAVcNP7GG4xI",
|
||
# "groups": [
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "groupKey": "similar-asin:6850",
|
||
# "baseId": "",
|
||
# "displayId": "1",
|
||
# "items": [
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::2",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::1@2",
|
||
# "id": "1",
|
||
# "asin": "B0D792ND9V",
|
||
# "country": "英国",
|
||
# "price": "12.29",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::3",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_1",
|
||
# "asin": "B0D14L34S7",
|
||
# "country": "英国",
|
||
# "price": "14.88",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::4",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_2",
|
||
# "asin": "B0D14N8FJY",
|
||
# "country": "英国",
|
||
# "price": "15.36",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::5",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_3",
|
||
# "asin": "B0D14NBL5D",
|
||
# "country": "英国",
|
||
# "price": "14.87",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::6",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_4",
|
||
# "asin": "B0D14M5X4J",
|
||
# "country": "英国",
|
||
# "price": "14.41",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::7",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::3@7",
|
||
# "id": "3_1",
|
||
# "asin": "B0CWQFZMGY",
|
||
# "country": "英国",
|
||
# "price": "17.05",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::8",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::3@7",
|
||
# "id": "3_2",
|
||
# "asin": "B0CX8D68L3",
|
||
# "country": "英国",
|
||
# "price": "16.46",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::9",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::4@9",
|
||
# "id": "4_1",
|
||
# "asin": "B0CX87XXWJ",
|
||
# "country": "英国",
|
||
# "price": "17.54",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::10",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::5@10",
|
||
# "id": "5_1",
|
||
# "asin": "B0CR3PMR8L",
|
||
# "country": "英国",
|
||
# "price": "16.74",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# }
|
||
# ]
|
||
# }
|
||
# ],
|
||
# "rows": [
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::2",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::1@2",
|
||
# "id": "1",
|
||
# "asin": "B0D792ND9V",
|
||
# "country": "英国",
|
||
# "price": "12.29",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::3",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_1",
|
||
# "asin": "B0D14L34S7",
|
||
# "country": "英国",
|
||
# "price": "14.88",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::4",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_2",
|
||
# "asin": "B0D14N8FJY",
|
||
# "country": "英国",
|
||
# "price": "15.36",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::5",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_3",
|
||
# "asin": "B0D14NBL5D",
|
||
# "country": "英国",
|
||
# "price": "14.87",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::6",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::2@3",
|
||
# "id": "2_4",
|
||
# "asin": "B0D14M5X4J",
|
||
# "country": "英国",
|
||
# "price": "14.41",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::7",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::3@7",
|
||
# "id": "3_1",
|
||
# "asin": "B0CWQFZMGY",
|
||
# "country": "英国",
|
||
# "price": "17.05",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::8",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::3@7",
|
||
# "id": "3_2",
|
||
# "asin": "B0CX8D68L3",
|
||
# "country": "英国",
|
||
# "price": "16.46",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::9",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::4@9",
|
||
# "id": "4_1",
|
||
# "asin": "B0CX87XXWJ",
|
||
# "country": "英国",
|
||
# "price": "17.54",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# },
|
||
# {
|
||
# "sourceFileKey": "cdfa437df62b40a0bde5682a05c1f6a3",
|
||
# "sourceFilename": "17(1) - 副本1.xlsx",
|
||
# "rowToken": "cdfa437df62b40a0bde5682a05c1f6a3::row::10",
|
||
# "groupKey": "cdfa437df62b40a0bde5682a05c1f6a3::5@10",
|
||
# "id": "5_1",
|
||
# "asin": "B0CR3PMR8L",
|
||
# "country": "英国",
|
||
# "price": "16.74",
|
||
# "url": "",
|
||
# "title": "",
|
||
# "target_urls": []
|
||
# }
|
||
# ]
|
||
# }
|
||
# }
|
||
# sim_asin = SimilarAsinTask({})
|
||
# sim_asin.process_task(task_data)
|
||
|
||
|
||
|