优化记录_deca采集翻页_20260911.md 6.9 KB

deca采集翻页 优化记录

1. 优化背景

排查 spiders/shop_test.py(临时测试:抓单店铺历史成交存 deca_product_record_copy1)时发现:某店铺历史成交只抓到 60 条就停,而 App 显示 1943 条。

实测确认根因是服务端接口硬限,不是采集逻辑漏采:

  • 已售接口 /api/v1/app/groupbuy/merchant/sold-listpageSize 锁死 20(传其他值报 code=10001 "PageSize不能小于或大于20"),且 page 翻页只开放前 3 页(60 条),page=4(offset≥60)返回 total=0、空 list。响应里的 total(如 1943)只是统计展示值,不代表可拉取条数。
  • 盲试 13 个常见游标参数名(lastCompletedAt/beforeCompletedAt/cursor/lastCode/lastId 等)全被服务端忽略;App 端手动下滑同样是 3 页到底——确认无隐藏翻页方式。
  • 在售接口 /api/v1/app/groupbuy/merchant/on-sale-list 同源、同样 pageSize≤20;实测库内在售最多的商家仅 29 个(远 <60),触不到上限。

结论:这类列表接口天花板就是最近 3 页,遂把在售/已售所有列表翻页上限统一收敛到 3 页,并清理因此失效的「连续无新页早停」逻辑。

2. 改动内容

统一目标:所有在售/已售列表翻页上限 = 3 页;去重一律靠唯一键 INSERT IGNORE / upsert,不依赖翻页早停。

文件 函数 改动
common/deca_sold_core.py get_sold_list MAX_SOLD_PAGES 500→3;删除 STOP_AFTER_DUPE_PAGES 常量与 _filter_new_codes 函数;去掉增量早停分支,统一翻 3 页 + INSERT IGNOREincremental 参数保留仅兼容 run_pipeline,不再影响翻页
spiders/buy_record_spider.py get_monitored_onsale ON_SALE_MAX_PAGES 20→3(去重 = save_products upsert,原有 total/末页早停保留)
reports/onsale_report/deca_on_sale_report.py full_onsale_sweep ONSALE_MAX_PAGES 30→3(去重 = save_products upsert,原有早停保留)
spiders/onsale_alert_spider.py fetch_onsale 死常量 MAX_PROD_PAGES 100→3,并用它替换硬编码 while page <= 20(去重 = product_code 字典去重 + INSERT IGNORE

run_pipelinesold_daily_spider.py / sold_history_spider.pyincremental= 调用未改(保持最小影响面)。

3. 改动前后对比

common/deca_sold_core.py get_sold_list 核心逻辑:

# 改动前:分全量/增量两条路径,增量靠 _filter_new_codes + STOP_AFTER_DUPE_PAGES 连续无新页早停
while page <= MAX_SOLD_PAGES:  # 500
    ...
    if incremental:
        new_codes = _filter_new_codes(pool, [...])   # 查库筛新 code
        rows = [r for r in rows if r["product_code"] in new_codes]
    pool.insert_many(table=T_PROD, data_list=rows, ignore=True)
    if incremental:
        if new_on_page == 0:
            dupe_pages += 1
            if dupe_pages >= STOP_AFTER_DUPE_PAGES: break   # 连续无新页早停

# 改动后:单一路径,翻满 3 页,去重全交给唯一键 INSERT IGNORE
while page <= MAX_SOLD_PAGES:  # 3
    ...
    if rows:
        pool.insert_many(table=T_PROD, data_list=rows, ignore=True)  # 靠唯一键 product_code 去重
    if len(items) < PAGE_SIZE: break   # 不足一页=末页

4. 注意事项

  • 接口天花板不可逾越sold_history/sold_daily 对每个店铺同样只能采到最近 60 条历史成交,这是接口上限不是漏采。要积累更完整历史,只能靠每日增量长期跑(每天把新产生的成交追进库),不能指望一次性深翻。
  • 在售的 3 页是纯保护值:在售商家在售量普遍远 <60(实测 top 29 个),正常在第 1~2 页就靠 total/末页早停停下,3 页上限不会触发也不会漏采。
  • incremental 参数已成兼容摆设get_sold_list 保留它只为不改 run_pipeline 调用链;全量/增量翻页行为现已完全一致。若后续要彻底清理,需连带改 run_pipeline 与两个 spider 的调用。
  • 校验:4 个文件均通过 py_compileSTOP_AFTER_DUPE_PAGES / _filter_new_codes 全库无残留引用。
  • MAX_CARD_PAGES(卡密清单)、MAX_SHOP_PAGES(商家列表)为不同接口,本次未涉及,保持原值。

5. 后续增强:密集时段每小时占坑补采(同日)

5.1 背景

60 条上限本质是completedAt 倒序的滑动窗口:接口只吐最近 60 笔成交。若某商家在相邻两次采集间隔内成交 >60 笔,最早那批会被挤出前 3 页,永久漏采。而该店铺 13:00~06:00(实测真正密集在 22:00~04:00)售卖集中,每天一次 08:00 采集无法覆盖这种密集段。

5.2 方案

spiders/sold_daily_spider.py 内新增高频轻量任务(不新建脚本):

  • hourly_task(log):13:00~06:00 每整点跑一次,只对 HOURLY_MERCHANT_IDS 里的目标商家调 core.get_sold_list(翻 3 页 INSERT IGNORE 占坑入库),不跑详情/随机团/卡密/报告等精加工。
  • 每天 08:00 main_task(完整 run_pipeline)不变:负责详情补抓 / 随机团回补 / 拆卡报告等精加工。

解耦是方案成立的关键run_pipeline 里"步骤 2 占坑入库"与"步骤 3~5 精加工"完全解耦——精加工均由扫库存量驱动(fill_details 扫未补详情的行、backfill_team_amount 扫 team 为 NULL 的行、fill_reports 扫无报告的行)。所以高频只需保证 product_code 尽早进库不丢,08:00 完整流程会自动扫到新行补齐;INSERT IGNORE 幂等,高频重复抓不会重复入库。

5.3 数据依据(频率是否够)

统计库内 881226408 的成交(1912 条,跨 8/3~9/11)按「日期+小时」分桶:

  • 单小时成交峰值 = 41 笔(9/9 凌晨 3 点),top15 全在 20~41 之间。
  • 每小时采一次可覆盖最近 60 条,留 60−41=19 条余量 → 每小时够,不漏
  • (库数据本身受"每天只采 60 条"限制,极端爆量日可能被低估。)

5.4 改动点

文件 改动
spiders/sold_daily_spider.py 新增常量 HOURLY_MERCHANT_IDSHOURLY_HOURS(13~23、0~6);新增 hourly_taskschedule_task 注册 18 个整点跑 hourly_task;修正模块/main_task 已失准的"连续无新页早停"注释

5.5 注意事项

  • 爆量风险:若某商家新品首发单小时冲破 60,每小时仍会漏最早一段——届时对该商家提频到半小时/15 分钟(或按商家分级调度)。
  • 既有隐患(本次未改)main_task@retry(wait_fixed(3600))同步阻塞——若 08:00 完整流程失败进入重试,会 sleep 一小时并阻塞整个 schedule 单线程,期间 hourly_task 也无法触发。要让高频不受完整流程故障拖累,需将 main_task 失败重试改为非阻塞(独立线程/进程)。
  • 扩商家:往 HOURLY_MERCHANT_IDS 追加 ID 即可。