# -*- coding: utf-8 -*- # Author : Charley # Python : 3.12.10 # Date : 2026/07/02 """卡集历史全量采集脚本(一次性)。 与 kj_daily_spider(每天增量)的区别:本脚本对每个商户的已售列表「全量深翻」所有页 (调 get_sold_list(incremental=False),不走 start_at 停止线早停),把历史商品 / 玩家 / 视频 一次性灌满。跑一次即止,不带 schedule。 公共函数(请求 / 签名 / 解析 / 翻页 / 补采)全部从 kj_daily_spider 复用,避免重复维护。 运行:python kj_history_spider.py """ import sys from loguru import logger from mysql_pool import MySQLConnectionPool from kj_daily_spider import ( get_shop_list, get_sold_list, get_player_list, refill_videos, ) def history_main(log): """历史全量采集主函数:商户发现 → 全量深翻商品 → 全量采玩家 → 补视频。 Args: log: 日志对象。 Raises: RuntimeError: 数据库连接池异常时抛出。 """ log.info(f"开始运行 {sys._getframe().f_code.co_name} 卡集历史全量采集" + "." * 40) sql_pool = MySQLConnectionPool(log=log) if not sql_pool.check_pool_health(): log.error("数据库连接池异常") raise RuntimeError("数据库连接池异常") try: # 1) 商户发现:先用在售主列表把当前活跃商户补进 kj_shop_record(历史脚本也要完整商户清单) try: n = get_shop_list(log, sql_pool) log.info(f"商户发现完成,去重商户 {n} 个") except Exception as e: log.error(f"get_shop_list error: {e}") # 2) 商品全量:遍历所有商户,深翻已售列表全部页(incremental=False,不早停) try: shop_rows = sql_pool.select_all("SELECT shop_id, shop_name FROM kj_shop_record WHERE is_deleted = 0") log.info(f"待全量采集商户 {len(shop_rows)} 个") for shop_id, shop_name in shop_rows: try: cnt = get_sold_list(log, shop_id, shop_name, sql_pool, incremental=False) log.info(f"商户 {shop_id} {shop_name} 全量商品采集完成,写入 {cnt} 个") except Exception as e: log.error(f"get_sold_list error(商户 {shop_id}): {e}") except Exception as e: log.error(f"iterate_shop_list error: {e}") # 3) 玩家全量:遍历尚未成功采集玩家的商品(player_state != 1) try: prod_rows = sql_pool.select_all("SELECT pid FROM kj_product_record WHERE player_state != 1") pids = [row[0] for row in prod_rows] if prod_rows else [] log.info(f"待采集玩家的商品 {len(pids)} 个") for pid in pids: try: # 先置 1 表示开始采集(对齐 zc 断点标记) sql_pool.update_one("UPDATE kj_product_record SET player_state = 1 WHERE pid = %s", (pid,)) has_data = get_player_list(log, pid, sql_pool) if not has_data: # 无玩家(如拼团未完成)置 2,下次仍会重试 sql_pool.update_one("UPDATE kj_product_record SET player_state = 2 WHERE pid = %s", (pid,)) except Exception as pid_error: log.error(f"商品 {pid} 玩家采集失败: {pid_error}") try: sql_pool.update_one("UPDATE kj_product_record SET player_state = 3 WHERE pid = %s", (pid,)) except Exception as update_error: log.error(f"更新商品 {pid} 状态失败: {update_error}") except Exception as e: log.error(f"iterate_player_list error: {e}") # 4) 视频补采:拼团已完成但 video_url 仍空的商品,重取回放地址 try: n = refill_videos(log, sql_pool) log.info(f"视频补采完成,本轮 {n} 条") except Exception as e: log.error(f"refill_videos error: {e}") except Exception as e: log.error(f"{sys._getframe().f_code.co_name} error: {e}") finally: log.info(f"卡集历史全量采集 {sys._getframe().f_code.co_name} 运行结束") if __name__ == "__main__": history_main(logger)