# -*- coding: utf-8 -*- # Author : Charley # Python : 3.12.10 # Date : 2026/08/04 """得卡 DECA 商品卡牌清单(checklist)采集:拉某商品 groupbuy/card/list 的全部卡牌,入 deca_onsale_kami_record。 翻页「翻到底」(靠 data.total 判断),uuid 唯一键 INSERT IGNORE 去重。 本接口需 Authorization Bearer,复用 deca_on_sale_daily_spider 的签名与 token 续期逻辑。 字段对应(列表项): sortOrder 序号 / aliasName 别名 / teamNameEn·teamNameZh 球队 / playerNameEn·playerNameZh 球员 / cardType 卡种 / limitNumber 限编 / cardNumber 卡编 / uuid 卡牌唯一标识 用法: python km_spdier.py <商品code> # 采集指定商品的全部卡牌清单,默认用示例 code 或作为模块:from km_spdier import get_card_list; get_card_list(log, code, pool) """ import sys import time from loguru import logger from mysql_pool import MySQLConnectionPool import deca_on_sale_daily_spider as deca # 复用 do_request / ensure_token / make_signature # 覆盖 deca 导入时装的 logger handler,本任务日志独立到 kami_*.log logger.remove() logger.add("./logs/kami_{time:YYYYMMDD}.log", encoding="utf-8", rotation="00:00", format="[{time:YYYY-MM-DD HH:mm:ss.SSS}] {level} {message}", level="DEBUG", retention="7 day") PAGE_SIZE = 20 # 每页条数(抓包实测 20) MAX_PAGES = 600 # 翻页保护上限(单商品清单可上万张,20/页约需数百页) def parse_kami(item: dict, product_code: str) -> dict | None: """把卡牌清单项解析成 deca_onsale_kami_record 一行。 Args: item (dict): card/list 的 data.list 项。 product_code (str): 所属商品编码(列表项不含,由外部带入)。 Returns: dict | None: 与 deca_onsale_kami_record 列对应的数据字典;无 uuid 时返回 None。 """ uuid = item.get("uuid") if not uuid: return None return { "uuid": uuid, "product_code": product_code, "sort_order": item.get("sortOrder"), "alias_name": item.get("aliasName"), "team_name_en": item.get("teamNameEn"), "team_name_zh": item.get("teamNameZh"), "player_name_en": item.get("playerNameEn"), "player_name_zh": item.get("playerNameZh"), "card_type": item.get("cardType"), "limit_number": item.get("limitNumber"), "card_number": item.get("cardNumber"), } def get_card_list(log, product_code: str, pool) -> int: """翻页拉取某商品的全部卡牌清单(翻到底),INSERT IGNORE 写入 deca_onsale_kami_record。 Args: log: 日志对象。 product_code (str): 商品编码。 pool (MySQLConnectionPool): MySQL 连接池。 Returns: int: 本商品实际写入的卡牌数(已去重)。 """ page = 1 saved = 0 total = None while page <= MAX_PAGES: body = {"code": product_code, "page": page, "pageSize": PAGE_SIZE} try: log.info(f"商品 {product_code} 卡牌清单请求第 {page} 页") resp = deca.do_request(log, "/api/v1/app/groupbuy/card/list", body, need_auth=True) except Exception as e: log.error(f"商品 {product_code} 卡牌清单第 {page} 页请求失败: {e}") break if not resp or resp.get("code") != 0: log.info(f"商品 {product_code} 卡牌清单返回异常: {resp.get('msg') if resp else None}") break data = resp.get("data") or {} if total is None: total = data.get("total") log.info(f"商品 {product_code} 卡牌清单共 {total} 张,开始翻页入库") items = data.get("list") or [] if not items: break rows = [r for r in (parse_kami(it, product_code) for it in items) if r] if rows: pool.insert_many(table="deca_onsale_kami_record", data_list=rows, ignore=True) saved += len(rows) # 翻到底:本页不足一页( 1 else "GB26082040642" main(code)