MediaCrawler 项目代码结构详解:从目录布局到核心调用链的源码导读
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 持久化),base、cache、proxy、tools 作为横切能力被各层复用。下面逐层展开。
二、程序入口 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_OPTION为sqlite/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 |
start、search、launch_browser |
爬虫主流程契约;另提供可选的 launch_browser_with_cdp,默认实现会回退到标准 launch_browser |
AbstractLogin |
login_by_qrcode、login_by_mobile、login_by_cookies、begin |
三种登录方式(二维码/手机验证码/Cookie)的统一契约 |
AbstractStore |
store_content、store_comment、store_creator |
内容、评论、创作者信息的存储契约 |
AbstractStoreImage / AbstractStoreVideo |
store_image、store_video |
媒体资源存储契约(源码注释标注目前仅部分平台实现,因此未强制为抽象方法) |
AbstractApiClient |
request、update_cookies |
平台 API 客户端契约,负责发起请求与刷新浏览器 Cookie |
每个 media_platform/<平台>/ 目录内部都会实现这套契约:core.py 是爬虫主流程、login.py 是登录逻辑、client.py 是 API 客户端,与上表一一对应。
四、cmd_arg 目录:Typer 命令行参数体系
cmd_arg/arg.py 基于 typer 库定义了全部命令行参数,其内部的枚举类也精确界定了项目的取值空间:
PlatformEnum:xhs、dy、ks、bili、wb、tieba、zhihu;LoginTypeEnum:qrcode、phone、cookie;CrawlerTypeEnum:search(关键词搜索)、detail(指定内容详情)、creator(创作者主页数据);SaveDataOptionEnum:csv、db、json、jsonl、sqlite、mongodb、excel、postgres;InitDbOptionEnum:sqlite、mysql、postgres,即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.py、config/dy_config.py、config/tieba_config.py 等),承载该平台特有的行为开关。
六、media_platform 目录:七平台采集实现
media_platform 是业务逻辑的核心,七个平台目录结构高度一致(以小红书为例):
core.py:采集主流程,实现AbstractCrawler的start/search等方法;login.py:实现AbstractLogin三种登录方式,其中抖音、小红书的登录链路会调用libs/下的签名脚本;client.py:实现AbstractApiClient,封装各平台内部 API;field.py:字段/常量定义;help.py:平台私有辅助函数;- 部分平台另有
exception.py、extractor.py(小红书)、playwright_sign.py与xhs_sign.py(小红书)、graphql.py及graphql/下的查询文件(快手使用 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 目录承担全部数据库交互:
- database/db.py:模块级数据库操作入口,
init_table_schema(db_type)根据类型初始化表结构,main.py中--init_db走的正是这里; - database/db_session.py:会话管理,含
create_tables等建表逻辑; - database/models.py:SQLAlchemy ORM 模型定义(如
BilibiliVideo表结构); - database/mongodb_store_base.py:MongoDB 存储基类。
值得特别注意的是当前仓库 models.py 头部的教学版说明:该版本 ORM 不再持久化可识别用户的字段(用户 ID、头像、主页链接、签名、性别等一律不落库),原始用户 ID 在提取层经 tools/user_hash.py 的 anonymize_user_id 转为匿名 creator_hash 后写入,昵称经脱敏处理,创作者个人档案表也已整体移除。这是使用当前代码库做数据存储时必须了解的合规设计。
model 目录则是各平台"业务数据模型":m_xiaohongshu.py、m_douyin.py、m_kuaishou.py、m_baidu_tieba.py、m_weibo.py、m_zhihu.py,用于描述笔记/视频/评论等实体的字段组织,与 database/models.py 中的 ORM 表模型分工不同——前者偏业务结构,后者偏持久化结构。
九、cache 目录:抽象缓存与工厂选择
cache 目录实现了"本地内存 + Redis"双后端缓存:cache/abs_cache.py 定义抽象基类,cache/local_cache.py 与 cache/redis_cache.py 分别是本地与 Redis 实现,cache/cache_factory.py 负责按配置选择后端。后端类型常量定义在 config/db_config.py:CACHE_TYPE_REDIS = "redis" 与 CACHE_TYPE_MEMORY = "memory"。测试目录 test/test_redis_cache.py 与 test/test_expiring_local_cache.py 分别验证两个后端的读写与过期行为。从源码结构看,缓存主要服务于代理 IP 池、登录状态等需要跨请求共享的数据。
十、proxy 目录:代理 IP 池的三层结构
proxy 目录采用"基类 + 提供商 + 池"的三层设计:
- proxy/base_proxy.py:定义
ProxyProvider抽象基类(IP 获取契约)与IpGetError异常; - proxy/providers/ 目录:具体提供商实现,
kuaidl_proxy.py(快代理)、jishu_http_proxy.py(极数 HTTP)、wandou_http_proxy.py(豌豆 HTTP),通过工厂函数new_kuai_daili_proxy、new_wandou_http_proxy暴露给上层; - 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 设为 True,IP_PROXY_PROVIDER_NAME 取 kuaidaili/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.AsyncSession 与 Session,说明存储层同时支持异步会话(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/postgres 走 database 层 ORM 落库,excel 走 ExcelStoreBase。各平台子目录中还可能有媒体专用存储文件,如 store/xhs/xhs_store_media.py、store/douyin/douyin_store_media.py,用于 ENABLE_GET_MEIDAS = True 时的图片/视频资源保存。
十二、tools 目录:跨平台的公共工具集
tools 是各层共用的工具函数仓库,文档中点名的几个文件对应明确职责:
- tools/browser_launcher.py:浏览器启动器,负责按配置拉起本地 Chrome/Edge 或 Playwright 内置浏览器;
- tools/cdp_browser.py:CDP 模式下的浏览器连接与管理,
main.py的async_cleanup中调用的cdp_manager.cleanup()即来自这条链路; - tools/crawler_util.py:爬虫通用工具函数;
- tools/utils.py:通用工具(如
str2bool,被cmd_arg参数解析调用); - 其余还包括 tools/async_file_writer.py(文件写出与词云生成)、tools/slider_util.py(滑块辅助)、tools/httpx_util.py(httpx 客户端构造)、tools/user_hash.py(用户 ID 匿名化,与
models.py的脱敏设计配套)等。
十三、其余顶层文件:constant、recv_sms.py 与 var.py
constant目录:各平台常量定义,当前仓库中为 constant/baidu_tieba.py 与 constant/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:两层测试目录
仓库中存在两个测试目录,可按主题定位:
test/:偏功能/集成性质,如 test/test_db_sync.py(数据库同步)、test/test_proxy_ip_pool.py(代理 IP 池)、test/test_redis_cache.py(Redis 缓存)、test/test_mongodb_integration.py(MongoDB 集成);tests/:偏单元与行为契约,如 tests/test_xhs_core_access_error.py(小红书访问异常处理)、tests/test_cmd_arg_tieba.py(贴吧参数解析)、tests/test_static_proxy_provider.py(静态代理)、tests/test_store_factory.py(存储工厂)等。
十五、沿着结构走一遍:一次采集请求的完整链路
综合以上各目录,一次典型采集运行(python main.py --platform xhs --ct search)的代码流经如下:
main.py的main()调用 cmd_arg/arg.py 解析命令行,覆盖config中由 config/base_config.py 与各平台配置合并出的参数;CrawlerFactory.create_crawler("xhs")返回media_platform/xhs中的XiaoHongShuCrawler,其start()内先登录(login.py,签名借助 libs/ 下脚本),再启动浏览器(base抽象的launch_browser或 CDP 模式,由tools/cdp_browser.py支撑,代理 IP 由proxy池提供);- 搜索/详情/创作者三种模式(
CrawlerTypeEnum)由core.py分发,client.py发起平台 API 请求,extractor.py/field.py把原始响应解析为结构化数据; - 数据经 store/xhs/_store_impl.py 按
SAVE_DATA_OPTION落盘:文件类走tools/async_file_writer.py,数据库类走databaseORM(用户标识先经tools/user_hash.py匿名化,见 database/models.py 教学版说明); - 运行结束,
main.py执行 Excel 落盘、词云生成、浏览器与数据库连接的清理。
从源码结构看,这套"工厂 + 抽象基类 + 平台目录同构"的布局,使得阅读任意一个平台的实现即可类推其余平台;若要扩展新平台,则需要在 media_platform、store、model、config 四处各加对应文件,并在 main.py 的 CrawlerFactory 与 cmd_arg/arg.py 的 PlatformEnum 中注册。
十六、延伸阅读
- 存储方案与数据库细节:数据存取指南、Excel 导出指南、项目架构文档;
- CDP 模式完整用法:CDP 模式使用指南;
- 代理与登录专题:代理使用、手机号登录说明、词云图使用配置。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00