引言
在数据分析的日常工作中,我们常常需要从外部 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_sha | author_name | commit_date | total_changes |
|---|---|---|---|
| a1b2c3d… | DuckDB Dev | 2026-07-15 | 142 |
| e4f5g6h… | DuckDB Dev | 2026-07-14 | 89 |
| i7j8k9l… | Community | 2026-07-13 | 56 |

图: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 扩展为数据分析师提供了一条通往远程数据的"高速公路":
- 零中间存储:直接从 URL 读取,无需先下载再加载
- 多格式支持:JSON、CSV、Parquet 均可直接查询
- 认证灵活:支持 Bearer Token、自定义 Header
- 流式处理:大文件不会撑爆内存
- 增量友好:配合时间戳和去重逻辑,轻松实现增量拉取
掌握这些技巧后,你可以将 DuckDB 变成一个强大的"数据连接器",让分析工作流更加流畅高效。
更多 DuckDB 实战技巧,请关注 DuckDB Lab(duckdblab.org)