kj_history_spider.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. # -*- coding: utf-8 -*-
  2. # Author : Charley
  3. # Python : 3.12.10
  4. # Date : 2026/07/02
  5. """卡集历史全量采集脚本(一次性)。
  6. 与 kj_daily_spider(每天增量)的区别:本脚本对每个商户的已售列表「全量深翻」所有页
  7. (调 get_sold_list(incremental=False),不走 start_at 停止线早停),把历史商品 / 玩家 / 视频
  8. 一次性灌满。跑一次即止,不带 schedule。
  9. 公共函数(请求 / 签名 / 解析 / 翻页 / 补采)全部从 kj_daily_spider 复用,避免重复维护。
  10. 运行:python kj_history_spider.py
  11. """
  12. import sys
  13. from loguru import logger
  14. from mysql_pool import MySQLConnectionPool
  15. from kj_daily_spider import (
  16. get_shop_list,
  17. get_sold_list,
  18. get_player_list,
  19. refill_videos,
  20. )
  21. def history_main(log):
  22. """历史全量采集主函数:商户发现 → 全量深翻商品 → 全量采玩家 → 补视频。
  23. Args:
  24. log: 日志对象。
  25. Raises:
  26. RuntimeError: 数据库连接池异常时抛出。
  27. """
  28. log.info(f"开始运行 {sys._getframe().f_code.co_name} 卡集历史全量采集" + "." * 40)
  29. sql_pool = MySQLConnectionPool(log=log)
  30. if not sql_pool.check_pool_health():
  31. log.error("数据库连接池异常")
  32. raise RuntimeError("数据库连接池异常")
  33. try:
  34. # 1) 商户发现:先用在售主列表把当前活跃商户补进 kj_shop_record(历史脚本也要完整商户清单)
  35. try:
  36. n = get_shop_list(log, sql_pool)
  37. log.info(f"商户发现完成,去重商户 {n} 个")
  38. except Exception as e:
  39. log.error(f"get_shop_list error: {e}")
  40. # 2) 商品全量:遍历所有商户,深翻已售列表全部页(incremental=False,不早停)
  41. try:
  42. shop_rows = sql_pool.select_all("SELECT shop_id, shop_name FROM kj_shop_record WHERE is_deleted = 0")
  43. log.info(f"待全量采集商户 {len(shop_rows)} 个")
  44. for shop_id, shop_name in shop_rows:
  45. try:
  46. cnt = get_sold_list(log, shop_id, shop_name, sql_pool, incremental=False)
  47. log.info(f"商户 {shop_id} {shop_name} 全量商品采集完成,写入 {cnt} 个")
  48. except Exception as e:
  49. log.error(f"get_sold_list error(商户 {shop_id}): {e}")
  50. except Exception as e:
  51. log.error(f"iterate_shop_list error: {e}")
  52. # 3) 玩家全量:遍历尚未成功采集玩家的商品(player_state != 1)
  53. try:
  54. prod_rows = sql_pool.select_all("SELECT pid FROM kj_product_record WHERE player_state != 1")
  55. pids = [row[0] for row in prod_rows] if prod_rows else []
  56. log.info(f"待采集玩家的商品 {len(pids)} 个")
  57. for pid in pids:
  58. try:
  59. # 先置 1 表示开始采集(对齐 zc 断点标记)
  60. sql_pool.update_one("UPDATE kj_product_record SET player_state = 1 WHERE pid = %s", (pid,))
  61. has_data = get_player_list(log, pid, sql_pool)
  62. if not has_data:
  63. # 无玩家(如拼团未完成)置 2,下次仍会重试
  64. sql_pool.update_one("UPDATE kj_product_record SET player_state = 2 WHERE pid = %s", (pid,))
  65. except Exception as pid_error:
  66. log.error(f"商品 {pid} 玩家采集失败: {pid_error}")
  67. try:
  68. sql_pool.update_one("UPDATE kj_product_record SET player_state = 3 WHERE pid = %s", (pid,))
  69. except Exception as update_error:
  70. log.error(f"更新商品 {pid} 状态失败: {update_error}")
  71. except Exception as e:
  72. log.error(f"iterate_player_list error: {e}")
  73. # 4) 视频补采:拼团已完成但 video_url 仍空的商品,重取回放地址
  74. try:
  75. n = refill_videos(log, sql_pool)
  76. log.info(f"视频补采完成,本轮 {n} 条")
  77. except Exception as e:
  78. log.error(f"refill_videos error: {e}")
  79. except Exception as e:
  80. log.error(f"{sys._getframe().f_code.co_name} error: {e}")
  81. finally:
  82. log.info(f"卡集历史全量采集 {sys._getframe().f_code.co_name} 运行结束")
  83. if __name__ == "__main__":
  84. history_main(logger)