PHP调用YouTube API OAuth2超1小时运行的令牌续期方案
核心问题根因
你当前用的response_type=token是OAuth2隐式授权模式,天生适配不了后台长时跑的批处理任务:
- 隐式流只会返回1小时有效期的
access_token,不会返回可自动续期的refresh_token,令牌一过期必须人工重新跳转授权,没法自动续 - 不带令牌调用走公开接口配额,单IP默认上限就是拉2万条左右;带令牌走授权用户配额虽然上限更高,但令牌过期就直接断,你现在拉8000条就失败,本质是令牌有效期内的请求额度打满+令牌到期双重限制,没有自动续期机制根本跑不完4万条。
改造步骤
1. 替换授权模式,首次授权后拿到长期有效的refresh_token
把你现在的隐式流授权改成授权码模式,加上离线权限申请,第一次人工授权完成后,就能拿到长期有效的refresh_token,后续季度跑任务不用再人工介入授权。
修改授权地址拼接代码:
// 注意补全https前缀,response_type改成code,加access_type=offline和prompt=consent参数,参数值记得urlencode $iurl = "https://accounts.google.com/o/oauth2/auth?client_id=" . $client_id . "&response_type=code&redirect_uri=" . urlencode($redirect_uri) . "&scope=" . urlencode($scopes) . "&access_type=offline&prompt=consent";
踩坑提醒:第一次跑这个授权跳转时,必须在弹出的授权页确认勾选YouTube数据读取权限,授权完成后Google会跳回你配置的回调地址,URL里会带
code参数,拿这个code就能换令牌。返回结果里会同时有1小时有效期的access_token,和长期有效的refresh_token——只要你不主动撤销授权,这个refresh_token一直能用,把它存在本地配置文件/数据库里就行,整个流程只需要第一次人工操作这一次。
回调接口里用code换令牌的参考代码:
// 回调地址接收到URL里的code参数后执行 $code = $_GET['code']; $tokenApi = "https://oauth2.googleapis.com/token"; $postData = [ 'code' => $code, 'client_id' => $client_id, 'client_secret' => $client_secret, // 从Google API控制台拿客户端密钥,隐式流用不上这个,现在必须配 'redirect_uri' => $redirect_uri, 'grant_type' => 'authorization_code' ]; $ch = curl_init($tokenApi); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData)); $tokenRes = json_decode(curl_exec($ch), true); curl_close($ch); // 把返回的refresh_token持久化存储,比如写到配置文件里,后续任务直接读取 $savedRefreshToken = $tokenRes['refresh_token']; // 同时把首次拿到的access_token和过期时间存到本地缓存 file_put_contents('./token_cache.json', json_encode([ 'access_token' => $tokenRes['access_token'], 'expire_at' => time() + $tokenRes['expires_in'] ]));
2. 实现access_token自动续期逻辑,无需手动切换
在拉取视频的主逻辑入口,加一个统一获取有效令牌的方法,每次发请求前先检查本地缓存的access_token是不是快过期,过期就用存好的refresh_token自动换新,全程无人工干预:
function getValidAccessToken($clientId, $clientSecret, $savedRefreshToken) { // 读本地缓存的令牌信息 $cachePath = './token_cache.json'; $tokenCache = file_exists($cachePath) ? json_decode(file_get_contents($cachePath), true) : []; // 如果缓存的令牌还有5分钟以上有效期,直接返回使用,减少不必要的换令牌请求 if (!empty($tokenCache['expire_at']) && $tokenCache['expire_at'] > time() + 300) { return $tokenCache['access_token']; } // 令牌过期,用refresh_token申请新的access_token $tokenApi = "https://oauth2.googleapis.com/token"; $postData = [ 'refresh_token' => $savedRefreshToken, 'client_id' => $clientId, 'client_secret' => $clientSecret, 'grant_type' => 'refresh_token' ]; $ch = curl_init($tokenApi); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData)); $newToken = json_decode(curl_exec($ch), true); curl_close($ch); // 新令牌写回缓存 file_put_contents($cachePath, json_encode([ 'access_token' => $newToken['access_token'], 'expire_at' => time() + $newToken['expires_in'] ])); return $newToken['access_token']; }
3. 拉取逻辑加容错,避免异常中断
分页拉取视频数据时,每次请求都调用上面的方法拿最新的有效令牌,同时加异常重试逻辑:
- 单页拉取条数设为API支持的最大值50条,减少总请求数,省配额
- 遇到401/403令牌失效错误时,强制清空本地令牌缓存,重新拉取新令牌后重试当前请求
- 遇到429配额超限错误时,暂停2-3秒再重试,不要高频硬打接口
- 已经拉取过的视频ID存本地,后续季度跑任务可以做增量拉取,不用每次都从头拉4万条,进一步省配额省时间
其他注意点
- 第一次申请refresh_token必须加
prompt=consent参数,不然Google不会返回refresh_token,只返回短期access_token,这点是高频踩坑点 client_secret和refresh_token都要存在服务器本地,不要对外泄露,否则其他人拿到可以越权操作你授权的账号- 提前在Google Cloud控制台给对应项目开启YouTube Data API v3权限,否则接口会报无权限错误
- YouTube Data API v3单授权用户默认每日配额是10000单位,拉取单条视频元数据仅消耗1单位,拉4万条完全在配额范围内,只要令牌自动续期正常,不会出现拉到一半断的问题
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

