如何在数据「追平」时停止API分页迭代请求?
分页API增量同步:重复记录块检测实现
场景说明
测试用免费分页API端点:https://api.instantwebtools.net/v1/passenger?page=0&size=10,该场景与实际需求高度契合:
- 通过迭代替换URL中的
page=0为page=i获取多页数据 - 单页响应包含固定10条记录(实际场景为48-52条随机变动)
- 无主键或索引,需通过行值比对判断重复
核心需求
每次运行代码时:
- 优先获取最新数据,将新增记录添加到已保存表的顶部
- 当请求返回的整页记录块已存在于已保存表中时,立即停止后续API请求迭代
伪工作流
- 首次运行:检索并存储所有记录
- 再次运行:
- 获取当前页响应记录
- 对比响应记录与已保存表:
- 若记录块未存在:添加到数据集顶部,继续迭代下一页
- 若记录块已存在:终止迭代
优化方案(避免双重循环)
为提升效率,采用整记录块比对而非单条行检查:
- 动态获取当前响应的记录数量(适配实际场景的48-52条随机变动)
- 以该记录数为步长,检查已保存表的开头部分是否存在完全匹配的记录块(如响应有9条记录,则比对表中
j:j+8行与响应行) - 仅当整块匹配时才停止迭代,避免因单条历史追溯记录导致的提前终止
带重复检测的完整Power Query M代码
let // 加载已保存的历史数据(替换为你的实际已保存查询/数据源) SavedData = try SavedPassengerData otherwise Table.FromRecords({}), SavedRowCount = Table.RowCount(SavedData), // 定义API相关变量 _base_url = "https://api.instantwebtools.net/v1/passenger?page=", _end_url = "&size=10", _max_iterations = 1000, // 实际场景可设为更大值,防止无限循环 // 定义单页数据获取函数 FnGetOnePage = (page_num as number) as list => let // API限流延迟 sleep = Function.InvokeAfter(() => null, #duration(0,0,0,1)), url = _base_url & Number.ToText(page_num) & _end_url, Source = Json.Document(Web.Contents(url)), // 提取并标准化数据结构(与SavedData结构对齐) RawData = Source[data], ExpandedAirline = List.Transform(RawData, (row) => Record.Combine({ Record.RemoveFields(row, {"airline"}), Record.RenameFields(row[airline]{0}, {"id", "name", "country", "logo", "slogan", "head_quaters", "website", "established"}, {"data.airline.id", "data.airline.name", "data.airline.country", "data.airline.logo", "data.airline.slogan", "data.airline.head_quaters", "data.airline.website", "data.airline.established"}) }) ), StandardizedData = Table.FromRecords(ExpandedAirline) in StandardizedData, // 迭代获取数据,加入重复块检测 GeneratedList = List.Generate( () => [ page = 0, current_data = FnGetOnePage(0), is_duplicate = if SavedRowCount >= Table.RowCount(current_data) then Table.Equals(Table.FirstN(SavedData, Table.RowCount(current_data)), current_data) else false ], // 终止条件:未达最大迭代次数、当前数据不为空、且未检测到重复块 each [page] < _max_iterations and Table.RowCount([current_data]) > 0 and not [is_duplicate], each [ page = [page] + 1, current_data = FnGetOnePage([page]), is_duplicate = if SavedRowCount >= Table.RowCount(current_data) then Table.Equals(Table.FirstN(SavedData, Table.RowCount(current_data)), current_data) else false ], each [current_data] ), // 合并新增数据与历史数据 CombinedData = if List.IsEmpty(GeneratedList) then SavedData else Table.Combine(GeneratedList) & SavedData, // 统一数据类型(与原代码对齐) #"Changed Type" = Table.TransformColumnTypes(CombinedData,{ {"_id", type text}, {"name", type text}, {"trips", Int64.Type}, {"__v", Int64.Type}, {"data.airline.id", Int64.Type}, {"data.airline.name", type text}, {"data.airline.country", type text}, {"data.airline.logo", type text}, {"data.airline.slogan", type text}, {"data.airline.head_quaters", type text}, {"data.airline.website", type text}, {"data.airline.established", Int64.Type} }) in #"Changed Type"
代码说明
- 历史数据加载:通过
SavedPassengerData加载已保存的历史表,首次运行时为空表 - 单页数据标准化:将API返回的嵌套结构展开,确保与历史表结构完全对齐,保证比对准确性
- 重复块检测:在
List.Generate中,每次获取新页后,检查历史表的前N行(N为当前页记录数)是否与当前页完全匹配,匹配则终止迭代 - 数据合并:将新增的多页数据合并后追加到历史表顶部,实现最新数据优先
内容的提问来源于stack exchange,提问作者wildcat89
相关产品推荐
相关产品推荐

