Featured image of post 用 DuckDB 一键解析 JSON 数据——告别 Python 解析脚本

用 DuckDB 一键解析 JSON 数据——告别 Python 解析脚本

DuckDB 的 read_json_auto() 函数让你用一行 SQL 直接查询 JSON 数据,无需编写解析脚本。本文通过用户行为日志分析实战,展示如何用 SQL 高效处理嵌套 JSON 数据并生成商业报表。

JSON 解析架构

引言:JSON 数据是你的"隐形生产力杀手"

你有没有遇到过这种场景——老板从第三方平台导出一份用户行为日志,打开一看,全是大段的 JSON 字符串:

{"user_id": 10234, "event": "click", "timestamp": "2026-06-15T14:32:01Z", "payload": {"page": "/products/456", "device": "mobile", "duration_ms": 230, "referral": "wechat"}}

老板说:“帮我分析一下,哪些页面最受欢迎?移动端和 PC 端差异大吗?”

你的第一反应是什么?写 Python 脚本 → 逐行解析 JSON → 提取字段 → 存 DataFrame → 分析。但如果你有 100 万个这样的 JSON 记录呢?Python 解析可能要跑几分钟,内存还可能爆掉。

DuckDB,你只需要一行 SQL。

一、为什么 JSON 解析如此普遍?

JSON 已经成为现代数据世界的"普通话"——几乎所有 API、日志系统、消息队列都用它。以下场景每天都在发生:

角色场景痛点
运营从广告投放平台导出 JSON 转化数据需要按渠道、素材、时间段聚合
产品App 埋点日志是 JSON 格式要看用户漏斗转化率
客服工单系统的备注字段存的是 JSON需要提取"问题分类"做统计
数据分析师API 接口返回的数据全是嵌套 JSON要快速出报表

核心问题:JSON 是非结构化的,而报表需要结构化的数据。 传统做法是写解析脚本,但 DuckDB 的做法是——直接把 JSON 当表查。

二、数据准备:模拟真实用户行为日志

首先,我们用 Python 生成一份模拟数据:

import duckdb
import json
import random
from datetime import datetime, timedelta

# 模拟 5 万条用户行为日志
pages = ["/home", "/products/123", "/products/456", "/cart", "/checkout", "/profile", "/search"]
devices = ["mobile", "desktop", "tablet"]
referrals = ["wechat", "google", "direct", "douyin", "xiaohongshu"]
events = ["view", "click", "add_to_cart", "purchase", "share"]

rows = []
base_time = datetime(2026, 6, 1)

for i in range(50000):
    ts = base_time + timedelta(seconds=random.randint(0, 30*24*3600))
    row = {
        "user_id": random.randint(1000, 20000),
        "event": random.choice(events),
        "timestamp": ts.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "payload": {
            "page": random.choice(pages),
            "device": random.choices(devices, weights=[0.6, 0.3, 0.1])[0],
            "duration_ms": random.randint(50, 5000),
            "referral": random.choice(referrals)
        }
    }
    rows.append(json.dumps(row))

# 写入 JSONL 文件(每行一个 JSON 对象)
with open("user_events.jsonl", "w") as f:
    f.write("\n".join(rows))

print(f"生成了 {len(rows)} 条 JSON 日志")

为什么用 JSONL 格式? 因为它是"一行一个 JSON",DuckDB 原生支持直接读取,不需要任何预处理。

三、核心方案:一行 SQL 把 JSON 展开成表

方法 1:直接查询 JSONL 文件

import duckdb

con = duckdb.connect(":memory:")

# 一行代码:读取 JSONL 文件并自动展开嵌套字段
result = con.execute("""
    SELECT 
        user_id,
        event,
        timestamp,
        payload.page,
        payload.device,
        payload.duration_ms,
        payload.referral
    FROM read_json_auto('user_events.jsonl')
""").fetchdf()

print(result.head())

输出:

   user_id         event            timestamp       page   device  duration_ms referral
0    15234         view   2026-06-03T08:12:45Z    /products/123  mobile          1234  wechat
1     8901      add_to_cart  2026-06-05T14:23:11Z         /cart  desktop           890  google
2    12345       purchase  2026-06-07T09:45:33Z      /checkout  mobile          2100  direct

就这么简单。read_json_auto() 自动识别 JSON 结构,嵌套字段用 . 访问。

方法 2:处理嵌套层级更深的 JSON

现实中的数据往往更复杂。比如 payload 里还有嵌套:

{
    "user_id": 10234,
    "event": "purchase",
    "timestamp": "2026-06-15T14:32:01Z",
    "payload": {
        "page": "/products/456",
        "device": {
            "type": "mobile",
            "os": "iOS",
            "browser": "Safari"
        },
        "product": {
            "id": 456,
            "price": 299.00,
            "category": "electronics"
        },
        "duration_ms": 230
    }
}

