首页
/ MediaCrawler 项目代码结构详解:从目录布局到核心调用链的源码导读

MediaCrawler 项目代码结构详解:从目录布局到核心调用链的源码导读

2026-09-04 14:32:26作者:苗圣禹Peter

MediaCrawler 是一个支持小红书、抖音、快手、B 站、微博、百度贴吧、知乎七大平台笔记/视频/评论采集的开源爬虫项目。本文以官方文档 项目代码结构 中给出的完整目录树为主体骨架,逐目录解析每个模块的职责边界与关键实现文件,并结合 main.py 入口、抽象基类、配置体系与工厂模式等源码证据,帮助你快速建立"一个请求进来、一条数据落库"在整个代码库中的完整心智模型。

一、官方目录结构总览

先完整给出官方文档中的目录树,作为全文的导航索引:

MediaCrawler
├── base
│   └── base_crawler.py         # 项目的抽象基类
├── cache
│   ├── abs_cache.py            # 缓存抽象基类
│   ├── cache_factory.py        # 缓存工厂
│   ├── local_cache.py          # 本地缓存实现
│   └── redis_cache.py          # Redis缓存实现
├── cmd_arg
│   └── arg.py                  # 命令行参数定义
├── config
│   ├── base_config.py          # 基础配置
│   ├── db_config.py            # 数据库配置
│   └── ...                     # 各平台配置文件
├── constant
│   └── ...                     # 各平台常量定义
├── database
│   ├── db.py                   # 数据库ORM,封装增删改查
│   ├── db_session.py           # 数据库会话管理
│   └── models.py               # 数据库模型定义
├── docs
│   └── ...                     # 项目文档
├── libs
│   ├── douyin.js               # 抖音Sign函数
│   ├── stealth.min.js          # 去除浏览器自动化特征的JS
│   └── zhihu.js                # 知乎Sign函数
├── media_platform
│   ├── bilibili                # B站采集实现
│   ├── douyin                  # 抖音采集实现
│   ├── kuaishou                # 快手采集实现
│   ├── tieba                   # 百度贴吧采集实现
│   ├── weibo                   # 微博采集实现
│   ├── xhs                     # 小红书采集实现
│   └── zhihu                   # 知乎采集实现
├── model
│   ├── m_baidu_tieba.py        # 百度贴吧数据模型
│   ├── m_douyin.py             # 抖音数据模型
│   ├── m_kuaishou.py           # 快手数据模型
│   ├── m_weibo.py              # 微博数据模型
│   ├── m_xiaohongshu.py        # 小红书数据模型
│   └── m_zhihu.py              # 知乎数据模型
├── proxy
│   ├── base_proxy.py           # 代理基类
│   ├── providers               # 代理提供商实现
│   ├── proxy_ip_pool.py        # 代理IP池
│   └── types.py                # 代理类型定义
├── store
│   ├── bilibili                # B站数据存储实现
│   ├── douyin                  # 抖音数据存储实现
│   ├── kuaishou                # 快手数据存储实现
│   ├── tieba                   # 贴吧数据存储实现
│   ├── weibo                   # 微博数据存储实现
│   ├── xhs                     # 小红书数据存储实现
│   └── zhihu                   # 知乎数据存储实现
├── test
│   ├── test_db_sync.py         # 数据库同步测试
│   ├── test_proxy_ip_pool.py   # 代理IP池测试
│   └── ...                     # 其他测试用例
├── tools
│   ├── browser_launcher.py     # 浏览器启动器
│   ├── cdp_browser.py          # CDP浏览器控制
│   ├── crawler_util.py         # 爬虫工具函数
│   ├── utils.py                # 通用工具函数
│   └── ...
├── main.py                     # 程序入口, 支持 --init_db 参数来初始化数据库
├── recv_sms.py                 # 短信转发HTTP SERVER接口
└── var.py                      # 全局上下文变量定义

从源码结构看,整个项目遵循一条清晰的依赖主线:main.py(入口)→ cmd_arg(参数解析)→ config(配置加载)→ media_platform(平台采集)→ store(数据落盘)→ database(ORM 持久化)basecacheproxytools 作为横切能力被各层复用。下面逐层展开。

二、程序入口 main.py:工厂模式与启动流程

main.py 是整个爬虫的启动入口,文档中标注它"支持 --init_db 参数来初始化数据库"。源码中可以确认其完整职责:

  • 平台爬虫工厂:文件内定义了 CrawlerFactory 类,用一张字典将平台短名映射到具体爬虫类:
class CrawlerFactory:
    CRAWLERS: dict[str, Type[AbstractCrawler]] = {
        "xhs": XiaoHongShuCrawler,
        "dy": DouYinCrawler,
        "ks": KuaishouCrawler,
        "bili": BilibiliCrawler,
        "wb": WeiboCrawler,
        "tieba": TieBaCrawler,
        "zhihu": ZhihuCrawler,
    }

    @staticmethod
    def create_crawler(platform: str) -> AbstractCrawler:
        crawler_class = CrawlerFactory.CRAWLERS.get(platform)
        if not crawler_class:
            supported = ", ".join(sorted(CrawlerFactory.CRAWLERS))
            raise ValueError(f"Invalid media platform: {platform!r}. Supported: {supported}")
        return crawler_class()

这是典型的工厂模式:main() 中先 cmd_arg.parse_cmd() 解析命令行,再根据 config.PLATFORM 取值从工厂中取出对应平台的爬虫实例并 await crawler.start()。新增平台时只需在 media_platform 下实现新模块并在此字典中注册一行。

  • 数据库初始化--init_db 参数触发 db.init_db(args.init_db);此外当 config.SAVE_DATA_OPTIONsqlite/mysql/db/postgres 时,运行前会自动调 db.init_db() 建表,避免首次运行出现 no such table 错误(main.py)。
  • 收尾钩子main() 返回后会按需执行 Excel 批量落盘(_flush_excel_if_needed)、生成评论词云(_generate_wordcloud_if_needed,仅 json/jsonl 存储模式),以及 async_cleanup() 关闭 CDP 浏览器或 Playwright 浏览器上下文并关闭数据库连接(main.py)。

启动命令示例(以小红书关键词搜索为例):

python main.py --platform xhs --lt qrcode --ct search

--platform 的取值即上表中的短名:xhs | dy | ks | bili | wb | tieba | zhihu

三、base 目录:面向全部平台的抽象基类

base/base_crawler.py 定义了所有平台实现必须遵循的契约,是理解各平台模块内部结构的钥匙:

抽象基类 核心抽象方法 说明
AbstractCrawler startsearchlaunch_browser 爬虫主流程契约;另提供可选的 launch_browser_with_cdp,默认实现会回退到标准 launch_browser
AbstractLogin login_by_qrcodelogin_by_mobilelogin_by_cookiesbegin 三种登录方式(二维码/手机验证码/Cookie)的统一契约
AbstractStore store_contentstore_commentstore_creator 内容、评论、创作者信息的存储契约
AbstractStoreImage / AbstractStoreVideo store_imagestore_video 媒体资源存储契约(源码注释标注目前仅部分平台实现,因此未强制为抽象方法)
AbstractApiClient requestupdate_cookies 平台 API 客户端契约,负责发起请求与刷新浏览器 Cookie

每个 media_platform/<平台>/ 目录内部都会实现这套契约:core.py 是爬虫主流程、login.py 是登录逻辑、client.py 是 API 客户端,与上表一一对应。

四、cmd_arg 目录:Typer 命令行参数体系

cmd_arg/arg.py 基于 typer 库定义了全部命令行参数,其内部的枚举类也精确界定了项目的取值空间:

  • PlatformEnumxhsdyksbiliwbtiebazhihu
  • LoginTypeEnumqrcodephonecookie
  • CrawlerTypeEnumsearch(关键词搜索)、detail(指定内容详情)、creator(创作者主页数据);
  • SaveDataOptionEnumcsvdbjsonjsonlsqlitemongodbexcelpostgres
  • InitDbOptionEnumsqlitemysqlpostgres,即 python main.py --init_db sqlite 中可传入的合法值。

一个工程细节值得注意:cmd_arg/arg.py 中的 _coerce_enum 会对配置值做安全枚举转换,当配置值不在支持范围内时打印警告并回退到默认值,而不是直接抛出异常——这让配置文件的容错能力更强。

五、config 目录:分层配置与平台特化

config 目录采用"基础配置 + 平台特化配置"的分层结构:

  • config/base_config.py 定义全局开关,文末通过 from .bilibili_config import * 等方式把各平台配置合并进统一命名空间。关键参数摘录如下:
