Yelp 评论 API 返回空数组?排查筛选条件、分页与未知总数
| Gary Danko 的两份独立响应记录 | 实际评论条数 | count | 分页信息 | 应记录的状态 |
|---|---|---|---|---|
| 记录 A:返回评论正文 | 3 | 6149 | current_page=1,per_page=3 | 已取得一页,不是已取得全部 |
记录 B:reviews=[] | 0 | null | 第 1 页,total_pages=1,next=null | 当前响应为空,总数未知 |
同一商家两种响应:三条评论与空数组分别记录
这两份 Yelp 评论 API 响应指向同一商家,但不是可据此归因的严格对照实验。记录 A 使用 sort_by=newest、page=1、page_size=3;记录 B 返回空数组。不能仅凭表格断言是语言、关键词、权限或临时故障造成了空页,更不能把后者写成“这家店没有评论”。
排查时先留存请求参数、采集时间、HTTP 状态和脱敏响应。HTTP 200 后仍要核对平台的 code、message:业务失败应进入错误队列,不能伪装成空结果;确认业务成功并拆出评论载荷后,再检查 reviews。字段缺失或类型不符也不是合法的空数组。
count=6149 与 count=null 不能合并成零条评论
count=6149 是那次响应报告的计数,实际只拿到了三条,不能直接填写导出条数。count=null 则没有提供可用总数;数据库中保留空值,并另存“总数未知”,不要使用 count or 0。
把“本页条数”“去重后累计条数”“响应报告总数”分列。即使空页同时返回 total_pages=1,也只保存这次分页元数据,不据此宣布整家店零评论。门店详情里的 review_count 属于另一次查询,也不能替代当前评论请求的覆盖范围。
固定商家 ID,每次只调整一个筛选条件
若尚未确认分店,先用快速搜索按名称和地点找候选;页面安全示例是 Golden Boy Pizza、San Francisco, CA、limit=3。再用商家详情核对街道、门牌和城市,保存对应的 id、alias,不要取同名结果第一条。快速搜索不是全城商家穷举工具。
对已确认的门店,先保存不附加关键词、星级、原文语言及翻译要求的基线,再分别测试 query、rating、language。星级写法如 1,2;language 筛原文语言,translate_to 请求额外译文,不能互相替代。每轮只改一项,排序、门店和页大小保持一致;这些筛选与翻译能力有接口说明,但不代表每种组合都已验证有数据。
从控制台获取有权使用的本平台密钥,只在服务端替换 YOUR_API_KEY;不要填 Yelp 账号密码、会话或 Cookie。工单和日志须去掉密钥及完整含密钥 URL。下面手动执行才会请求已观察过的门店;打开文章、展开页面或复制代码不会调用数据接口,也不保证重跑仍返回三条。
curl --get 'https://api.opendata-api.com/api/project/yelp/business-reviews?key=YOUR_API_KEY' \
--data-urlencode 'business=gary-danko-san-francisco' \
--data-urlencode 'sort_by=newest' \
--data-urlencode 'page=1' \
--data-urlencode 'page_size=3'
如需类似应用的 API,可通过联系页面提出需求,也可了解私有源代码部署。
先核对 current_page 和实际条数,再决定是否翻页
评论接口的 page 默认 1,说明范围为 1–1000;page_size 默认 20,范围为 1–49。这里取 3 是诊断用小页,不是接口只能返回三条的证明,也不是调大就一定有更多结果的承诺。
每页核对请求页码与 current_page,保存 per_page、实际数组长度和评论 ID,再结合 next、total_pages 决定是否继续。页码不符、整页重复、导航矛盾或意外空页时暂停并留证;不能只凭数组为空宣布完成,也不能在总数未知时无限递增页码。为临时错误设置有限重试和退避,不自动反复请求未解释的空页。
分页采集保存检查点,不把部分结果标成全量
每页落盘后再推进进度。下面只读取本地已确认成功、已拆包的单页业务 JSON,不发送请求;页码是待核对的请求记录,不从响应反推。它保存的是诊断检查点,不是完整采集器。
import json
from datetime import datetime, timezone
from pathlib import Path
request = {
"business": "gary-danko-san-francisco",
"sort_by": "newest", "page": 1, "page_size": 3,
}
payload = json.loads(Path("review-page.json").read_text())
rows = payload["reviews"]
assert isinstance(rows, list)
assert payload["current_page"] == request["page"]
checkpoint = {
"request": request,
"fetched_at": datetime.now(timezone.utc).isoformat(),
"current_page": payload["current_page"],
"reported_count": payload.get("count"),
"count_state": "unknown" if payload.get("count") is None else "reported",
"returned_rows": len(rows),
"review_ids": [row["id"] for row in rows],
"coverage": "partial",
}
Path("checkpoint.json").write_text(
json.dumps(checkpoint, ensure_ascii=False, indent=2)
)
生产采集还须原子保存评论与检查点,记录全部筛选条件、已确认页和待核验页。按商家 ID 加评论 ID 去重,保留采集时间;正文变化可另存版本。重启后先复核最后一页,过滤条件改变则另开批次。newest 不是保证稳定的增量游标,采集中新增评论可能令后续页移动。交付口碑报告时标注“已采集若干条、覆盖尚未确认”,不要把重试耗尽或页数上限命名为全量完成。
明确边界:未知总数、缺失评论与官方接口差异
- 三条不等于全文或全部历史。 已观察到正文
text,但不能据此保证每条都是未截断全文;应检查具体返回内容。此第三方 Yelp 数据接口与官方接口是不同契约,不能搬用官方的分页、语言参数或“三条摘要”限制,也不能承诺跨页拿齐历史。 - 素材缺失不能靠评论补齐。 菜单接口曾返回分区、条目与价格,不只菜单链接;图片接口曾返回原图链接、分类、说明及上传者字段,但不保证每家店都有菜单或所有图片字段齐全。
- 分析入口不是同一种样本。 评论主题用于发现讨论维度;高频片段用于概览常见表述;主题下评论需使用该商家返回的主题再查看相关评论。不要将片段统计直接当作全店情感占比。
- 未推荐不等于虚假。 未推荐评论与常规评论分开处理;按 Yelp 的说明,这类评论不计入展示评分和评论数,不能仅凭推荐状态贴欺诈标签。
- 公开可见不代表可任意复用。 采集前确认实际合同下的数据访问、保存期限、分析、署名及再发布权限;作者信息和原图也需单独评估。成功返回不是许可,官方接口条款也不能自动扩大本接口的授权范围。未知总数、缺失评论和未验证的筛选组合,都应作为交付限制保留。