跳至内容
免费开始
返回博客

确保数据采集可靠的 REST API 技巧

Robert Sfichi最后更新于 2 min read
确保数据采集可靠的 REST API 技巧
简而言之:一个实用的收集 API 能让客户端以清晰的方式请求数据、继续遍历、从故障中恢复,并了解已收集的内容。这些 REST API 技巧将每个服务器端契约与可靠的消费者必须实现的行为联系起来。

一个采集器检索了三页数据,在第四页时超时,并于次日早晨重新启动。它究竟是跳过记录、重复工作,还是安全地恢复,取决于早在超时发生之前就做出的决策。当 REST API 建议将这些决策作为生产者与消费者之间的具体契约来处理时,其价值便最为凸显。

本指南侧重于用于定期收集、同步或分析数据的 API。其目的并非解决关于 REST 的所有架构争议,而是确保在数据集变更、凭据过期、请求失败以及客户端在不同时间升级时,接口仍能保持可预测性。

对于每项设计选择,请考虑客户端必须持久化哪些信息,以及它能够安全推断出哪些信息。如果 next ,若未在文档中说明光标过期机制,则该字段本身并不足够。若客户端无法区分临时过载与无效输入,仅靠一条错误消息也不够。如果成功响应悄然改变了价格字段的含义,仅凭成功响应也不够。

下文的七个部分涵盖了资源契约、分页、故障处理、条件请求、访问控制、模式演进以及操作证明。它们还为使用非自己设计的 API 的开发人员提供了一份审查清单:先确定接口的保证内容,然后在数据收集计划中明确列出所有缺失的保证。

1. 以资源契约为基础构建 REST API 建议

在设计大型端点家族之前,先描述一条记录。对于目录项,需定义其稳定标识符、价格的含义、货币单位、可选字段以及更新时间戳。说明遗漏的字段与值为 null 的字段有何区别。这些选择决定了消费者如何存储和比较观测值。

为过滤条件赋予同等精确的含义。如果 updated_after 支持时间戳,请明确其格式、时区以及时间范围是否包含边界值。说明筛选条件是针对业务变更、数据库更新还是发布时间。当多个不同事件共享一个含糊的“更新”标签时,客户端将无法构建正确的增量收集器。

请确保 HTTP 方法的使用与其语义保持一致。检索端点不应仅因接收 GET 请求就生成需付费的导出数据。如果启动任务会改变应用程序状态,请为该操作提供明确的契约,并记录如何检索其结果。安全方法与幂等方法之间的区别在 HTTP 语义中已有定义。

这些 REST API 建议同样适用于表示形式的选择。集合响应可以使用包含记录和分页元数据的封装,但该封装需要有已记录的模式。避免强迫消费者检查可读消息来判断数据是否完整。如果响应是不完整的,请在具有明确含义的机器可读字段中说明这一事实。

契约细节

提供方应定义

消费者应保留

标识

稳定标识符及重用策略

源ID应与行位置分离

时间

时区与更新含义

原始时间戳加上解析结果

货币

货币和单位

支持小数运算的金额和货币

缺失数据

省略与空值的语义区别

模式要求的区分

补全

完整结果与部分结果

运行状态与采集窗口

当客户端实现此接口时,应有选择地保留原始响应以供诊断,并将规范化记录单独存储。不要自动将每个未知字段转换为生产列。先就下游数据集的承诺达成一致,然后将源合同显式映射到其中。

2. 设计可在重启后保持有效的分页机制

有用的下一页引用应遵循已记录的规则来表示连续性。明确说明 API 采用页码、偏移量、游标还是服务器生成的链接。说明在遍历过程中哪些过滤条件和排序顺序必须保持不变。如果游标会过期,请公布其有效期或失效的条件。

关于分页的 REST API 建议往往仅止于选择每页条目数。更棘手的问题在于,底层数据在不同请求之间可能发生变化。当有条目插入到当前位置之前时,偏移量可能会发生偏移。光标可以减少一些遍历问题,但并不能自动提供不可变的快照。请将一致性保证与分页机制分开说明。

最好直接向用户提供应遵循的延续引用,而不是要求他们自行生成。GitHub 文档中记录的 REST 分页机制使用响应链接来指示相关页面;其分页指南就是客户端遵循服务器导航契约的具体示例。其他 API 则在响应正文中使用令牌,因此用户必须阅读具体的文档说明。

