title: 电商选品自动化:JinritemaiCrawler 抖音商品数据采集实践 slug: jinritemai-crawler-project date: 2026-06-23 tags: [Python, DrissionPage, 爬虫, 自动化, 电商选品] description: 深入解析基于 DrissionPage 的今日头条电商选品爬虫项目,涵盖浏览器自动化、网络请求拦截、数据提取与 Excel 导出的完整实践。
电商选品自动化:JinritemaiCrawler 抖音商品数据采集实践
一、项目背景
在抖音电商生态中,选品是决定带货效果的核心环节。达人需要从海量商品中筛选出高佣金、高转化率、销售数据优秀的商品。然而,抖音选品后台(今日头条电商平台)的数据维度丰富,手动逐条查看不仅效率低下,而且容易遗漏关键指标。
JinritemaiCrawler 是一个基于 DrissionPage 的开源选品爬虫项目,通过浏览器自动化模拟人工操作,登录抖音电商选品后台后批量采集商品详情、销售数据、转化率及带货内容链接,最终输出结构化的 Excel 报表。该项目托管于 GitHub(Lireal-w/JinritemaiCrawler),采用纯 Python 实现,核心依赖为 DrissionPage 和 xlsxwriter。
本文将从架构设计、浏览器自动化策略、数据提取模式、Excel 导出等维度,深度拆解该项目的技术实现。
二、项目架构解析
项目整体采用三层结构,职责清晰:
index.py → 程序入口、交互菜单、配置管理
Jinritemai.py → 爬虫核心:浏览器控制、数据抓取、解析逻辑
client_db.py → Excel 导出模块
2.1 入口层:index.py
index.py 是程序的启动入口,继承了 ConfigFile 类(定义在 config.py 中)。它并不直接执行爬虫逻辑,而是提供了一个交互式菜单系统,让用户在执行爬虫前完成环境准备:
class App(ConfigFile):
def static(self):
# 确保 config/ 和 Browser/ 目录存在
if not os.path.exists("config"): os.mkdir("config")
if not os.path.exists("Browser"): os.mkdir("Browser")
if not self.IsInited: self.InitEnv()
help_eon = ["读取浏览器列表", "创建新浏览器","运行浏览器",
"查看配置","修改配置","退出程序"]
# 循环展示菜单,等待用户选择
这种设计体现了渐进式初始化的思想:用户首次运行时会自动检测环境,如果 Chrome 不可用或浏览器数据目录未创建,程序会给出明确指引而非直接崩溃。
2.2 核心层:Jinritemai.py
这是整个项目的重头戏,约 300 行代码,封装了 Browser 基类和 Jinritemai 主类。爬虫策略的核心思路是:
- 复用本地浏览器用户数据目录(
Browser/xxx),跳过每次扫码登录 - 在选品库页面遍历商品卡片,通过佣金阈值前置过滤
- 点击卡片进入详情页,利用 DrissionPage 的 listen/wait 模式拦截 API 响应
- 解析 JSON 数据提取结构化字段
- 可选抓取"带货内容"子页面中的视频/图文链接
2.3 导出层:client_db.py
基于 xlsxwriter 的 Excel 生成模块,支持:
- 主 sheet:「商品列表」— 19 列商品指标
- 子 sheet:每个商品一个独立 sheet,记录其带货内容详情
- 自定义列宽、表头样式(蓝底白字)、行高、居中对齐
三、DrissionPage 浏览器自动化深度实践
3.1 为什么选择 DrissionPage?
传统的 Selenium 方案需要单独管理 ChromeDriver 版本,而 DrissionPage 封装了 Chromium 的 DevTools Protocol(CDP),可以直接控制本地已安装的 Chrome 浏览器,无需额外的驱动文件。项目中的环境检测函数清晰地体现了这一点:
def TestChromiumEnv():
browser = Chromium()
tab = browser.latest_tab
tab.get('https://www.bilibili.com/')
browser.quit()
如果本地 Chrome 不可用,程序会给出引导:
except Exception:
error("浏览器连接失败")
error("请先安装 Chrome 浏览器")
info("网址:https://support.google.com/chrome/answer/95346")
3.2 浏览器用户数据复用
这是项目中最精妙的设计之一。通过 set_user_data_path,每个浏览器实例关联一个独立的用户数据目录:
class Browser:
def __init__(self, path):
self.co = ChromiumOptions().auto_port(True).set_user_data_path(path)
self.co.set_argument('--start-maximized')
self.browser = Chromium(addr_or_opts=self.co)
这样做的好处:
- 一次登录,长期使用:在选品库的登录页面完成扫码后,Cookies 和会话状态保存在
Browser/user1/目录中,下次启动时无需重新登录 - 多账号隔离:同一台机器可以维护多个浏览器数据目录,对应不同的抖音账号
- 自动端口:
auto_port(True)避免端口冲突
CheckLogin 方法通过检测页面是否存在"选品"文本来判断登录状态:
def CheckLogin(self, page=None):
while True:
try:
page.ele('text:选品', timeout=3).click()
info("请选择类别后按回车")
input("> ")
break
except ElementNotFoundError:
info("未登录,请登录后按回车")
input("> ")
这是一个用户参与式的登录检测:程序自动判断是否需要登录,需要时暂停等待用户手动扫码,登录后继续执行。这种方式比纯自动化的验证码处理更加稳定可靠。
3.3 无驱动自动化原理
DrissionPage 的底层机制值得关注——它通过 WebSocket 连接 Chrome DevTools Protocol,直接向浏览器发送指令。这意味着:
- 无需 Selenium WebDriver、无需 ChromeDriver 二进制文件
- 操作速度快于传统的 WebDriver 方案(因为省去了 HTTP 协议转换的开销)
- 可以监听网络请求、拦截响应,实现"API 级"的数据采集
四、数据采集的两大核心模式
4.1 模式一:佣金前置过滤(DOM 解析)
在进入商品详情页之前,程序会在选品库列表页先做一轮快速过滤。inspect_cosFee 函数直接从 DOM 中提取佣金数值:
def inspect_cosFee(wrapper, _cond) -> bool | None:
try:
cosFee_str = wrapper.s_ele(".index_module__cosFee___dadac").text
cosFee = float(cosFee_str[2:]) # 去掉 "¥" 前缀
return cosFee < _cond.CommissionMin
except ElementNotFoundError:
return None # 异常时返回 None,触发刷新重试
返回值有三种语义:
| 返回值 | 含义 | 处理逻辑 |
|---|---|---|
True |
佣金低于阈值 | 跳过,继续下一个 |
False |
佣金达标 | 进入详情 |
None |
元素未找到(可能页面加载异常) | 滚动刷新后重试 |
这种 "先过滤、后详情" 的策略极大地减少了不必要的页面跳转,提高了抓取效率。
4.2 模式二:API 响应拦截(listen/wait)
进入商品详情页后,代码并不解析 DOM,而是利用了 DrissionPage 最强大的特性——网络请求监听。这是整个爬虫的数据采集核心:
def Commodity_details(self, tab, condi):
c_id = re.findall(r"commodity_id=(\d+)", tab.url)[0]
tab.listen.start(JinritemaiAPI) # 开始监听 API
tab.refresh() # 刷新页面触发请求
res = tab.listen.wait(2) # 等待响应,超时 2 秒
goods = extractGoods(res, c_id, condi) # 解析 JSON 数据
监听的 API 端点为 buyin.jinritemai.com/pc/selection/decision/pack_detail,这是一个返回商品完整数据的 REST 接口。通过拦截其 JSON 响应,爬虫获取到的数据结构远丰富于 DOM 中展示的信息。
extractGoods 函数将 JSON 数据映射到 19 个字段的结构化列表中:
item = [c_id] + [""] * 18 # 商品 ID + 18 个数据字段
# 商品基础信息
item[1] = d_res["product"]["product_base"]["title"] # 商品名称
item[8] = d_res["product"]["product_comment"]["good_ratio"] # 好评率
# 推广数据
item[2] = calculate_data["sales_amount"] # 总销售额
item[3] = calculate_data["sales_amount"] # 总销量
item[6] = calculate_data["order_conversion_rate"] # 下单转化率
# 渠道数据 - 达人推广销售额占比
for stat in stat_list:
if stat.get("title", "") == "销售渠道":
for s_data in stat["stat_data"]["sales"]["stat_list"]:
if s_data.get("key", "") == "达人推广":
item[10] = s_data["value"]
其中还包含了两个重要的过滤规则:
- 达人推广占比 ≥ 50%:确保商品主要通过达人带货而非品牌自播
- 视频销售额 ≥ 直播销售额:偏向视频带货场景,符合选品预期
- 视频转化率 ≥ 设定阈值:对应配置项
VideoConversionRateMin
如果某个条件不满足,函数返回空列表,该商品被跳过。
4.3 带货内容链接提取
当用户选择"商品详情与视频"模式时,程序会进一步点击"带货内容"标签,加载商品的带货视频/图文列表:
tab.ele('text:带货内容').click()
details_res = tab.listen.wait()
self.Detailssheet[c_id] = extractLink(details_res.response)
extractLink 函数提取每条内容的标题、抖音视频链接、销量、销售额、转化率、播放量和点赞数:
def extractLink(res):
data = res.body["data"]["model"]
content_list = data["sale_content_data"].get("content_list", [])
for i in content_list:
_item = [None for _ in range(7)]
_item[0] = i["content_title"]
_item[1] = "https://www.douyin.com/video/" + i["content_id"]
_item[2] = i["sales"]
_item[3] = i["sales_amount"]
_item[4] = i["order_conversion_rate"]
_item[5] = i["play_count"]
_item[6] = i["like_count"]
sheetData.append(_item)
这些数据最终会被写入独立的 Excel 子 sheet,形成"一个商品一个详情页"的报表结构。
五、选品条件筛选体系
项目支持多维度的选品过滤条件,通过 condition.json 配置:
{
"CommissionMin": 2,
"Target": 100,
"Interval": 1,
"Pause": 1,
"VideoConversionRateMin": 20,
"ProhibitedWords": []
}
筛选流程
商品列表页
│
├─ 佣金 ≥ CommissionMin? → 否 → 跳过
│
├─ 商品标题含禁止关键词? → 是 → 跳过
│
├─ 达人推广占比 ≥ 50%? → 否 → 跳过
│
├─ 视频销售额 ≥ 直播销售额? → 否 → 跳过
│
├─ 视频转化率 ≥ VideoConversionRateMin? → 否 → 跳过
│
└─ 全部通过 → 加入选品车、记录数据
这种多层级的流水线过滤值得借鉴——尽早用低成本操作排除不符合条件的商品(DOM 解析比页面跳转代价小得多),需要详细数据的操作放在流水线末端。
速率控制
通过 Interval 和 Pause 两个参数控制抓取节奏:
if length % condo.Interval == 0:
time.sleep(condo.Pause)
每抓取 N 个商品后暂停 M 秒,避免被平台的反爬机制拦截。
六、Excel 报表导出实现
导出模块 client_db.py 采用 xlsxwriter 库实现,支持丰富的格式自定义。
6.1 表头与列宽
主商品列表包含 19 列,分别对应商品标识、名称、销售额、销量、转化率等字段。列宽通过数组预先定义:
self.head_column_widths = [23, 40] + [15]*8 + [27] + [15]*8
- 商品名称列特别宽(40),适应较长标题
- "达人推广销售额占比"列最宽(27),因表头文字较长
- 其余列统一为 15
6.2 样式系统
项目设计了一个可外部配置的样式系统。默认样式硬编码在 XlsxFormat 类中,同时支持通过 config/xlsx_format.json 覆盖:
self.default_format = {
'align': 'center', # 水平居中
'valign': 'vcenter', # 垂直居中
'font': '宋体', # 字体
}
self.head = [
["A1", [/* 19 个列名 */],
{'bg_color': '#1E88E5', 'font_color': 'white'}]
]
表头采用蓝底白字(#1E88E5),这是 Google Material Design 的蓝色系,视觉上专业清晰。
6.3 多 Sheet 结构
主商品列表位于"商品列表"sheet 中,而每个商品的带货内容则创建独立的子 sheet:
def addDetails(self, name, data):
Detailssheet = self.workbook.add_worksheet(name)
# 写入表头
for _i, row, style in self.format.details:
style_s = self.workbook.add_format({**style, **d_style})
Detailssheet.write_row(_i, row, style_s)
# 写入数据行
for _d in data:
Detailssheet.write_row(i + str(cur + 1), _d, self.default_format)
cur += 1
子 sheet 以商品 ID 命名,包含 7 列:标题、链接、销量、销售额、转化率、播放量、点赞数。这种 "主表 + 子表" 的报表结构,既保证了总览的简洁性,又保留了每个商品的详细数据。
七、配置系统设计
项目的配置系统采用了类继承 + JSON 持久化的模式:
JsonFile → ConfigFile(浏览器配置)
→ Condition(抓取条件)
→ XlsxFormat(Excel 格式)
JsonFile 是核心基类,它利用 __dict__ 实现了对象属性和 JSON 文件的双向同步:
class JsonFile:
def __init__(self, path):
self.__dict__.update(read_json(path)) # JSON → 对象属性
self.path = path
def __del__(self):
write_json(self.path, self.__dict__) # 对象属性 → JSON
这种设计的好处是:
- 零模板代码:无需为每个配置类编写序列化/反序列化逻辑
- 自动持久化:对象析构时自动保存,修改配置后无需手动调用保存方法
- 透明访问:通过
c.CommissionMin直接读取配置,代码语义清晰
拦截词的管理也通过这个系统实现,支持运行时动态添加和删除:
if key == "ProhibitedWords":
# 用户选择添加或删除
inst = Input_inst(["添加", "删除"])
if inst == 0:
print("请输入要添加的值")
c.ProhibitedWords.append(input("> "))
八、打包与部署
项目提供了 PyInstaller 打包配置(index.spec、delself.spec),可以将整个爬虫打包为单文件可执行程序。特别地,delself.py 实现了自删除功能——这在一些需要清理运行痕迹的场景下非常实用。
打包命令:
pyinstaller --onefile index.py
九、项目亮点总结
回顾 JinritemaiCrawler 的整体设计,有几个值得学习的工程实践:
DrissionPage 的 listen/wait 模式:避免了繁琐的 DOM 解析和 XHR 断点调试,直接通过 CDP 拦截网络响应获取结构化 JSON 数据,采集效率和数据质量都远高于纯 DOM 方案。
浏览器数据目录复用:将用户数据与代码逻辑分离,实现了"一次登录、多次使用",大幅降低了重复认证的摩擦。
分层过滤策略:在列表页做低成本的佣金过滤,在详情页做高成本的完整数据校验,资源利用效率最大化。
自动持久化的配置系统:利用 Python 的
__dict__和 JSON 序列化,用不到 30 行代码实现了完整的配置读写功能,思路值得借鉴。多 Sheet 报表:主子表结构兼顾了总览与细节,xlsxwriter 的格式自定义能力让输出报表具备专业质感。
十、实践思考与改进方向
在实际使用中,有几点值得注意:
- 页面结构变化风险:项目依赖于特定 CSS 类名(如
.index_module__cosFee___dadac)和 API 端点,抖音电商后台改版后需要相应更新选择器。 - 反爬策略升级:目前项目通过用户数据目录复用 + 请求间隔控制来应对反爬,未来可能需要引入代理 IP、请求头随机化等更完善的措施。
- 数据维度扩展:当前主要采集销售和转化数据,未来可以加入评价内容分析、价格变动追踪、竞品对比等维度。
结语
JinritemaiCrawler 是一个典型但不过时的浏览器自动化采集项目。它的价值不仅在于解决了抖音电商选品的实际问题,更在于展示了一种高效的数据采集范式:通过 CDP 协议拦截 API 响应,而非逐页解析 DOM。这种思路适用于绝大多数 SPA(单页应用)网站的数据采集场景。
如果你对 DrissionPage 的 listen/wait 模式感兴趣,或者正在寻找一个真实可用的电商数据采集项目,JinritemaiCrawler 的源码值得一读。
项目地址: https://github.com/Lireal-w/JinritemaiCrawler 技术栈: Python · DrissionPage · xlsxwriter · PyInstaller 许可证: MIT
评论