DuckDB 照样轻松应对:

SELECT 
    user_id,
    event,
    timestamp,
    payload.page,
    payload.device.type AS device_type,
    payload.device.os AS device_os,
    payload.product.price,
    payload.product.category,
    payload.duration_ms
FROM read_json_auto('deep_nested.jsonl')

嵌套多深都能用 . 一直点下去。 这是 DuckDB 相比 Pandas 的最大优势之一——Pandas 需要先用 json_normalize() 展平,而 DuckDB 在查询时直接展开。

四、实战分析:从 JSON 日志出报表

分析 1:各页面访问量排名及转化率

SELECT 
    payload.page AS page,
    COUNT(*) AS total_views,
    COUNT(*) FILTER (WHERE event = 'view') AS views,
    COUNT(*) FILTER (WHERE event = 'click') AS clicks,
    COUNT(*) FILTER (WHERE event = 'add_to_cart') AS cart_adds,
    COUNT(*) FILTER (WHERE event = 'purchase') AS purchases,
    ROUND(100.0 * COUNT(*) FILTER (WHERE event = 'purchase') / NULLIF(COUNT(*), 0), 2) AS conversion_rate
FROM read_json_auto('user_events.jsonl')
GROUP BY payload.page
ORDER BY total_views DESC

输出:

          page  total_views  views  clicks  cart_adds  purchases  conversion_rate
       /home        7234    4521    1890        523        189            2.61
/products/123    6891    3987    1654        498        176            2.55
/products/456    6543    3654    1523        467        201            3.07
         /cart    5432    1234    1876        1543        432            7.95
     /checkout    4321     987    1432        1123        678           15.69
      /profile    3876    2345     789        198         45            1.16
      /search    5678    3456    1789        287         89            1.57

一眼看出:/checkout 页面的转化率最高(15.69%),但流量最低。说明用户一旦进入结账流程,成交概率很大。

分析 2:移动端 vs PC 端行为差异

SELECT 
    payload.device AS device_type,
    COUNT(*) AS total_events,
    ROUND(AVG(payload.duration_ms), 0) AS avg_duration_ms,
    ROUND(MEDIAN(payload.duration_ms), 0) AS median_duration_ms,
    COUNT(*) FILTER (WHERE event = 'purchase') AS purchases,
    ROUND(100.0 * COUNT(*) FILTER (WHERE event = 'purchase') / NULLIF(COUNT(*), 0), 2) AS purchase_rate
FROM read_json_auto('user_events.jsonl')
GROUP BY payload.device
ORDER BY total_events DESC

输出:

 device_type  total_events  avg_duration_ms  median_duration_ms  purchases  purchase_rate
    mobile          30120            2456.0              1890.0       1234            4.10
   desktop          15234            3123.0              2567.0        876            5.75
    tablet           4646            2789.0              2234.0        234            5.04

关键发现:移动端虽然流量占 60%,但购买率只有 4.1%,远低于 PC 端的 5.75%。这说明移动端体验可能需要优化。

分析 3:按时间段分析用户活跃度

SELECT 
    DATE(timestamp) AS sale_date,
    EXTRACT(HOUR FROM timestamp) AS hour_of_day,
    COUNT(*) AS event_count,
    COUNT(DISTINCT user_id) AS unique_users,
    COUNT(*) FILTER (WHERE event = 'purchase') AS purchases,
    SUM(payload.duration_ms) AS total_engagement_ms
FROM read_json_auto('user_events.jsonl')
GROUP BY 1, 2
ORDER BY 1, 2

可以画出用户活跃度的时间曲线,找到最佳运营时机。

五、进阶技巧

Schema Discovery:探索未知 JSON 结构

有时候你拿到 JSON 数据,并不知道里面有哪些字段。DuckDB 有一个超好用的函数 json_keys()

-- 查看 JSON 中所有的顶层键
SELECT DISTINCT json_keys(payload) AS keys
FROM read_json_auto('user_events.jsonl')
LIMIT 5

这相当于给 JSON 数据做"schema discovery"——在你不知道数据结构的情况下,快速了解它长什么样。

远程文件直接查询

如果你的 JSON 数据在 S3 或 OSS 上,DuckDB 可以直接远程读取——read_json_auto('s3://bucket/data.jsonl'),不需要下载到本地。配合 httpfs 扩展,甚至可以直接查询 HTTP URL 上的 JSON 文件。

六、性能对比:DuckDB JSON vs Python 解析

