数据版本 v4 · 最后更新 2026-07-01 · 覆盖城市 12 个
| 数据源 | 角色 | 获取方式 | 更新频率 | 覆盖 |
|---|---|---|---|---|
| 高德地图 | 主表骨架 | 高德 POI 2.0 API | 每日 3:00 | 12 城市全量 |
| 百度地图 | 补充字段 + 独有 POI | 百度 Place API(39 分类) | 每日 3:30(轮转,2 城/天) | 12 城市轮转 |
| Google Place | 评分增强 + 小众挖掘 | Google Place Details API | 每日 4:00 | 高德主表按名称查询 + 独立 POI 发现 |
is_places 和 source_category,在评分阶段享受差异化加分。
| 步骤 | 时间 | 脚本 | 说明 |
|---|---|---|---|
| 1 | 3:00 | daily_poi_cron.py | 高德拉取 12 城市全量 POI + 评分,输出 v2.json |
| 2 | 3:30 | baidu_rotator.py | 百度轮转拉取(2 城/天),写入 raw/baidu_{city}.json |
| 3 | 4:00 | google_ratings_cron.py | 按 v3 名称查询 Google 评分,写入 google_ratings.json |
| 4 | 4:30 | merge_enrich.py | 三源合并输出 v3.json,本文档即描述此步骤 |
| 5 | 手动 | places_merge.py | Places 景点 API 富化,输出 places_cache.json;详见下方流水线 |
Places 数据由独立脚本 places_merge.py 处理,与高德/百度主流水线分离:
| 文件 | 说明 |
|---|---|
/data/git/places/popular.json | 人工维护的热门景点列表 |
/data/git/places/unpopular.json | 人工维护的小众冷门景点 |
/data/git/places/value.json | 人工维护的口碑景点(最高优先级) |
三文件合并时优先级 value > unpopular > popular,同景点 tags 取并集,source_category 累积。
| 步骤 | 规则 |
|---|---|
| 本地匹配 | 优先使用高德 v3 文件、百度 raw 文件做名称匹配(不消耗 API 配额) |
| API 查询(本地未命中) | 依次查询 高德 Place API → 百度 Place API → Google Places API |
| 坐标兜底 | 所有 API 均未命中时,使用城市中心坐标作为兜底 |
| 参数 | 值 |
|---|---|
| TTL | 30 天(CACHE_TTL_DAYS=30) |
| 缓存键 | 城市;景点名称(如 深圳;大梅沙海滨公园) |
| 过期策略 | 加载时自动删除过期条目;被删条目在本次运行中走完整富化流程重新查询 API |
| 写回机制 | 仅未过期缓存复用 + 本次 API 富化结果写回,过期条目不保留 |
| 操作 | 说明 |
|---|---|
| 匹配已有 POI | 用 城市;名称 键匹配高德主表 POI,注入 desc / tags / highlights / places_type / source_category |
| 追加独有 POI | Places 中未匹配到高德 POI 的条目,若有坐标则追加,并使用 classify() 重新分类 |
Google 评分缓存为 名称 → rating 的键值对,与城市无关。每当出现新 POI 名称(高德新增或百度独有追加),该名称会在次日 4:00 自动添加到 Google 批量查询队列中,4:30 合并时即可命中评分。
| 条件 | 阈值 | 可信度 |
|---|---|---|
| 名称去括号后完全一致 | 距离 ≤ 1000m | 100% |
示例: "海上世界" ⇔ "海上世界" (括号不影响)
| 规则 | 说明 |
|---|---|
| 去括号 | 去掉 (…) 及(…) |
| 去符号 | 移除 · - — - 空格 |
| 数字归一化 | 一→1 二→2 … 九→9 零→0 |
| 异体字统一 | 覔→觅 峯→峰 羣→群 啓→启 衞→卫 裏→里 佈→布 |
| 距离约束 | ≤ 100m |
示例: "2号沙滩" ⇔ "二号沙滩" "溪涌度假村海滨浴场" ⇔ "溪涌度假村-海滨浴场" "深圳湾公园日出剧场" ⇔ "深圳湾公园-日出剧场" "覔书店" ⇔ "觅书店"
需同时满足以下全部条件:
正确匹配: "深圳书城(宝安城店)" ⇔ "深圳书城·宝安城" (词片"深圳书城""宝安城"重叠) "广东梧桐山国家森林公园" ⇔ "梧桐山" (包含关系) 过滤案例(L3 拒绝): "大梅沙湾游艇会" ⇔ "深圳海钓游乐园" (同类型 + 20m,但无词重叠)
| 高德一级分类 | 百度一级 tag |
|---|---|
| 风景名胜 | 旅游景点 |
| 公园广场 | 旅游景点 |
| 科教文化服务 | 文化场馆 |
| 体育休闲服务 | 休闲娱乐 |
| 购物服务 | 购物 |
| 餐饮服务 | 美食 |
| 住宿服务 | 酒店 |
| 字段 | 优先级 | 默认值 | 说明 |
|---|---|---|---|
rating |
Google > 高德 > 百度 | 3.0 | 先按 Google 缓存名称匹配注入 google_rating,评分计算时按优先级取值 |
price |
百度 > 高德 | 无默认值 | 百度有价则覆盖,否则保留高德原值。无为 null |
shop_hours |
百度 | — | 百度有则写入,否则保留高德原值 |
baidu_tag |
百度 | — | 匹配成功后写入,用于分类映射和噪声判断 |
source_count |
— | 1 | 每命中一个额外数据源 +1(max 3) |
source_count=1)type 和 typecode 留空满分 10 分,四维加权 + Places 背书 + 年龄适配(静态预计算):
| 维度 | 满分 | 规则 |
|---|---|---|
| 评分 (rating) | 3.0 | Google > 高德 > 百度 > 默认 3.0,min(rating, 5.0) / 5.0 × 3.0Places value 类型:直接设为满分 3.0 |
| 内容丰富度 (content) | 1.5 | 有营业时间 +0.4 / 有价格 +0.4 / 有电话 +0.35 / 有地址 +0.35 |
| 知名度 (popularity) | 1.60 |
百度热度:≥500→10, ≥200→7, ≥100→5, ≥50→3, ≥10→1,无→0 百度评论数:≥200→5, ≥50→3, >0→1,无→0 百度排行榜:命中 +3 Google 评分:有评分 +0.6 各子项相加后 / 10 × 1.6 Places popular 类型:直接设为满分 1.60 |
| 亲子适配 (family_fit) | 3.0 | 按分类加权(见下表)cat_weight / 3.0 × 3.0 |
| Places 背书 (places_bonus) | 3.0 | 仅 is_places POI 有效,按 source_category 差异化: popular 类型 → 0(已通过 popularity 满分体现) value 类型 → 1.5 unpopular 类型 → 3.0 详见 第 9 章 |
| 年龄适配 (age_adapt) | ±1.6 | 静态预计算写入 score_breakdown.age_adapt,在 server.py Phase 4 查表取用底层逻辑: AGE_BONUS[category] / 5.0 |
汇总公式:family_score = rating_score + content_score + pop_score + family_fit_score + places_bonus,最终 min(s, 10.0)
| 分类 | 权重 | 分类 | 权重 |
|---|---|---|---|
| 儿童乐园 | 3.0 | 运动场馆 | 1.8 |
| 主题乐园 | 2.8 | 展览馆 | 1.8 |
| 水上乐园 | 2.8 | 文化场馆 | 1.8 |
| 动物园 | 2.8 | 休闲娱乐 | 1.8 |
| 水族馆 | 2.5 | 美术馆 | 1.5 |
| 科技馆 | 2.5 | 图书馆 | 1.5 |
| 天文馆 | 2.5 | 其他 | 1.5 |
| 博物馆 | 2.5 | ||
| 户外公园 | 2.5 | ||
| 户外活动 | 2.2 | ||
| 风景名胜 | 2.0 | ||
| 植物园 | 2.0 |
命中以下任一一级 tag 的 POI 直接过滤:
购物 | 出入口 | 交通设施 | 房地产 | 政府机构 | 酒店 生活服务 | 美食 | 公司企业 | 医疗 | 道路 | 组织机构 | 金融 | 丽人
命中以下组合需同时满足子标签才过滤:
| 一级 tag | 子标签 |
|---|---|
| 教育培训 | 幼儿园 / 小学 / 培训机构 / 校内设施 / 职能机构 |
| 休闲娱乐 | 茶馆 / 电玩电竞 |
合并后每条 POI 按 name + type + baidu_tag + tag 文本关键词匹配,归属于以下 19 个分类之一:
儿童乐园 | 主题乐园 | 水上乐园 | 水族馆 | 动物园 | 植物园 | 科技馆 天文馆 | 博物馆 | 展览馆 | 美术馆 | 图书馆 | 运动场馆 | 户外公园 户外活动 | 风景名胜 | 文化场馆 | 休闲娱乐 | 其他
输出文件 /var/www/kids-trip/family_scored_v3.json
{
"scored": [
{
"name": "深圳世界之窗",
"city": "深圳",
"lat": 22.537,
"lng": 113.973,
"type": "风景名胜;国家级景点;世界之窗",
"typecode": "110200",
"rating": 4.6,
"price": "220",
"cost": null,
"address": "深圳市南山区深南大道9037号",
"tel": "0755-26608000",
"shop_hours": "09:00-22:30",
"poi_id": "B0FFGXXXXX",
"first_seen": "2025-01-01",
"fee": "",
"baidu_tag": "旅游景点;风景名胜;主题公园",
"d7_hot_value": 90,
"baidu_comment_num": 200,
"baidu_ranking": "全民爱去榜NO.3",
"google_rating": 4.5,
"source_count": 3,
"is_places": false,
"category": "主题乐园",
"family_score": 7.84,
"places_type": null,
"desc": "",
"tags": [],
"highlights": "",
"source_category": [],
"score_breakdown": {
"rating": 2.76,
"content": 1.5,
"popularity": 0.96,
"family_fit": 2.8,
"places_bonus": 0.0,
"age_adapt": {"under12": 1.6, "12+": 1.6}
}
}
]
}
| 字段 | 来源 | 说明 |
|---|---|---|
name | 高德 / 百度 | POI 名称 |
city | 高德 / 百度 | 所属城市 |
lat / lng | 高德 / 百度 | WGS84 坐标 |
type / typecode | 高德 | 高德多级分类,百度独有 POI 留空 |
rating | 高德 | 高德原始评分 |
price | 百度 > 高德 | 合并后的价格(数字字符串) |
cost | 高德 | 高德原始费用信息(保留但不使用) |
address / tel | 高德 / 百度 | 地址和电话 |
shop_hours | 高德 / 百度 | 营业时间(百度优先) |
poi_id | 高德 / 百度 | 唯一标识 |
baidu_tag | 百度 | 百度原始分类标签(分号分隔) |
google_rating | Google 评分(命中时存在) | |
source_count | 合并 | 命中数据源数量(1-3) |
is_places NEW | 是否来自 Google Places 查询 | |
source_category NEW | AI 分类(每日更新) | AI 从公网多源数据聚合分类,详见第 9 章 |
places_type NEW | Places 返回的类型标签(中文) | |
desc NEW | Places 描述文案 | |
tags NEW | Places 标签列表 | |
highlights NEW | Places 亮点文案 | |
category | 分类映射 | 统一亲子分类 |
family_score | 评分算法 | 家庭友好度评分(0-10) |
score_breakdown | 评分算法 | 六维评分明细(含 places_bonus 和 age_adapt) |
用户请求到达 server.py 后,依次执行四阶段流水线,对当前页 POI 逐条叠加实时修正。最终评分 adj_score = family_score + Σ修正项增量。
| 阶段 | 操作 | 说明 |
|---|---|---|
| Phase 1 | 关键词过滤 | 按 kw 匹配 name / address / category,不匹配的直接跳过 |
| Phase 2 | 静态排序 + 分页 | 按 (免费优先, -family_score) 排序,再按 offset/limit 分页 |
| Phase 3 | OSRM 驾车数据 | 调用 OSRM Table API 一次请求算起点到当前页所有 POI 的真实驾车耗时和距离,非全量 |
| Phase 4 | 逐 POI 动态修正 | 依次叠加年龄段、天气、距离、Google、交通等修正项 |
| 修正项 | 触发条件 | 计算逻辑 | 分值范围 |
|---|---|---|---|
| 年龄段适配 | 用户传入 age 参数 |
查 score_breakdown.age_adapt.under12 或 .12+ 直接取用(已由 merge_enrich 静态预计算),不再每次请求动态查表 |
±2.0 |
| 实时天气 | 用户传了经纬度 + 天气缓存命中 |
基于 precip_prob 分五档:
博物馆/科技馆:降雨 +10×ratio 儿童乐园/水族馆:+7×ratio 主题乐园/动物园/水上乐园:-5×ratio 海滩/公园/植物园/景区:-10×ratio 晴天额外:户外类 +5 AQI 修正:≥150∩户外 -8 / ≥100 -4 / 优 +5 |
-10 ~ +10 |
| 距离重算 | 用户传了经纬度 | OSRM 驾车公里数替代 Haversine 直线距离,按阶梯分档替换原 distance_score ≤3km→10 / ≤5km→8 / ≤10km→6 / ≤20km→4 / ≤30km→2 / ≤50km→0 / >50km→-5 |
替换原值 |
| Google 评分融合 | Google 缓存命中 |
quality = max(amap, google) / 5 × 32 trending 按 Google review 总数分档:≥5000→10 / ≥2000→8 / ≥500→6 / ≥200→4 / ≥50→2 / 0→0 |
quality: 0~32 trending: 0~10 |
| Google 实时拥挤 | popular_times 有 current_hour + avg_hourly |
ratio = now / avg。若 >1.5 则 crowd_penalty = -8无 popular_times 时按 review 数回退推算:>10000→-3 / >5000→-1 / ≥200→0 / ≥50→+2 / >0→+3 |
-8 ~ +3 |
| 交通综合 | 始终执行 |
max(公交分, 停车分 + 驾车分)公交分:按城市默认 transit 时间分档(≤15→7 / ≤30→5 / ≤45→3 / ≤60→1 / ≤90→-2 / >90→-5) 停车分:Google 停车场数据命中 → +3 有停车场 / -3 明确无 驾车分:按 OSRM 驾车分钟分档(同公交分档) |
-5 ~ 10 |
所有修正项叠加完成后,当前页 POI 按 (免费优先, -adj_score) 二次排序返回。免费 + 实时修正高分 POI 靠前。
| 机制 | 实现 |
|---|---|
| OSRM 懒加载 | 仅请求当前分页的 POI(最多 ~50 个),非全量 POI |
| 天气缓存 | 按 0.01° 网格粒度内存缓存,同一网格 POI 共享,随 weather_cache 文件 mtime 失效 |
| 逆地理缓存 | 按 0.01° 粒度高德逆地理编码缓存,10 分钟有效 |
| 城市公交默认值 | 深圳 45min / 广州 114min / 东莞 140min / 惠州 212min / 珠海 191min / 中山 211min / 香港 65min / 澳门 65min / 其他 78min |
source_category 三层标记和差异化打分,让小众高质量景点不被埋没。
source_category 由 AI 从公网数据抓取自动分类生成,每日更新。分类规则由多个数据源聚合确定(评分、评论数、热度趋势等),主要标记:
| 标记 | AI 分类依据(多源聚合) | 含义 |
|---|---|---|
popular | 多源口碑数据达到规模化阈值(如评论数充足、榜单命中) | 热门景点 |
unpopular | 公网数据稀疏但仍被收录 | 小众冷门 |
value | 多源评分聚合后达到高评价阈值 | 口碑优良 |
三个标记互不排斥,一个 POI 可同时携带多个标记(如 ["popular", "value"])。标记优先级:popular 和 value 分别覆盖对应维度,unpopular 仅在无 popular 且无 value 时触发 places_bonus。
| source_category | 数量 | 说明 |
|---|---|---|
["popular"] | 70 | 纯热门 |
["unpopular"] | 64 | 纯小众(无评分信号) |
["value"] | 40 | 纯口碑(高评分低人气) |
["popular", "value"] | 16 | 热门 + 口碑 |
["popular", "unpopular"] | 1 | 人气临界(review 数接近 50) |
在 score_poi() 和 build_breakdown() 中,根据 source_category 决定三个维度是否设为满分或额外加分:
| source_category | rating_score | pop_score | places_bonus | 典型 family_score |
|---|---|---|---|---|
| popular | 原始计算 (max 3.0) | 1.60(满分) | 0.0 | ~7.5 |
| value | 3.0(满分) | 原始计算 | 1.5 | ~8.0 |
| unpopular | 原始计算 | 原始计算 | 3.0 | ~8.7–9.2 |
| popular + value | 3.0 | 1.60 | 1.5 | ~9.3 |
| popular + unpopular | 原始计算 | 1.60 | 0.0 | ~7.5 |
核心逻辑(伪代码):
places_bonus = 0.0
if is_places:
if 'value' in source_category:
rating_score = 3.0 # 高评分 → 评分满分
places_bonus = 1.5 # 高评分 → 额外 1.5 分背书
if 'popular' in source_category:
pop_score = 1.60 # 有人气数据 → 知名度满分
if 'unpopular' in source_category
and 'value' not in source_category
and 'popular' not in source_category:
places_bonus = 3.0 # 纯小众 → 3.0 分探索背书
family_score = min(rating_score + content_score + pop_score
+ family_fit_score + places_bonus, 10.0)
每条 POI 的 score_breakdown 包含六维:
| 字段 | 类型 | 说明 |
|---|---|---|
rating | float | 评分维得分 (max 3.0) |
content | float | 内容丰富度得分 (max 1.5) |
popularity | float | 知名度得分 (max 1.60) |
family_fit | float | 亲子适配得分 (max 3.0) |
places_bonus | float | Places 背书加分 (0 / 1.5 / 3.0) |
age_adapt | object | 年龄适配值 {"under12": float, "12+": float} |
places_cache.json 中未匹配到高德主表 POI 的条目(即 Places 独有),经过滤后作为独立 POI 追加:
Places 数据在 merge_enrich.py 中重新走 classify() 分类(含 CATEGORY_MAP 关键词兜底),解决高德 typecode 映射错误(如渔村误分类为餐饮)。详见 第 2 章 Places 数据流水线。
| 条件 | 说明 |
|---|---|
| does_not_exist = True | 未在现有 POI 池中找到同名匹配 |
| Google type 不在非亲子黑名单 | 排除 lodging / clothing_store / electronics_store / hardware_store / home_goods_store / liquor_store / furniture_store / shoe_store / pharmacy / car_repair / gas_station / real_estate_agency 等 |
| 有 lat/lng | 坐标不为空 |
追加的 POI 继承 is_places=True、source_category、places_type、desc、tags、highlights 字段,参与后续评分。
最终输出按 -family_score 降序排列。places_bonus 生效后,纯 unpopular 景点(如塱头古村 family_score 9.16)的排序位置大幅提升,popular 热门景点(如大梅沙 7.48)因无 bonus 而相对偏低——差异反映了"大众热门 vs 隐藏宝藏"的排序理念。
kids-trip 三源 POI 合并规则文档 · v4 · 2026-07-01