百度统计 API 接入血泪录:14 次鉴权失败之后
官网上线第二天,我需要一个自动日报——每天早晚各拉一次 PV、UV、跳出率,异常自动告警。手动导出 PDF 不是办法。
百度统计有开放 API。我想着,注册个应用,拿个 token,调个接口,半小时搞定。
三天之后,我对着终端里第十四次返回的报错,终于找到了那组正确的参数。
这篇文章记录整个过程。不是教程——教程应该告诉你正确的做法。这是踩坑录,告诉你哪些路走不通,以及为什么。
起点:一张 PDF 和一组凭证
官网上线的第一天,我在百度统计后台看到这样的数据:
| 日期 | PV | UV | IP | 跳出率 |
|---|---|---|---|---|
| 5/15 | 1 | 1 | 1 | 0% |
| 5/16 | 60 | 14 | 17 | 66.67% |
数据是好的,但只能手动看。我需要 API。
手里有的凭证:百度商业开发者中心签发的 JWT(HS384 签名,aud: "百度统计"),以及一个应用 AppKey f4909ca6e7。
第一回合:标准 REST API(失败 ×3)
直觉告诉我,百度统计开放 API 用 OAuth 2.0。把 JWT 当 access_token 往标准端点扔:
GET https://openapi.baidu.com/rest/2.0/tongji/report/getSiteList
?access_token=<JWT>
→ error_code: 110
→ "Access token invalid or no longer valid"
换到 api.baidu.com、aip.baidubce.com,同样 110。连 Authorization: Bearer header 都试了,返回 "API not supported by authorization"。
这不是鉴权方式不对——是这个 token 根本不走标准 OAuth 通路。
第二回合:JSON-RPC(失败 ×4)
查文档发现,百度商业开发者中心的应用走的是 JSON-RPC 格式:
POST https://api.baidu.com/json/tongji/v1/ReportService/getSiteList
{
"header": {
"username": "82454026",
"token": "<JWT>",
"account_type": 1
},
"body": {}
}
→ code: 84261
→ "The token is invalid in encrypt"
有进展——不再是 110,API 认出这个字段了,只是加密格式不对。我试了 accessToken、auth_token、apiToken 各种字段名,只有 token 字段被识别。但内容永远不对。
又试了 AppKey 代替 JWT:
"token": "f4909ca6e7"
→ code: 8001
→ "Server internal error, please try again later"
鉴权通过了!但所有后端服务——Tongji、SEM AccountService——统一返回 8001 系统错误。不是账号问题,是整个服务集群拒绝了这个 token 的请求。
我还换了 account_type 的六个值(1/2/3/5/9/11),全部 8001。显然不是参数问题,是权限层面的阻断——这个 AppKey 注册的是移动统计(Schema: mtj),没有网站统计 API 的调用权限。
第三回合:OAuth 换发(失败 ×2)
也许需要走 OAuth 换发流程,把 AppKey + Secret Key 换成真正的 access_token:
POST https://openapi.baidu.com/oauth/2.0/token
grant_type=client_credentials
&client_id=13bd5049a676d10737959932102ec557
&client_secret=f4909ca6e7
→ "unknown client id"
appId 不是 OAuth client_id。尝试 grant_type=jwt-bearer、refresh_token ——全部不支持。
百度商业开发者中心的应用体系,和百度开放平台的 OAuth 体系,是两套完全不同的东西。
第四回合:直接账号鉴权(失败 ×3)
回到 JSON-RPC,把百度账号密码当 accessToken 传:
"userName": "xierongji",
"accessToken": "wuji.198919"
→ code: 89406
→ "The access token you provided is invalidate"
用中文名 "谢荣极"、"谢秉极",UID "82454026",手机号——全部 81020("username information is incorrect")。这个 userName 字段只认百度账号登录名,也就是 xierongji。
但即使登录名对了,accessToken 传明文密码也不行。百度商业 API 的 accessToken 字段要的不是密码,是一个加密后的凭证。
破局:把 JWT 放进 accessToken
回到第一性原理。我手里有三个东西:
- JWT:商业开发者中心签发的,
aud明确写着 "百度统计" - AppKey:应用标识,公开可查
- 百度账号:登录名 xierongji
JWT 在 token 字段被拒(加密不匹配),但 JWT 本身就是一个标准的鉴权凭证。为什么不放进 accessToken?
POST https://api.baidu.com/json/tongji/v1/ReportService/getSiteList
{
"header": {
"userName": "xierongji",
"accessToken": "<JWT>",
"account_type": 1
},
"body": {}
}
返回的不是错误,是 code: 91021601——"账户受限,暂不支持该操作"。这不一样了。鉴权通过了,只是数据获取权限还没开。
我用 getSiteList 先试——这个接口不需要数据权限:
→ status: 0 (success)
{
"list": [{
"domain": "jingxuanyoupin.icu",
"site_id": 23188112,
"status": 0
}]
}
通了。而且发现了一个关键事实:
站点 ID 是 23188112,不是 JWT 里的 uid 82454026(那是用户 ID)。
用正确的 site_id 调 getData:
POST /json/tongji/v1/ReportService/getData
body: {
"site_id": "23188112",
"start_date": "20260516",
"end_date": "20260517",
"metrics": "pv_count,visitor_count,ip_count,bounce_ratio,avg_visit_time",
"method": "overview/getTimeTrendRpt"
}
→ status: 0 (success)
data: [
[60, 14, 17, 66.67, 624], // 5/16
["--", "--", "--", "--", "--"] // 5/17(当日,未出数据)
]
和 PDF 报告完全一致。
最终方案全景
折腾三天的最终结论,正确的鉴权组合只有这一种:
| 参数 | 值 | 踩坑 |
|---|---|---|
| API 格式 | JSON-RPC (POST) | 不是 REST |
| 端点 | /json/tongji/v1/ReportService/getData | 不是 rest/2.0 也不是短信服务 |
| userName | xierongji(百度账号登录名) | 不是中文名、UID、手机号 |
| accessToken | 商业开发者中心 JWT | 不是 token 字段、不是 password、不是 AppKey |
| site_id | 23188112 | 不是 82454026(那是 UID) |
| account_type | 1 | 全部试过了,就是 1 |
完整的错误码演变路径:
110 → 标准 REST:access_token 无效
84261 → JSON-RPC token 字段:JWT 加密不匹配
8001 → JSON-RPC token 字段:AppKey 权限不足(系统错误)
81020 → userName 字段:值不是账号登录名
89406 → accessToken 字段:明文密码不是有效 token
91021 → 鉴权通过,site_id 错误导致无权限
0 → ✅ 通了
四个完全不同的鉴权体系在同一个域名下并行存在——这就是百度商业 API 的"特色"。没有哪份文档会告诉你 "userName 要传登录名、accessToken 要传 JWT" 这个组合。你得自己试。
落地:自动日报
调通之后的事就简单了。写了一个 Node.js 脚本,两个 Cron 定时触发:
- 09:00 早间版:拉昨日数据,简洁汇报
- 22:00 晚间版:拉当日数据,自动对比前日,异常(PV 变动 >50%、跳出率 >80%、平均时长 <60s)自动标记
JWT 存在 600 权限的文件里,.gitignore 豁免,脚本运行时读取。不依赖任何第三方 SDK,纯 Node.js 原生 HTTPS 模块,不到 60 行代码。
如果重来一次
我会做三件事:
- 先用 getSiteList 验证鉴权。这个接口不需要数据权限,用来测试 userName + accessToken 组合最合适。
- 不要假设 UID 就是 site_id。用 API 返回的真实 site_id,而不是 JWT 里的 uid。
- 认准 JSON-RPC 格式。百度商业开发者中心的应用不走 REST,不走 OAuth,只走
api.baidu.com/json/这条 JSON-RPC 通道。
至于百度为什么要在同一个平台塞进四种互不兼容的鉴权方式——这是一个比 API 鉴权更难回答的问题。
标签:百度统计 · API 踩坑 · JSON-RPC · 自动化运维 · 鉴权调试
📱 关注公众号「星尘和光文化传媒」
每周推送 AI 实战技术文章、全栈开发案例和自动化管线拆解。
不写广告,只写踩坑录。