明确记录终止条件。下一个游标不存在可能意味着查询已完成,而页面记录较少时则未必如此。除非该行为是契约的一部分,否则应避免要求客户端根据记录数推断结束点。应包含空结果示例,因为零条记录是正常的业务结果,并不一定代表失败。

消费者应仅在相应页面被接受并存储后才持久化检查点。如果检查点存储与记录存储是分离的,应通过去重或事务协调来确保重放安全。在保存当前记录之前保存下一个游标,会在崩溃后造成数据缺口。

对页面、时间以及重复的继续值设置保护性限制。如果服务器两次返回相同的光标,客户端应以分类的分页失败终止请求,而不是无限循环。保护性终止必须与正常完成保持可区分性。

对于频繁变化的集合,应明确记录对账策略。客户端可以将增量集合与定期全量比较相结合,前提是两者均符合 API 的预期用途。删除操作也需要明确约定:如果遍历不完整,某条记录从某一页中消失,这仅是删除的弱证据。

3. 将错误和重试作为共享策略

将需要不同处理方式的错误类别区分开来。无效的过滤条件需要更正请求;过期的凭据需要遵循文档中规定的身份验证流程;临时过载则可能需要等待。服务器响应应返回稳定的错误代码、适当的 HTTP 状态码,以及有助于支持团队追踪故障的请求标识符(且不包含机密信息)。

最佳的 REST API 建议应使恢复操作具有边界。提供方应说明哪些响应可重试以及速率限制如何应用。调用方应设置最大尝试次数、总时间预算和延迟策略,而非对每个异常无限期重试。重复失败应生成一个可见的未完成任务,并附有有用的诊断信息。

当提供该参数时,应 Retry-After :它可能是以秒为单位的延迟,也可能是 HTTP 日期格式。MDN 的 Retry-After 参考文档中记载了这两种形式。如果客户端剩余的任务预算无法满足请求的延迟时间,应明确推迟或终止任务,而非提前重试。

当重试策略允许重试时,应结合抖动与退避机制,以避免所有工作者在同一瞬间同时唤醒。对于共享同一账户或令牌的工作者,应协调速率限制。若多个进程同时运行,即使独立限制每个进程,仍可能导致账户级配额超载。

对于会创建导出、订阅或其他状态的操作,请格外谨慎。超时并不意味着服务器未执行任何操作;它可能表示操作完成后响应丢失了。如果 API 支持幂等键,请明确说明其作用域、保留时间,以及当同一键伴随不同有效载荷时的行为。 消费者应根据该契约,在执行同一逻辑操作时重复使用该键。

GitHub 的 REST API 最佳实践说明了提供商指导为何重要:其中包含关于顺序请求、速率限制错误和条件请求的具体建议。切勿假设其他提供商具有相同的配额或重试行为。

在客户端实现中,应选择具有显式超时和连接管理控制功能的 HTTP 库。我们的 Python HTTP 客户端指南为此层提供了背景信息。应将响应分类置于通用传输封装器之外,以便端点特有的含义保持可见且可测试。

4. 为客户端提供验证器与刷新策略

一个定期收集数据的服务不应被迫下载庞大的表示形式,结果却发现没有任何变化。在适当的情况下,应公开 ETag 等验证器,并记录条件请求的行为。客户端可以保留该验证器,并在后续请求中发送相关条件。

关于缓存的 REST API 建议需要区分传输的字节与数据的正确性。ETag 标识的是服务器契约下的表示形式;它不一定是客户端应解码的时间戳或内容哈希。应将该值视为不透明数据。将其与请求标识以及它所验证的缓存表示形式一同存储。

对于收到“未修改”(Not Modified)响应的条件 GET 请求,应使用已存储的表示形式。切勿将空响应正文输入常规 JSON 解析器,也不要用空数组替换缓存的集合。没有匹配缓存正文的消费者无法将该响应视为包含最新数据集的响应来使用。

在缓存键中包含相关的请求上下文:端点、查询参数、选定的表示形式,以及 API 要求的任何标识分区。为某个账户缓存的响应,绝不能仅仅因为 URL 匹配就提供给另一个账户。应记录授权和个性化设置如何影响缓存。

将增量过滤器和验证器设计为独立的工具。集合验证器可以指示所请求的表示形式未发生变化;更新过滤器则用于查询满足特定时间条件的记录。二者均无法自动解释删除、补全的时间戳或重新排序的页面。应明确说明每种机制解决的问题。

