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 主类。爬虫策略的核心思路是:

  1. 复用本地浏览器用户数据目录(Browser/xxx),跳过每次扫码登录
  2. 在选品库页面遍历商品卡片,通过佣金阈值前置过滤
  3. 点击卡片进入详情页,利用 DrissionPage 的 listen/wait 模式拦截 API 响应
  4. 解析 JSON 数据提取结构化字段
  5. 可选抓取"带货内容"子页面中的视频/图文链接

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"]

其中还包含了两个重要的过滤规则

  1. 达人推广占比 ≥ 50%:确保商品主要通过达人带货而非品牌自播
  2. 视频销售额 ≥ 直播销售额:偏向视频带货场景,符合选品预期
  3. 视频转化率 ≥ 设定阈值:对应配置项 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 解析比页面跳转代价小得多),需要详细数据的操作放在流水线末端。

速率控制

通过 IntervalPause 两个参数控制抓取节奏:

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.specdelself.spec),可以将整个爬虫打包为单文件可执行程序。特别地,delself.py 实现了自删除功能——这在一些需要清理运行痕迹的场景下非常实用。

打包命令:

pyinstaller --onefile index.py

九、项目亮点总结

回顾 JinritemaiCrawler 的整体设计,有几个值得学习的工程实践:

  1. DrissionPage 的 listen/wait 模式:避免了繁琐的 DOM 解析和 XHR 断点调试,直接通过 CDP 拦截网络响应获取结构化 JSON 数据,采集效率和数据质量都远高于纯 DOM 方案。

  2. 浏览器数据目录复用:将用户数据与代码逻辑分离,实现了"一次登录、多次使用",大幅降低了重复认证的摩擦。

  3. 分层过滤策略:在列表页做低成本的佣金过滤,在详情页做高成本的完整数据校验,资源利用效率最大化。

  4. 自动持久化的配置系统:利用 Python 的 __dict__ 和 JSON 序列化,用不到 30 行代码实现了完整的配置读写功能,思路值得借鉴。

  5. 多 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