Featured image of post DuckDB实战:HTTPS/API数据接入

DuckDB实战:HTTPS/API数据接入

引言

在数据分析的日常工作中,我们常常需要从外部 API 获取数据——无论是 RESTful API 返回的 JSON、CSV 格式的导出文件,还是 HTTPS 加密的安全连接。传统做法是先编写脚本下载数据,保存到本地文件,再用工具加载分析。DuckDB 的 httpfs 扩展彻底改变了这一流程,让我们可以直接从 URL 读取远程文件,实现"零中间存储"的一站式分析。

本文将通过真实业务场景,演示如何使用 DuckDB 的 httpfs 扩展高效接入 HTTPS/API 数据,包括 JSON 解析、增量拉取、以及性能优化技巧。

架构图

图:DuckDB httpfs 数据接入架构——直接从 HTTPS 源到本地分析

环境准备

首先确保你的 DuckDB 版本 >= 0.9.0(推荐最新稳定版),并加载 httpfs 扩展:

INSTALL httpfs;
LOAD httpfs;

验证安装:

SELECT * FROM extensions WHERE name = 'httpfs';

场景一:直接读取远程 JSON API

1.1 基础读取

假设我们要从 GitHub API 获取某个仓库的最新提交记录:

-- 直接读取 GitHub API 返回的 JSON
CREATE TABLE github_commits AS
SELECT * FROM read_json_auto(
    'https://api.github.com/repos/duckdb/duckdb/commits?per_page=5'
);

SELECT commit->>'sha' AS sha,
       commit->>'message' AS message,
       author->>'name' AS author_name
FROM github_commits
LIMIT 5;

1.2 处理嵌套 JSON

GitHub API 返回的是嵌套结构,read_json_auto 会自动展开为扁平表。如果需要更精细的控制,可以使用 json_extract

-- 手动解析嵌套字段
SELECT 
    json_extract(commit, '$.sha') AS commit_sha,
    json_extract(commit, '$.commit.author.name') AS author_name,
    json_extract(commit, '$.commit.author.date') AS commit_date,
    json_extract(commit, '$.commit.stats.total') AS total_changes
FROM github_commits
LIMIT 3;

输出示例:

commit_shaauthor_namecommit_datetotal_changes
a1b2c3d…DuckDB Dev2026-07-15142
e4f5g6h…DuckDB Dev2026-07-1489
i7j8k9l…Community2026-07-1356

终端运行截图

图:DuckDB CLI 直接查询 GitHub API 的 JSON 数据

场景二:HTTPS 认证与自定义请求头

很多 API 需要身份验证。httpfs 支持通过 user_agent 和自定义 header 传递认证信息:

-- 使用 Bearer Token 访问私有 API
CREATE TABLE private_data AS
SELECT * FROM read_json_auto(
    'https://api.example.com/v1/data',
    type_map='auto',
    file_format='json',
    headers='Authorization: Bearer YOUR_TOKEN_HERE'
);

2.1 设置 User-Agent

某些 API 会拒绝没有 User-Agent 的请求。我们可以通过注册自定义函数来设置:

-- 设置全局 HTTP 配置
PRAGMA http_user_agent('DuckDB-DataBot/1.0');

-- 然后正常读取
SELECT * FROM read_csv_auto(
    'https://data.example.com/export.csv'
);

2.2 处理分页 API

对于返回大量数据的 API,通常需要分页拉取。我们可以用循环方式实现增量拉取:

-- 创建目标表
CREATE TABLE api_incremental (
    id INTEGER,
    name VARCHAR,
    value DOUBLE,
    fetched_at TIMESTAMP
);

-- 模拟分页拉取(实际场景中替换为真实 API)
WITH RECURSIVE page_counter(page) AS (
    VALUES (0)
    UNION ALL
    SELECT page + 1 FROM page_counter WHERE page < 4
)
INSERT INTO api_incremental
SELECT 
    page * 100 + row_number() OVER (ORDER BY (SELECT NULL)) AS id,
    'item_' || page * 100 + row_number() OVER (ORDER BY (SELECT NULL)) AS name,
    random() * 1000 AS value,
    current_timestamp AS fetched_at
FROM page_counter, generate_series(1, 100);

SELECT COUNT(*) AS total_records FROM api_incremental;

场景三:读取远程 CSV/Parquet 文件

httpfs 不仅支持 JSON,还支持 CSV、Parquet 等格式:

-- 直接读取远程 CSV
CREATE TABLE remote_csv AS
SELECT * FROM read_csv_auto(
    'https://raw.githubusercontent.com/duckdb/duckdb/main/extension/httpfs/test/data/sample.csv'
);

-- 查看数据概览
DESCRIBE remote_csv;

-- 直接对远程 Parquet 进行聚合查询(无需下载到本地)
SELECT 
    region,
    COUNT(*) AS order_count,
    SUM(total_amount) AS total_revenue
FROM read_parquet(
    'https://example-data.com/sales/*.parquet'
)
GROUP BY region
ORDER BY total_revenue DESC;

