
本文详解如何使用 PHP 自动遍历含非递增、随机 Base64 分页键(如 key=xxx)的分页 JSON API,通过循环请求 + 条件终止机制,一次性拉取并插入全部数据(支持数万页),避免手动翻页或超时中断。
本文详解如何使用 php 自动遍历含非递增、随机 base64 分页键(如 `key=xxx`)的分页 json api,通过循环请求 + 条件终止机制,一次性拉取并插入全部数据(支持数万页),避免手动翻页或超时中断。
在处理某些第三方 API(如订单、日志或用户列表接口)时,分页并非采用传统的 page=1/2/3 或 offset/limit 模式,而是返回一个不可预测的 Base64 编码字符串作为下一页标识(例如 "key": "aGVsbG86d29ybGQ="),且该键仅存在于当前响应体末尾。此时无法预知总页数,也无法通过数学计算跳转——唯一可靠的方式是“以数据驱动翻页”:每次请求后解析响应,提取新 key,再发起下一次请求,直到无新数据为止。
以下是一个健壮、可落地的 PHP 实现方案,兼顾稳定性、错误处理与性能控制:
✅ 核心逻辑流程
- 延长脚本执行时限:因可能需处理数万次 HTTP 请求,务必调用 set_time_limit(0)(不限时)或设为足够大值(如 3600 秒);
- 初始化首次请求 URL:不带 key 参数(即第一页);
-
循环请求 → 解析 → 入库 → 判停:
- 使用 cURL 发起 GET 请求(推荐启用 CURLOPT_RETURNTRANSFER、CURLOPT_FOLLOWLOCATION 及超时控制);
- json_decode($response, true) 解析响应,确保检查 json_last_error();
- 提取业务数据数组(如 'items' 或根级数组)及分页键(如 'key' 字段);
- 关键判停条件:若数据数组为空 [],或 key 不存在/为空字符串,则终止循环;
- 批量插入数据库(强烈建议使用事务 + INSERT INTO ... VALUES (),(),... 减少 I/O);
- 更新 URL:$url = "https://example.com/json?order=asc&key=" . urlencode($next_key);;
? 示例代码(精简可运行版)
<?php
set_time_limit(0); // 禁用超时限制
$base_url = 'https://example.com/json?order=asc';
$db = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
function fetchJson($url) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
CURLOPT_USERAGENT => 'PHP-DataSync/1.0'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || !$response) {
throw new Exception("HTTP {$httpCode} fetching {$url}");
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("Invalid JSON from {$url}");
}
return $data;
}
try {
$url = $base_url;
$totalInserted = 0;
do {
echo "Fetching: {$url}\n";
$data = fetchJson($url);
// 假设业务数据在 'records' 键下,分页键为 'key'
$records = $data['records'] ?? [];
$nextKey = $data['key'] ?? '';
if (empty($records)) {
echo "No more data. Stopping.\n";
break;
}
// 批量插入(示例:假设表结构为 id, name, created_at)
$stmt = $db->prepare("INSERT INTO items (id, name, created_at) VALUES (?, ?, ?)");
foreach ($records as $item) {
$stmt->execute([$item['id'], $item['name'], $item['created_at']]);
}
$totalInserted += count($records);
echo "Inserted " . count($records) . " records. Total: {$totalInserted}\n";
// 更新 URL 为下一页(仅当存在有效 key 时继续)
if (!empty($nextKey)) {
$url = $base_url . '&key=' . urlencode($nextKey);
} else {
echo "No 'key' found. Ending pagination.\n";
break;
}
// 可选:防爬间隔(如每请求后 sleep(0.1))
usleep(100000); // 100ms
} while (true);
echo "✅ Done. Total inserted: {$totalInserted}\n";
} catch (Exception $e) {
error_log("Fatal error: " . $e->getMessage());
echo "❌ Failed: " . $e->getMessage();
}⚠️ 关键注意事项
- 错误重试机制:生产环境应增加 curl 失败重试(如 3 次指数退避),避免单点网络抖动导致中断;
- 内存管理:若单页数据极大(如 >10MB),避免将全部历史数据保留在内存中,应边解析边插入;
- 数据库事务粒度:对超大分页,建议按每 100~1000 条记录提交一次事务,而非全量包裹在一个事务中(防锁表/内存溢出);
- 日志与断点续传:记录最后成功 key 到文件或 DB,崩溃后可从中断处恢复,而非从头开始;
- User-Agent 与限流:遵守目标 API 的 robots.txt 和速率限制,必要时添加合理 User-Agent 并控制请求频率;
- SSL 验证:线上环境务必保留 CURLOPT_SSL_VERIFYPEER => true,禁用验证仅用于调试。
掌握此模式后,你不仅能应对 Base64 分页键,还可适配任意“黑盒式”分页(如游标 cursor、哈希 hash、时间戳 since_id 等)。核心思想始终如一:把分页逻辑交给 API 响应本身,而非客户端猜测——这是构建高适应性数据同步服务的基石。



















