feat(meituan-etl): 离线库扩到全国 359 城(多城并发抓取入库) (#57)
智能推荐 / 销量最高两 tab 的离线库此前只有北京(ETL 写死 cityId),改为遍历 美团官方城市字典 359 个地级市全量抓取。实测一个地级市 cityId 已覆盖其下辖县级市 (徐州→邳州/新沂/睢宁等),按地级市抓即可,无需区县层级。 - 城市字典:tools/gen_meituan_cities.py 从美团 Excel 生成随仓库 JSON (app/integrations/data/meituan_cities.json,359 城),app/integrations/cities.py 读取; - ETL:city_id 参数化 + 城市级并发(默认 12)+ worker 启动错峰削平瞬时峰值 + 主线程逐城串行入库(Session 不跨线程); - 配速实测:单城全量 ~110s/~2300 条;15 并发抓完一轮 ~50-60min,402 占 3% 退避全消化; 每 3h 一轮全量(--interval 10800),窗口充裕; - prune 双护栏:本轮 0 入库 或 失败城占比 >5% 时跳过,防上游故障/大面积限流误删全表; - 仅写入侧;读取侧(rec/top-sales 按城过滤)待后续(依赖用户定位→cityId 映射,字典无经纬度)。 --------- Co-authored-by: chenshuobo <1119780489@qq.com> Reviewed-on: #57 Co-authored-by: chenshuobo <chenshuobo@wonderable.ai> Co-committed-by: chenshuobo <chenshuobo@wonderable.ai>
This commit was merged in pull request #57.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""从美团「城市字典」Excel 生成随仓库的 JSON(app/integrations/data/meituan_cities.json)。
|
||||
|
||||
本地一次性工具:美团每年更新城市字典时,把新 Excel 放进来重跑即可。
|
||||
Excel 不入库、不进仓库(二进制、需 openpyxl);生成的 JSON 随仓库提交,部署到服务器后
|
||||
ETL(scripts/pull_meituan_coupons.py)直接读它遍历全部城市。
|
||||
|
||||
字典口径:359 个地级市,经实测一个地级市 cityId 已覆盖其下辖县级市(如徐州→邳州/新沂),
|
||||
故无需区县层级。
|
||||
|
||||
用法:
|
||||
python tools/gen_meituan_cities.py ["城市字典xxx.xlsx" 路径]
|
||||
(不传则用默认 e:\\codes\\城市字典2025 (1).xlsx)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import openpyxl
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.dirname(HERE)
|
||||
DEFAULT_EXCEL = r"e:\codes\城市字典2025 (1).xlsx"
|
||||
OUT = os.path.join(ROOT, "app", "integrations", "data", "meituan_cities.json")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
excel = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_EXCEL
|
||||
wb = openpyxl.load_workbook(excel, data_only=True)
|
||||
ws = wb.worksheets[0]
|
||||
rows = list(ws.iter_rows(values_only=True))
|
||||
header = rows[0]
|
||||
|
||||
cities = []
|
||||
seen = set()
|
||||
for r in rows[1:]:
|
||||
cid = str(r[0]).strip() if r[0] else ""
|
||||
name = str(r[1]).strip() if len(r) > 1 and r[1] else ""
|
||||
prov = str(r[2]).strip() if len(r) > 2 and r[2] else ""
|
||||
if not cid or cid in seen:
|
||||
continue
|
||||
seen.add(cid)
|
||||
cities.append({"city_id": cid, "name": name, "province": prov})
|
||||
|
||||
os.makedirs(os.path.dirname(OUT), exist_ok=True)
|
||||
with open(OUT, "w", encoding="utf-8") as f:
|
||||
json.dump(cities, f, ensure_ascii=False, indent=1)
|
||||
|
||||
print(f"表头: {header}")
|
||||
print(f"写出 {len(cities)} 城 → {OUT}")
|
||||
print("样例:", cities[:3])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user