电商ETL实战:如何用Python处理JSON、MySQL和日志数据(附完整代码)
最近在帮一个朋友优化他们电商团队的数据处理流程,发现很多工程师虽然会用Python写脚本,但一到实际的生产环境,面对多种数据源、增量同步、错误处理这些实际问题就有点手忙脚乱。尤其是电商场景,订单、商品、用户行为日志这些数据来源五花八门,格式各异,处理起来确实需要一套清晰的思路和可靠的代码结构。
这篇文章我就结合自己最近做的一个中型电商数据平台的项目经验,从头到尾拆解一下如何用Python构建一个稳健、可维护的电商ETL管道。我们不仅会处理JSON格式的订单数据、MySQL里的商品信息,还会搞定那些看似杂乱无章的服务器日志文件。更重要的是,我会分享代码组织、错误处理、增量采集这些实战中真正重要的细节,并提供可以直接在项目中复用的模块化代码。
如果你已经熟悉Python基础,但想系统学习如何将Python用于真实的数据集成任务,那么这篇文章正是为你准备的。我们避开那些华而不实的理论,直接进入操作环节。
1. 项目蓝图:理解电商ETL的核心挑战与设计思路
在动手写代码之前,我们得先想清楚要解决什么问题。电商数据管道通常面临几个典型挑战:
- 数据源异构性:订单可能来自前端API生成的JSON文件,商品信息躺在业务数据库的MySQL表里,而用户点击、搜索行为则记录在Nginx或应用服务器的日志文件中。每种数据源的读取方式、解析逻辑都不同。
- 数据量与时效性:订单数据可能每分钟都在产生,商品表每天更新几次,日志文件更是源源不断。全量抽取在数据量变大后根本不现实,必须设计增量采集机制。
- 数据质量与一致性:JSON里可能有字段缺失,日志行格式可能不规范,MySQL连接可能意外断开。管道必须有足够的韧性(Resilience)来处理这些异常,保证最终输出数据的质量。
- 可维护性与可观测性:ETL脚本不是一次性的。随着业务变化,数据源、转换规则都可能调整。代码需要易于修改、扩展,并且运行时状态要清晰可见(比如哪些文件处理了,处理到哪一步了)。
针对这些挑战,我设计了一个基于 “元数据管理” 和 “模块化处理” 的架构。简单来说,就是用一个单独的“小本本”(通常是数据库里的一张表)记录每次数据采集的状态(比如最后一个处理的文件名、最后一条记录的时间戳)。下次运行时,先查“小本本”,就知道该从哪儿继续,从而实现增量同步。同时,将不同数据源的处理逻辑封装成独立的模块,通过统一的配置和日志接口进行管理。
整个管道的流程可以概括为下图所示的逻辑:
[数据源] -> [抽取Extract] -> [转换Transform] -> [加载Load] -> [数据目的地]
| | | | |
订单JSON 读取新文件 清洗、格式化 写入MySQL数仓 -> 分析报表
商品MySQL 查询增量记录 关联、打宽表 写入CSV备份 -> 数据恢复
访问日志 对比文件列表 解析、提取字段 记录处理状态 -> 用户行为分析
提示:在实际项目中,数据目的地(Data Destination)通常会是专业的数据仓库(如Snowflake、BigQuery)或数据湖。但为了演示和本地开发的便利性,本文使用MySQL模拟数仓,其原理是相通的。
2. 环境搭建与项目骨架:打造一个专业的工程结构
我不喜欢把一堆脚本扔在同一个文件夹里。一个好的项目结构是后期维护性的第一道保障。下面是我推荐的目录布局,你可以直接在命令行中创建:
mkdir -p ecommerce_etl/{config,models,utils,core/{extractors,transformers,loaders},logs,tests}
touch ecommerce_etl/{main.py,requirements.txt,README.md}
touch ecommerce_etl/config/__init__.py ecommerce_etl/config/settings.py
touch ecommerce_etl/core/__init__.py
关键目录说明:
config/: 存放所有配置,如数据库连接字符串、文件路径、日志级别。绝对不要把密码等敏感信息硬编码在代码里。models/: 定义数据模型或实体类(例如Order, Product),虽然Python动态类型,但用类或dataclass定义结构能让代码更清晰。utils/: 放置通用的工具函数,比如文件操作、日期处理、字符串清洗等。core/: 核心ETL逻辑。再按extractors(抽取器)、transformers(转换器)、loaders(加载器)细分,符合ETL的概念模型。logs/: 存放程序运行的日志文件。tests/: 单元测试文件。
接下来,初始化你的Python虚拟环境并安装核心依赖:
cd ecommerce_etl
python -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
将以下内容写入requirements.txt:
pymysql>=1.0.0
pandas>=1.5.0 # 用于复杂转换,非必须但推荐
python-dotenv>=0.19.0 # 管理环境变量
schedule>=1.0.0 # 如需定时任务
然后安装:
pip install -r requirements.txt
现在,我们来编写核心的配置文件config/settings.py。这里我使用.env文件加载敏感信息,这是一个好习惯。
首先,在项目根目录创建.env文件(并记得将其加入.gitignore):
# .env
DB_HOST=localhost
DB_PORT=3306
DB_USER=etl_user
DB_PASSWORD=your_secure_password_here
DB_NAME=ecommerce_dw
SOURCE_DB_NAME=ecommerce_source
LOG_LEVEL=INFO
LOG_FILE_PATH=./logs/etl_pipeline.log
ORDERS_JSON_PATH=./data_source/orders
PRODUCTS_LAST_ID_FILE=./state/last_product_id.txt
然后,在settings.py中读取:
# config/settings.py
import os
from pathlib import Path
from dotenv import load_dotenv
# 加载.env文件
load_dotenv()
BASE_DIR = Path(__file__).resolve().parent.parent
# 数据库配置
DB_CONFIG = {
'host': os.getenv('DB_HOST', 'localhost'),
'port': int(os.getenv('DB_PORT', 3306)),
'user': os.getenv('DB_USER'),
'password': os.getenv('DB_PASSWORD'),
'database': os.getenv('DB_NAME', 'ecommerce_dw'),
'charset': 'utf8mb4'
}
SOURCE_DB_CONFIG = {
'host': os.getenv('DB_HOST', 'localhost'),
'port': int(os.getenv('DB_PORT', 3306)),
'user': os.getenv('DB_USER'),
'password': os.getenv('DB_PASSWORD'),
'database': os.getenv('SOURCE_DB_NAME', 'ecommerce_source'),
'charset': 'utf8mb4'
}
# 路径配置
ORDERS_JSON_DIR = BASE_DIR / os.getenv('ORDERS_JSON_PATH', './data_source/orders')
LOG_FILE_PATH = BASE_DIR / os.getenv('LOG_FILE_PATH', './logs/etl.log')
STATE_FILE_DIR = BASE_DIR / './state'
# 确保目录存在
ORDERS_JSON_DIR.mkdir(parents=True, exist_ok=True)
LOG_FILE_PATH.parent.mkdir(parents=True, exist_ok=True)
STATE_FILE_DIR.mkdir(parents=True, exist_ok=True)
# 日志配置
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
3. 核心模块深度剖析:从抽取、转换到加载的完整实现
有了稳固的基础设施,我们现在可以深入每个ETL环节。我会先讲最复杂的部分——增量状态管理,因为它是串联起所有数据源处理逻辑的钥匙。
3.1 状态管理:ETL管道的“记忆中枢”
增量同步的核心是记住“上次做到哪里了”。对于不同数据源,记忆的方式不同:
| 数据源类型 | 状态记录内容 | 存储方式 | 示例 |
|---|---|---|---|

743

被折叠的 条评论
为什么被折叠?