3.1 批量读取多文件

支持通配符和目录模式:

-- 读取目录下所有 Parquet 文件
SELECT * FROM read_parquet('https://storage.example.com/daily_reports/2026-07-*.parquet');

-- 合并多个 CSV
SELECT * FROM read_csv_auto(
    'https://example.com/exports/jan.csv',
    'https://example.com/exports/feb.csv',
    'https://example.com/exports/mar.csv'
);

场景四:增量拉取与数据去重

在实际业务中,API 数据往往是增量更新的。以下是完整的增量拉取模式:

-- 1. 创建增量拉取表(包含元数据)
CREATE TABLE IF NOT EXISTS api_staging (
    record_id VARCHAR PRIMARY KEY,
    data JSON,
    source_url VARCHAR,
    fetched_at TIMESTAMP DEFAULT current_timestamp,
    is_deleted BOOLEAN DEFAULT FALSE
);

-- 2. 增量拉取函数(伪代码,展示逻辑)
-- 实际使用时可封装为 DuckDB UDF

-- 3. 检查最后拉取时间
SELECT MAX(fetched_at) AS last_fetch FROM api_staging;

-- 4. 只拉取更新的数据(使用 If-Modified-Since 头)
INSERT INTO api_staging
SELECT * FROM read_json_auto(
    'https://api.example.com/data?since=2026-07-16T00:00:00Z',
    headers='If-Modified-Since: Fri, 16 Jul 2026 00:00:00 GMT'
);

-- 5. 去重:保留最新版本
DELETE FROM api_staging
WHERE record_id IN (
    SELECT record_id
    FROM api_staging
    GROUP BY record_id
    HAVING COUNT(*) > 1
)
AND rowid NOT IN (
    SELECT MIN(rowid)
    FROM api_staging
    GROUP BY record_id
);

性能优化技巧

4.1 并发连接

-- 设置最大 HTTP 连接数
PRAGMA http_max_connections = 10;

-- 设置超时时间(秒)
PRAGMA http_timeout = 30;

4.2 缓存策略

对于频繁访问的 API 端点,可以结合本地文件缓存:

-- 首次拉取并缓存
COPY (
    SELECT * FROM read_json_auto('https://api.example.com/heavy-endpoint')
) TO '/tmp/cache/heavy_endpoint.json';

-- 后续使用本地缓存
SELECT * FROM read_json_auto('/tmp/cache/heavy_endpoint.json');

4.3 流式处理大文件

-- 使用 stream 模式避免内存溢出
SELECT * FROM read_csv_auto(
    'https://example.com/large_export.csv',
    stream=true
);

完整实战示例:电商订单数据接入

以下是一个完整的电商订单 API 接入示例:

-- 创建订单表
CREATE TABLE orders (
    order_id VARCHAR,
    customer_id VARCHAR,
    product_name VARCHAR,
    quantity INTEGER,
    unit_price DECIMAL(10,2),
    order_date DATE,
    status VARCHAR,
    region VARCHAR
);

-- 从 API 拉取数据(模拟)
INSERT INTO orders
SELECT 
    json_extract(item, '$.order_id') AS order_id,
    json_extract(item, '$.customer_id') AS customer_id,
    json_extract(item, '$.product_name') AS product_name,
    cast(json_extract(item, '$.quantity') AS INTEGER) AS quantity,
    cast(json_extract(item, '$.unit_price') AS DECIMAL(10,2)) AS unit_price,
    cast(json_extract(item, '$.order_date') AS DATE) AS order_date,
    json_extract(item, '$.status') AS status,
    json_extract(item, '$.region') AS region
FROM read_json_auto(
    'https://shop.example.com/api/v2/orders?date_from=2026-01-01&limit=10000',
    type_map='auto'
);

-- 分析:按地区统计销售额
SELECT 
    region,
    COUNT(*) AS order_count,
    SUM(quantity * unit_price) AS revenue,
    AVG(unit_price) AS avg_price
FROM orders
WHERE status = 'completed'
GROUP BY region
ORDER BY revenue DESC;

总结

DuckDB 的 httpfs 扩展为数据分析师提供了一条通往远程数据的"高速公路":

  1. 零中间存储:直接从 URL 读取,无需先下载再加载
  2. 多格式支持:JSON、CSV、Parquet 均可直接查询
  3. 认证灵活:支持 Bearer Token、自定义 Header
  4. 流式处理:大文件不会撑爆内存
  5. 增量友好:配合时间戳和去重逻辑,轻松实现增量拉取

掌握这些技巧后,你可以将 DuckDB 变成一个强大的"数据连接器",让分析工作流更加流畅高效。

更多 DuckDB 实战技巧,请关注 DuckDB Lab(duckdblab.org)

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计

⚠️ 本站为独立社区项目,与 DuckDB 基金会及 DuckDB 官方项目无任何从属、背书或赞助关系。

"DuckDB" 是 DuckDB 基金会的注册商标,本站仅以事实描述方式使用该名称。

本站内容仅供教育与社区推广用途,不构成任何商业服务。