这些 REST API 建议应能引导制定明确的刷新计划。例如,消费者可以验证频繁访问的资源表示,轮询更新源以获取新观测数据,并按照批准的时间表对完整数据集进行校对。具体的时间表是业务和容量方面的决策,而非 HTTP 的保证。

对“无变更”路径的测试应与“完整响应”路径同样严谨。采集器在首次运行时往往能正常工作,但当验证器开始屏蔽响应正文时,便可能出现故障。还应测试缓存丢失情况,以确保恢复过程不依赖于本地已不存在的数据。

5. 保持身份验证范围狭窄且可观察

为集成提供其收集任务所需的最低访问权限。只读目录客户端不应需要管理员级别的写入权限。应记录身份验证方案、所需范围、令牌有效期以及支持的续期流程。应确保授权失败足够明显,以便客户端能够停止操作或请求正确的干预措施。

承载凭证必须受到保护,因为持有即意味着可以使用。OAuth 承载令牌规范描述了通过 Authorization 头进行传输的方式,以及围绕承载令牌的安全要求。请使用提供商文档中指定的 HTTPS 端点,切勿将密钥置于普通查询字符串或诊断输出中。

关于凭据的 REST API 建议同时也适用于部署。应通过应用程序的密钥管理机制提供密钥,在日志中屏蔽请求头信息,并将开发凭据与生产环境凭据区分开来。如果运维人员需要识别哪个集成失败,请记录一个非机密的凭据标识符。令牌本身在事件报告中并无实际参考价值。

在附加凭证之前,请验证后续请求的目标。如果 API 返回了下一页的 URL,调用方应确保该 URL 属于文档中记录的允许源(或多个源)。重定向行为也应进行明确处理。通用的 URL 跟随循环绝不能将“Authorization”头转发到任意主机。

对于您设计的 API,请将身份验证状态与数据收集状态分离。凭证更新不应要求客户端无故放弃其存储的数据,但文档应说明游标是绑定到账户、令牌还是权限集。即使游标仍能解析,作用域的变更也可能改变可见的记录。

当工作流同时使用授权的浏览器会话时,需了解 HTTP Cookie 与 API 令牌标头的区别。切勿假设复制浏览器会话就能实现受支持的 API 集成。请使用专为该接口设计的访问机制,并在各工作进程之间明确所有权归属。

6. 在不破坏集合的情况下演进模式

请提供普通记录、缺失的可选值、空集合以及错误情况的示例。然后使正式模式与这些示例保持一致。如果服务偶尔会针对未知值返回空字符串,那么声明某个字段“始终为数值型”的类型声明就会产生误导。

JSON Schema 可以描述属性、必填字段以及附加属性的处理方式,具体请参阅其对象参考文档。需明确用户端应在何处保持容错,何处应报错。接受新的可选字段可能是合理的;但在要求货币金额的字段中接受字符串,则可能隐藏了破坏性变更。

关于版本控制的 REST API 建议应涵盖含义和形式两方面。将价格从次要单位改为主要单位,虽然可以保留 JSON 数字类型,但会导致所有计算失效。即使通用解析器能接受响应,重命名状态值或更改时区解释也可能导致兼容性问题。

对于提供方,应制定兼容性政策,并通过客户端实际使用的渠道传达弃用时间表。对于消费者,当提供方支持版本选择时,应锁定支持的 API 版本,并保留该版本的测试用例。切勿等到仪表盘显示出不合理的数值后才发现需要进行迁移。

请将无效记录隔离,并附上明确的分类原因和有限的诊断证据。需决定一条格式错误的记录是导致页面失败、运行失败,还是允许部分发布。该政策取决于下游用途,但必须明确规定。默默丢弃无效行会使输出看起来比实际更完整。

将提取或转换版本与已接受的数据一同存储。当解析器错误得到修正时,该版本有助于识别哪些记录可能需要重新处理。如果保留的原始响应已获批准且内容充分,请在生成不必要的重复请求之前对其进行重新处理。

这些 REST API 建议确立了一个有用的分工:发布方负责发布合同,而消费方负责根据合同进行验证以及解读其衍生数据集的含义。任何一方都不应依赖对方来发现所有不一致之处。

7. 将恢复测试纳入 API 体验