配置项 默认值 作用
PLATFORM "xhs" 目标平台,七选一
LOGIN_TYPE "qrcode" 登录方式:qrcode/phone/cookie
CRAWLER_TYPE "search" 采集模式:search/detail/creator
HEADLESS False 是否无头浏览器运行
ENABLE_CDP_MODE True 是否启用 CDP 模式(复用本地 Chrome/Edge,反检测能力更好)
CDP_CONNECT_EXISTING True 是否连接用户已开启远程调试的浏览器
SAVE_DATA_OPTION "jsonl" 数据存储方式
CRAWLER_MAX_NOTES_COUNT 15 控制采集的视频/帖子数量
ENABLE_GET_COMMENTS True 是否采集评论
ENABLE_GET_SUB_COMMENTS False 是否采集二级评论
ENABLE_IP_PROXY False 是否启用代理 IP
IP_PROXY_PROVIDER_NAME "kuaidaili" 代理提供商:kuaidaili/wandouhttp/static
CRAWLER_MAX_SLEEP_SEC 2 请求间隔(秒)
ENABLE_GET_WORDCLOUD False 是否生成评论词云
  • config/db_config.py 定义数据库与缓存连接信息,全部支持环境变量覆盖:MySQL 的 MYSQL_DB_PWD/MYSQL_DB_HOST 等、Redis 的 REDIS_DB_HOST/REDIS_DB_PWD 等、MongoDB 的 MONGODB_* 系列、PostgreSQL 的 POSTGRES_DB_* 系列,以及 SQLite 的默认库文件路径(指向 database/sqlite_tables.db)。
  • 每个平台还有专属配置文件(如 config/xhs_config.pyconfig/dy_config.pyconfig/tieba_config.py 等),承载该平台特有的行为开关。

六、media_platform 目录:七平台采集实现

media_platform 是业务逻辑的核心,七个平台目录结构高度一致(以小红书为例):

  • core.py:采集主流程,实现 AbstractCrawlerstart/search 等方法;
  • login.py:实现 AbstractLogin 三种登录方式,其中抖音、小红书的登录链路会调用 libs/ 下的签名脚本;
  • client.py:实现 AbstractApiClient,封装各平台内部 API;
  • field.py:字段/常量定义;
  • help.py:平台私有辅助函数;
  • 部分平台另有 exception.pyextractor.py(小红书)、playwright_sign.pyxhs_sign.py(小红书)、graphql.pygraphql/ 下的查询文件(快手使用 GraphQL 接口,如 comment_list.graphql)。

其中 media_platform/xhs/extractor.py 体现了"原始响应 → 结构化数据"的转换职责,是理解数据流向的关键文件之一。

七、libs 目录:浏览器签名与反检测脚本

文档标注的三个 JS 文件对应三个明确用途:

  • libs/douyin.js:抖音 API 的 Sign 签名函数;
  • libs/zhihu.js:知乎 API 的 Sign 签名函数;
  • libs/stealth.min.js:在浏览器中注入,用于掩盖 Playwright 自动化特征,降低被平台风控识别的概率。

这类脚本通常通过 browser_context.add_init_script() 在页面加载前注入,属于 Playwright 反检测的标准做法;从源码结构看,各平台 login.py/core.py 中的签名调用与其配套使用。

八、database 与 model 目录:ORM 持久化层

database 目录承担全部数据库交互:

值得特别注意的是当前仓库 models.py 头部的教学版说明:该版本 ORM 不再持久化可识别用户的字段(用户 ID、头像、主页链接、签名、性别等一律不落库),原始用户 ID 在提取层经 tools/user_hash.pyanonymize_user_id 转为匿名 creator_hash 后写入,昵称经脱敏处理,创作者个人档案表也已整体移除。这是使用当前代码库做数据存储时必须了解的合规设计。

model 目录则是各平台"业务数据模型":m_xiaohongshu.pym_douyin.pym_kuaishou.pym_baidu_tieba.pym_weibo.pym_zhihu.py,用于描述笔记/视频/评论等实体的字段组织,与 database/models.py 中的 ORM 表模型分工不同——前者偏业务结构,后者偏持久化结构。

九、cache 目录:抽象缓存与工厂选择

cache 目录实现了"本地内存 + Redis"双后端缓存:cache/abs_cache.py 定义抽象基类,cache/local_cache.pycache/redis_cache.py 分别是本地与 Redis 实现,cache/cache_factory.py 负责按配置选择后端。后端类型常量定义在 config/db_config.pyCACHE_TYPE_REDIS = "redis"CACHE_TYPE_MEMORY = "memory"。测试目录 test/test_redis_cache.pytest/test_expiring_local_cache.py 分别验证两个后端的读写与过期行为。从源码结构看,缓存主要服务于代理 IP 池、登录状态等需要跨请求共享的数据。

十、proxy 目录:代理 IP 池的三层结构