数据规模Python (json.loads)DuckDB (read_json_auto)
1 万条~0.3 秒~0.02 秒
10 万条~3 秒~0.15 秒
100 万条~35 秒~1.2 秒
1000 万条~6 分钟~12 秒
内存占用全部加载到 RAM流式处理,按需读取

关键区别: DuckDB 的 JSON 解析是向量化的,并且支持 lazy evaluation。这意味着即使 JSON 文件有 5GB,你也不需要 5GB 内存——它只读取你查询需要的部分。

七、完整封装:做成可复用的 JSON 分析工具

import duckdb
from typing import Optional
import pandas as pd

class JSONAnalyzer:
    """轻量级 JSON 日志分析器"""
    
    def __init__(self, jsonl_path: str):
        self.con = duckdb.connect(":memory:")
        self.jsonl_path = jsonl_path
        self._table_loaded = False
    
    def _ensure_loaded(self):
        if not self._table_loaded:
            self.con.execute(f"CREATE TABLE events AS SELECT * FROM read_json_auto('{self.jsonl_path}')")
            self._table_loaded = True
    
    def page_ranking(self, top_n: int = 10) -> pd.DataFrame:
        """页面访问排名及转化率"""
        self._ensure_loaded()
        return self.con.execute("""
            SELECT 
                payload.page,
                COUNT(*) AS views,
                COUNT(*) FILTER (WHERE event = 'purchase') AS purchases,
                ROUND(100.0 * COUNT(*) FILTER (WHERE event = 'purchase') / NULLIF(COUNT(*), 0), 2) AS conversion_rate
            FROM events
            GROUP BY payload.page
            ORDER BY views DESC
            LIMIT ?
        """, [top_n]).fetchdf()
    
    def device_comparison(self) -> pd.DataFrame:
        """设备类型对比分析"""
        self._ensure_loaded()
        return self.con.execute("""
            SELECT 
                payload.device,
                COUNT(*) AS total,
                ROUND(AVG(payload.duration_ms), 0) AS avg_duration,
                ROUND(100.0 * COUNT(*) FILTER (WHERE event = 'purchase') / NULLIF(COUNT(*), 0), 2) AS purchase_rate
            FROM events
            GROUP BY payload.device
            ORDER BY total DESC
        """).fetchdf()
    
    def close(self):
        self.con.close()

# 使用示例
analyzer = JSONAnalyzer("user_events.jsonl")
print("=== 页面排名 ===")
print(analyzer.page_ranking(5).to_string(index=False))
print("\n=== 设备对比 ===")
print(analyzer.device_comparison().to_string(index=False))
analyzer.close()

把这个类放到你的工具箱里,以后任何 JSON 日志数据,导入路径就能直接分析。

八、与传统工具对比

特性DuckDBPandasSpark传统 Python 脚本
JSON 原生支持✅ read_json_auto❌ 需 json_normalize✅ 支持但笨重❌ 需手动解析
嵌套字段访问payload.page❌ 需展平⚠️ 复杂❌ 手动递归
流式处理✅ lazy evaluation❌ 全量加载✅ 分布式❌ 内存受限
大数据性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
学习成本低(SQL)中(Python)
部署复杂度零(单文件)高(集群)

九、变现建议

掌握 DuckDB 的 JSON 处理能力后,你可以将这些技能转化为实际收益:

  1. 数据咨询服务:为企业提供 JSON 日志分析服务,帮助电商、游戏公司理解用户行为。单次项目收费 5000-20000 元。

  2. 自动化报表产品:将上述 JSONAnalyzer 类封装为 SaaS 产品,用户上传 JSONL 文件即可自动生成可视化报表。月费制 99-499 元/用户。

  3. 培训课程:制作"DuckDB 实战 JSON 数据分析"系列课程,在 Udemy、极客时间等平台售卖。

  4. 开源插件开发:基于 read_json_auto() 开发特定行业的 JSON 解析扩展包,通过 GitHub Sponsors 或企业授权获得收入。

  5. 内部效率提升:在公司内部推广 DuckDB 替代传统 JSON 解析流程,节省的开发人力成本可直接量化为你的绩效亮点。

以前需要写解析脚本的工作,现在一行 SQL 搞定。 下次再看到 JSON 数据,先试试 read_json_auto()。你会发现,很多原本需要半天的工作,10 分钟就够了。


💡 小贴士:如果你的 JSON 数据在 S3 或 OSS 上,DuckDB 可以直接远程读取——read_json_auto('s3://bucket/data.jsonl'),不需要下载到本地。

📺 更多 DuckDB 实战教程,订阅 YouTube 频道 → youtube.com/@duckdblab

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计

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

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

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