对于每小时运行一次的采集器而言,正常路径请求几乎无法证明其可靠性。针对空页面、重复光标、过期光标、速率限制、格式错误的 JSON、意外字段类型,以及存储后但检查点创建前的中断等情况,构建测试 fixture 或测试服务。在实现恢复机制之前,应明确说明每种情况的预期结果。

对于生产者而言,这些场景既是文档测试,也是软件测试。如果团队无法说明当光标在运行中途过期时客户端应如何处理,则说明合同不完整。应消除这种模糊性,并将答案放在端点文档旁边,以便集成开发人员能够找到。

对于消费者而言,需追踪有用的结果:已接受的记录、被拒绝的记录、已完成的页面、重试次数、检查点年龄以及最终完成状态。记录请求 ID 和端点名称时,应避免记录机密信息或不必要的个人数据。汇总成功率不应掩盖这样一个事实:每个大型数据集都会在最后一个页面停止处理。

当可观测性能够解答业务问题时,REST API 技巧才真正具有实用价值。分析师能否确信该类别数据已完全收集?昨天的数据被重复使用,是因为没有任何变化,还是因为今天的运行失败了?当一小部分记录被隔离时,下游任务是否应继续执行?在实现发布自动化之前,请先明确这些决策。

确保示例可重现。使用沙箱或本地测试环境进行故障测试,而非通过故意超载生产 API 来探索其极限。一份有文档记录的测试响应足以验证解析和重试分类;容量实验则需要单独的规划和授权。

在模式、身份验证和分页发生变更后,应重新审查集成情况,而不仅仅是在传输失败后。可靠的数据采集是不断演进的 API 与不断演进的客户端之间的一种关系。一套精简的契约测试套件可在错误记录到达用户之前,使这种关系清晰可见。

关键要点

  • 在选择端点细节之前,先定义资源标识、字段含义和数据集合的完整性。消费者需要能够在每次运行中保持一致的语义。
  • 分别记录延续性、过期规则和一致性。在数据持久化后保存检查点,并确保重放安全。
  • 使重试规则具有明确边界且针对特定提供商。将模棱两可的写入结果与读取失败区别对待。
  • 将验证器与缓存表示配对,正确划分凭据和缓存,并在发布衍生数据前测试模式变更。

常见问题

批量导出应与实时集合具有相同的接口吗?

它可以共享相同的资源定义,但可能需要单独的作业契约。长期运行的导出应配备状态资源、有文档记录的保留窗口,以及对所代表的时间范围或快照的清晰描述。应明确说明这些生命周期细节,而非让客户端无限期保持一个请求处于打开状态。

Webhook 能否替代定时同步?

Webhook 可以减少轮询,但其交付和重放保证决定了需要进行何种额外收集。应明确记录事件是否可能被重复、延迟或重新交付,以及消费者如何恢复遗漏的时间间隔。将事件视为另一种输入契约,当所需的完整性保证超出事件流的已记录行为时,应使用对账机制。

文档是否应包含所有受支持的编程语言?

应为实际的集成用户提供足够的示例,并确保其行为的一致性。在每个示例中,身份验证、URL 编码、响应分类和分页都应具有相同的含义。一套经过维护的少量示例,比一套代码片段在端点契约上存在矛盾或遗漏重要故障行为的大量示例更有用。

将契约转化为可靠的集成方案

在扩展接口之前,先将这些 REST API 技巧应用于一个集合工作流。选择一个具有代表性的资源,定义其标识和字段含义,并演练分页中途重启的情况。然后针对过期令牌、未修改响应以及违反模式的记录重复此练习。

这些场景揭示了端点目录可能忽略的决策点。它们还为生产者和消费者提供了一种审查变更的共同语言:响应的含义、客户端必须持久化的内容,以及哪些结果被视为完成。将该协议与可执行的测试用例和已发布的文档紧密关联,以确保其在团队人员变动后依然有效。

当所需的源数据仅存在于授权网页上,而非通过合适的 API 提供时,请考虑将 WebScrapingAPI 的 Scraper API 作为数据采集的输入源。对生成的管道也应遵循同样的规范:明确的记录、有限的恢复范围、有效性验证以及真实的完成状态。

关于作者

Robert Sfichi, 全栈开发工程师 @ WebScrapingAPI

Robert Sfichi

全栈开发工程师

罗伯特·斯菲奇是 WebScrapingAPI 的团队成员,致力于产品开发,并协助构建可靠的解决方案,以支持该平台及其用户。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。