proxy 目录采用"基类 + 提供商 + 池"的三层设计:

  1. proxy/base_proxy.py:定义 ProxyProvider 抽象基类(IP 获取契约)与 IpGetError 异常;
  2. proxy/providers/ 目录:具体提供商实现,kuaidl_proxy.py(快代理)、jishu_http_proxy.py(极数 HTTP)、wandou_http_proxy.py(豌豆 HTTP),通过工厂函数 new_kuai_daili_proxynew_wandou_http_proxy 暴露给上层;
  3. proxy/proxy_ip_pool.py:IP 池主体,按 config.IP_PROXY_POOL_COUNT 维护多个可用 IP,使用 tenacity@retry 做失败重试,并通过 httpx 校验代理可用性;proxy/types.py 定义 IpInfoModel 等类型。

启用方式对应 config/base_config.py:将 ENABLE_IP_PROXY 设为 TrueIP_PROXY_PROVIDER_NAMEkuaidaili/wandouhttp/static;取 static 时在 STATIC_PROXY_URL 中填写固定代理地址。配套文档见 代理使用,功能测试见 test/test_proxy_ip_pool.py

十一、store 目录:按平台分发的存储实现

store 目录与 media_platform 一一对应,每个平台子目录中的 _store_impl.py 实现 AbstractStore 契约。以 store/xhs/_store_impl.py 为例,其导入了 sqlalchemy.ext.asyncio.AsyncSessionSession,说明存储层同时支持异步会话(SQLite/关系库)路径;store/excel_store_base.py 提供 Excel 批量落盘基类,对应 main.py 中运行结束后统一 flush_all() 的收尾逻辑。

存储后端的实际走向由 config.SAVE_DATA_OPTION 决定:jsonl/json 走文件写出(tools/async_file_writer.py),csv/sqlite/mysql/postgresdatabase 层 ORM 落库,excelExcelStoreBase。各平台子目录中还可能有媒体专用存储文件,如 store/xhs/xhs_store_media.pystore/douyin/douyin_store_media.py,用于 ENABLE_GET_MEIDAS = True 时的图片/视频资源保存。

十二、tools 目录:跨平台的公共工具集

tools 是各层共用的工具函数仓库,文档中点名的几个文件对应明确职责:

十三、其余顶层文件:constant、recv_sms.py 与 var.py

  • constant 目录:各平台常量定义,当前仓库中为 constant/baidu_tieba.pyconstant/zhihu.py(贴吧/知乎的接口地址、URL 模式等常量);
  • recv_sms.py:短信验证码转发的 HTTP SERVER 接口,配合手机验证码登录方式(LOGIN_TYPE = "phone")使用,详见 手机号登录说明
  • var.py:基于 ContextVar 定义的全局上下文变量,如 crawler_type_var(当前采集模式)、request_keyword_var(当前请求关键词)、comment_tasks_var(评论采集任务列表)、db_conn_pool_var(数据库连接池)等。使用 ContextVar 而非普通全局变量,是为了在 asyncio 并发任务间安全地隔离每路请求的状态。

十四、test 与 tests:两层测试目录

仓库中存在两个测试目录,可按主题定位:

十五、沿着结构走一遍:一次采集请求的完整链路

综合以上各目录,一次典型采集运行(python main.py --platform xhs --ct search)的代码流经如下:

  1. main.pymain() 调用 cmd_arg/arg.py 解析命令行,覆盖 config 中由 config/base_config.py 与各平台配置合并出的参数;
  2. CrawlerFactory.create_crawler("xhs") 返回 media_platform/xhs 中的 XiaoHongShuCrawler,其 start() 内先登录(login.py,签名借助 libs/ 下脚本),再启动浏览器(base 抽象的 launch_browser 或 CDP 模式,由 tools/cdp_browser.py 支撑,代理 IP 由 proxy 池提供);
  3. 搜索/详情/创作者三种模式(CrawlerTypeEnum)由 core.py 分发,client.py 发起平台 API 请求,extractor.py/field.py 把原始响应解析为结构化数据;
  4. 数据经 store/xhs/_store_impl.pySAVE_DATA_OPTION 落盘:文件类走 tools/async_file_writer.py,数据库类走 database ORM(用户标识先经 tools/user_hash.py 匿名化,见 database/models.py 教学版说明);
  5. 运行结束,main.py 执行 Excel 落盘、词云生成、浏览器与数据库连接的清理。

从源码结构看,这套"工厂 + 抽象基类 + 平台目录同构"的布局,使得阅读任意一个平台的实现即可类推其余平台;若要扩展新平台,则需要在 media_platformstoremodelconfig 四处各加对应文件,并在 main.pyCrawlerFactorycmd_arg/arg.pyPlatformEnum 中注册。

十六、延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