一、痛点:获取公开数据,你还在写爬虫?
做一个数据分析项目,第一步是什么?
不是写 SQL,不是调模型——是 把数据搞到手。
传统的获取公开数据的工作流:
- 找到数据 URL(GitHub 上的 CSV、政府开放数据集、S3 上的 Parquet)
- 打开浏览器下载,或者写 Python 脚本:
import requests import csv from io import StringIO
resp = requests.get(‘https://example.com/data.csv') reader = csv.DictReader(StringIO(resp.text)) data = [row for row in reader]
3. 用 Pandas/Excel 打开 → OOM 崩溃,文件太大
4. 切到 DuckDB 终于能跑了
整个过程 **20 分钟过去了,你还没写一行分析代码**。
更糟糕的是:
- **网页上有 100 个 CSV 文件**,你要写循环一个个下载再合并
- **数据每天更新**,你得写 cron 脚本定时抓取
- **文件 2GB+**,Pandas `read_csv` 直接内存溢出
**如果有一种方式,不下载、不写爬虫脚本、直接查 URL 里的数据呢?**
这就是 DuckDB 的「零 ETL 数据获取」能力。
---
## 二、解决方案:DuckDB 代替你的爬虫脚本
DuckDB 内置的 `httpfs` 扩展,允许你直接在 SQL 中读取 HTTP/HTTPS 上的 CSV、Parquet、JSON 文件。
**核心思想:数据在哪儿,DuckDB 就查到哪儿,不需要先搬到本地。**
### 2.1 三步开启远程查询
```sql
INSTALL httpfs; -- 只需安装一次(DuckDB 1.0+ 内置)
LOAD httpfs; -- 每次会话加载
-- 然后就可以直接查 URL 了
SELECT * FROM read_csv_auto('https://example.com/data.csv');
就这么简单。不需要 requests.get(),不需要 wget,不需要下载到临时目录。
2.2 一句话总结
| 传统方式 | DuckDB 方式 |
|---|---|
requests.get(url) 下载 → pandas.read_csv() | read_csv_auto('url') 直接查 |
| 写循环合并 100 个 CSV | read_csv_auto('https://.../*.csv') 通配符搞定 |
每天跑 cron 脚本 wget → 解压 → 分析 | cron → duckdb -c "SELECT ..." 一行命令 |
| 下载完整文件才知道能不能用 | Parquet 远程查询只需拉元数据(5-50KB) |
三、实战案例
3.1 案例一:直接查询 GitHub 上的公开 CSV
GitHub 上有海量公开数据集。传统做法:git clone 整个仓库。DuckDB 做法:一条 SQL。
-- GitHub 上的 NYC 出租车样本数据
INSTALL httpfs;
LOAD httpfs;
SELECT
VendorID,
COUNT(*) AS 订单数,
ROUND(AVG(total_amount), 2) AS 平均金额
FROM 'https://github.com/duckdb/duckdb-data/raw/main/nyc-taxi-data.parquet'
WHERE total_amount > 0
GROUP BY VendorID
ORDER BY 订单数 DESC;
执行时间:3-5 秒(只传输了 Parquet 的元数据和需要的列)。
如果用传统方式:
- 下载 42MB Parquet → 10 秒
- 加载到内存 → 5 秒
- 执行查询 → 2 秒 总计:17 秒
DuckDB 远程查询:5 秒,零临时文件。
3.2 案例二:URL 通配符批量抓取
这是 DuckDB 最被低估的能力——对远程 URL 也支持 glob 通配符。
假设一个政府开放数据网站按日期组织文件:
https://data.gov.example/traffic/2026/01/traffic_20260101.csv
https://data.gov.example/traffic/2026/01/traffic_20260102.csv
...
https://data.gov.example/traffic/2026/05/traffic_20260524.csv
传统做法:写 Python 循环 + requests.get() + 拼接 DataFrame。
DuckDB 做法:
-- 读取某个月份的所有 CSV
SELECT
strftime(date, '%Y-%m-%d') AS day,
COUNT(*) AS records,
AVG(speed) AS avg_speed
FROM read_csv_auto(
'https://data.gov.example/traffic/2026/05/*.csv'
)
GROUP BY day
ORDER BY day;
* 通配符:匹配该目录下所有 CSV 文件。
-- 递归读取所有目录下的 CSV
SELECT *
FROM read_csv_auto(
'https://data.gov.example/traffic/**/*.csv'
);
** 递归通配符:匹配所有子目录,适合多层目录结构的数据仓库。
-- 更精确的模式:只取 2026 年 5 月的数据
SELECT *
FROM read_csv_auto(
'https://data.gov.example/traffic/2026/05/traffic_*.csv'
);
3.3 案例三:S3 / 对象存储 + Parquet 列裁剪
当数据存储在 AWS S3 或兼容 S3 的对象存储上时,Parquet 格式的远程查询才是真正的杀手锏。
-- 查询 S3 上销售数据的汇总
SELECT
region,
SUM(revenue) AS total_revenue,
COUNT(DISTINCT customer_id) AS customers
FROM read_parquet(
's3://my-bucket/sales/2026/*/*.parquet'
)
WHERE revenue > 0
GROUP BY region
ORDER BY total_revenue DESC;
Parquet 列裁剪原理:DuckDB 通过 HTTP Range Request 只拉取 region、revenue、customer_id 这 3 列的数据块。如果原文件有 30 列 1GB,实际传输可能只有 30-80MB——传输量减少 90%+。
而且 DuckDB 还会利用 Parquet 的 row group 统计信息进行 谓词下推(WHERE revenue > 0),跳过不满足条件的 row group,进一步减少传输量。
3.4 案例四:完整的 Python 脚本(复制即用)
以下是一个完整的 Python 脚本,演示从远程 GitHub URL 拉取数据、分析、输出报表的全流程:
#!/usr/bin/env python3
"""
---
## 本文信息
| 项目 | 内容 |
|------|------|
| DuckDB 版本 | v1.5.x(部分功能基于 v2.0 Preview) |
| 最后验证 | 2026-09-12 |
| 测试环境 | Linux / x86_64 / 16GB RAM |
| 官方文档 | [DuckDB Documentation](https://duckdb.org/docs/) |
| GitHub | [pengzz9527/duckdb-blog](https://github.com/pengzz9527/duckdb-blog) |
如发现错误,欢迎通过 [GitHub Issue](https://github.com/pengzz9527/duckdb-blog/issues) 或邮件 [email protected] 反馈。
