Cloudflare的Workers+D1
# 使用cf爬取第三方新闻内容
# 一、需求
# 1.1 背景
上游提供新闻类 HTTP API(示例基址:http://api.didaliveshow.com/article),列表与详情分开展示。若每次由客户端直接请求上游,会遇到几类问题:接口稳定性与速率不可控、列表与详情字段分散不利于产品侧统一消费、以及无法在本地做分页缓存与统计。
因此需要一层「同步服务」:定期或按需把数据拉取到自己的存储里,再由自有接口对外提供稳定、可预期的 JSON。
# 1.2 目标(功能)
- 全量列表入库:把上游所有列表页扫完,至少落库每条新闻的 id 及列表中已有的元数据(标题、缩略图、时间等),先不写正文,以降低单次任务压力。
- 详情补全:在列表阶段完成后,按文章 id 有序批量拉取详情,写入正文等字段,并标记「已同步详情」。
- 断点续跑:列表阶段不能假设一次 Worker 执行能跑完所有页(执行时间、子请求数均有限制),需要持久化游标,支持多次调用接续进度。
- 只读对外接口:提供分页列表、单篇详情等 GET JSON 接口,供站点或 App 使用(列表默认不含
body,避免大包)。
# 1.3 非功能与约束
- 部署成本:尽量无独立服务器运维,适合 Serverless / 边缘部署。
- 存储:与 Worker 同生态、SQL 易查、成本低;本项目选用 Cloudflare D1(SQLite)。
- 合规:爬取与数据使用须遵守上游服务条款与适用法律;频率需可控,避免对上游造成不当压力(终端或调度侧可加间隔、限流)。
# 二、实现
# 2.1 技术选型
| 层级 | 选择 | 说明 |
|---|---|---|
| 运行时 | Cloudflare Workers | 边缘执行,与 fetch、定时/HTTP 触发天然契合;单文件入口 src/index.ts 导出 fetch 处理器。 |
| 数据库 | D1 | 通过 wrangler.toml 绑定 DB;迁移放在 migrations/,用 Wrangler 在本地/远程应用。 |
| 语言 | TypeScript | 类型描述上游 JSON 与查询结果,便于维护。 |
本项目不设独立 build 脚本:wrangler dev / wrangler deploy 时完成打包与 TS 处理。
# 2.2 数据模型(概要)
articles:以上游文章id为主键,保存列表字段、详情字段(如body、category)、原始详情 JSON(result_json),以及list_synced_at、detail_synced_at用于判断列表/详情是否已同步。crawl_meta:键值表,保存列表阶段的list_next_page、list_max_page,实现列表游标持久化。
详见 migrations/0001_init.sql、migrations/0002_crawl_meta.sql。
# 2.3 核心流程:两阶段同步
阶段一(列表)
客户端反复请求GET /crawl/sync-list?chunk=…(单次在 Worker 内最多处理若干列表页,避免超时)。首次从第 1 页解析上游总页数并写入crawl_meta,之后按游标继续;列表内若出现重复 id,在写入前按 id 去重(后者覆盖前者)。直到返回list_phase_done: true,表示列表页已按游标跑完。阶段二(详情)
反复请求GET /crawl/sync-detail?limit=…或GET /pending?limit=…:从库中选出detail_synced_at为空的记录,按id升序取一批,逐条请求上游详情并 upsert。配合GET /stats观察pendingDetail,直至为 0(或不再下降,需结合失败重试策略排查)。
# 2.4 接口分层(思路)
- 写库(爬取):
/crawl/sync-list、/crawl/sync-detail、/pending等,负责与上游通信并写 D1。 - 读库(应用):
/api/list、/api/detail,仅从 D1 读取,支持分页与「仅已同步详情」等查询。 - 统计:
/stats汇总条数、列表阶段是否完成、待补详情数量等。
调试辅助路由(如单页列表、固定区间列表、单篇详情拉取)便于排障,生产调度以两阶段主路径为主。
# 2.5 部署与数据库迁移注意
- Worker 通过
npm run deploy(即wrangler deploy)发布;首次需npx wrangler login。 - Git 自动部署通常只发代码,不会自动执行 D1 迁移;新增或修改
migrations/*.sql后,需在可信环境执行远程迁移(见 使用说明.md)。 wrangler.toml中的database_id必须与 Cloudflare 控制台中的 D1 实例一致。
# 三、总结
# 3.1 做得好的地方
- 问题拆分清晰:列表与详情分两阶段,配合游标与
detail_synced_at,兼顾 Worker 时限与可恢复进度。 - 结构简单:单 Worker 单入口,读写路由集中,依赖少,适合小团队或个人维护。
- 读侧接口明确:分页列表与单篇详情分离,列表默认不带正文,有利于列表页性能。
# 3.2 局限与可改进方向(备忘)
- 安全:若 Worker 公网可访问,爬取类接口缺少鉴权时可能被滥用;生产环境建议加 Secret、Cloudflare Access 或限流。
- 健壮性:对上游 HTTP 状态码、失败重试、列表页失败是否跳过等策略可按业务收紧(避免静默丢页)。
- 性能:详情批量拉取当前偏串行,在子请求配额内可引入有限并发与 D1 batch 写入以缩短单次请求耗时。
# 3.3 一句话概括
这是一个 Cloudflare Workers + D1 的小型同步服务:把上游新闻先全列表、后补详情写入 SQLite,再对外提供稳定的只读 JSON API;实现上强调游标断点与两阶段分工,部署上记住代码与 D1 迁移分离发布。