基于Scrapy-Redis的分布式爬虫:全平台视频数据采集与互动率归因实战
简介这是一套面向数据科学、市场研究与舆情分析方向的B站全平台数据采集与分析项目源码适合具备Python基础、希望上手分布式爬虫与数据分析实战的开发者与研究者。系统覆盖视频数据、用户信息、弹幕、评论、点赞、收藏、分享、播放量、粉丝数、关注列表、历史投稿、直播数据与专栏文章等多类目标并配套数据清洗、数据库存储、可视化与机器学习分析思路。资源包共112个文件约5.29MB以js、png、md、ipynb、py、json、yml、html、css等为主涵盖前端页面、Notebook分析、配置脚本与说明文档另附docx说明与txt使用指引便于快速理解项目结构与运行方式。目前已有146人学习下载可帮助读者掌握分布式爬虫框架搭建、异步与动态渲染数据处理、数据清洗入库及多维度分析流程并了解反爬适配与合规采集要点。1. 全平台数据采集系统到底在采什么从一个反直觉的结论说起很多人第一次做视频平台数据采集直觉是“把播放量、点赞数抓下来就完事了”。但真正跑过一轮全平台数据采集系统的人会告诉你播放量是最没用的字段。原因很简单播放量是平台侧聚合后的结果指标它既不能反推用户行为路径也无法做跨分区对比——一个鬼畜区的百万播放和知识区的百万播放背后的用户画像、完播率、互动结构完全不同。基于 Python 的分布式爬虫框架要解决的恰恰是把这个“结果指标”拆成可分析的行为链路用户信息、弹幕、评论、点赞、收藏、分享、播放量、粉丝数、关注列表、历史投稿、直播数据、专栏文章这些字段组合起来才构成一个可做归因分析的数据底座。这套系统适合两类人一类是做内容运营分析、想搞清楚某个分区为什么起量的从业者另一类是想练分布式爬虫工程能力、但苦于找不到真实中等规模数据源的开发者。它不追求“爬全站”而是追求在可控资源下把一条完整的数据链路跑通——从任务分发、去重、限速、断点续爬到落库、清洗、可视化。下面按我实际搭过的一套方案把选型、代码、参数和踩过的坑讲清楚。2. 分布式爬虫框架选型为什么我最终用 Scrapy-Redis 而不是自己写调度2.1 三种常见架构的取舍做视频平台数据采集绕不开三个选择单机多线程、Scrapy-Redis 分布式、以及基于消息队列的自研调度。单机多线程在数据量小于十万级时够用但一旦涉及弹幕和评论这种长尾接口单机 IP 很快触发风控。自研调度灵活但去重、断点续爬、任务重试这些基础设施要自己写工期至少翻三倍。我最终选 Scrapy-Redis核心原因是它把请求队列和去重指纹都放在 Redis 里多个爬虫节点共享同一个队列天然支持横向扩容和断点续爬。代价是 Redis 成为单点需要自己做持久化配置。常见做法是 Redis 开 AOF 持久化爬虫节点无状态挂了直接重启即可。方案去重断点续爬扩容成本适用数据量单机多线程内存集合不支持高 10 万Scrapy-RedisRedis 集合支持低10 万千万自研 MQ 调度自实现支持中任意2.2 环境搭建与依赖安装先建虚拟环境把依赖锁在 requirements 里。注意 Scrapy-Redis 对 Redis 版本有要求低于 5.0 的 Redis 在大量指纹写入时性能下降明显。python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install scrapy scrapy-redis redis pymysql pandas pip freeze requirements.txt逻辑说明scrapy 是爬虫核心scrapy-redis 提供分布式调度组件redis 是 Python 客户端pymysql 负责落库pandas 留给后续清洗。参数上没有特殊配置但建议把 Redis 服务端单独部署不要和爬虫节点混部否则内存竞争会导致指纹写入延迟。2.3 Scrapy-Redis 核心配置项在 settings.py 里改四个地方这是分布式能不能跑起来的关键。# settings.py REDIS_HOST 127.0.0.1 REDIS_PORT 6379 REDIS_PARAMS {db: 0, password: } # 调度器换成 Redis 队列 SCHEDULER scrapy_redis.scheduler.Scheduler DUPEFILTER_CLASS scrapy_redis.dupefilter.RFPDupeFilter SCHEDULER_PERSIST True # 断点续爬的关键False 则每次清空队列 # 请求队列用优先队列方便给不同接口设优先级 SCHEDULER_QUEUE_CLASS scrapy_redis.queue.PriorityQueue # 下载延迟视频平台接口建议不低于 1.5 秒 DOWNLOAD_DELAY 1.5 RANDOMIZE_DOWNLOAD_DELAY True CONCURRENT_REQUESTS_PER_DOMAIN 4逻辑说明SCHEDULER_PERSIST True是断点续爬的开关设为 False 时爬虫重启会清空 Redis 里的请求队列之前排队的任务全丢。PriorityQueue让弹幕、评论这类高频接口和用户信息这类低频接口可以分优先级避免低频接口被高频接口饿死。DOWNLOAD_DELAY配合RANDOMIZE_DOWNLOAD_DELAY做随机延迟比固定延迟更难被识别为爬虫。注意CONCURRENT_REQUESTS_PER_DOMAIN不要超过 8实测超过 8 之后接口返回空数据的概率明显上升这是血泪经验。3. 核心数据字段的采集实现从视频详情到弹幕分片3.1 视频详情与互动数据的接口组织视频详情接口通常返回播放量、点赞、收藏、分享、投币等聚合字段但评论和弹幕是独立接口。我的做法是把一个视频拆成三类任务详情任务、评论任务、弹幕任务分别入队互不阻塞。import scrapy from scrapy_redis.spiders import RedisSpider class VideoDetailSpider(RedisSpider): name video_detail redis_key video:detail:start_urls def parse(self, response): data response.json() item { bvid: data[data].get(bvid), title: data[data].get(title), play: data[data][stat].get(view), like: data[data][stat].get(like), coin: data[data][stat].get(coin), favorite: data[data][stat].get(favorite), share: data[data][stat].get(share), reply: data[data][stat].get(reply), danmaku: data[data][stat].get(danmaku), owner_mid: data[data][owner].get(mid), } yield item # 详情拿到后把评论和弹幕任务推入队列 bvid item[bvid] self.server.lpush(video:comment:start_urls, fcomment:{bvid}) self.server.lpush(video:danmaku:start_urls, fdanmaku:{bvid})逻辑说明RedisSpider从 Redis 列表里取起始 URLparse里解析 JSON 后 yield item 落库同时把评论和弹幕任务推入各自的队列。参数上stat字段里的互动数据是聚合值不需要翻页owner.mid是后续抓用户信息的关联键必须存下来。3.2 弹幕分片采集与时间轴对齐弹幕接口通常按时间段分片返回一个视频的弹幕可能分散在多个分片里。常见做法是先请求一次拿到分片总数再按分片并发拉取。class DanmakuSpider(RedisSpider): name danmaku redis_key video:danmaku:start_urls def parse(self, response): bvid response.meta[bvid] data response.json() for d in data.get(elems, []): yield { bvid: bvid, time_offset: d[progress], # 毫秒弹幕出现时间 mode: d[mode], # 弹幕类型滚动/顶部/底部 font_size: d[fontsize], color: d[color], content: d[content], send_time: d[ctime], }逻辑说明progress是弹幕相对视频开头的毫秒偏移做时间轴分析时必须保留。mode区分弹幕类型做情感分析时滚动弹幕和底部弹幕的权重可以不同。参数上弹幕接口一般有分页需要在 parse 里判断是否还有下一页有则 yield 新 Request 并带上bvid到 meta。3.3 用户信息与关注列表的递归采集用户信息接口返回粉丝数、关注数、历史投稿数关注列表和历史投稿是分页接口。这里要控制递归深度否则容易从一个种子用户扩散到全站。class UserSpider(RedisSpider): name user redis_key user:info:start_urls max_depth 2 # 只递归两层避免无限扩散 def parse(self, response): data response.json() mid data[data][mid] yield { mid: mid, name: data[data][name], fans: data[data][fans], following: data[data][following], archive_count: data[data][archive_count], } depth response.meta.get(depth, 0) if depth self.max_depth: # 把关注列表里的用户推入队列深度加一 for f in data[data].get(following_list, []): self.server.lpush(user:info:start_urls, fuser:{f[mid]})逻辑说明max_depth是防止递归扩散的硬约束实测不设这个参数一个种子用户能在几小时内扩散出几十万任务。参数上fans和following是快照值做趋势分析需要定时重采建议用(mid, date)做联合主键。4. 数据落库与去重MySQL 表设计和 Redis 指纹的配合4.1 表结构设计视频、用户、弹幕、评论分四张主表互动数据用宽表存快照。CREATE TABLE video_snapshot ( bvid VARCHAR(20) NOT NULL, stat_date DATE NOT NULL, play INT DEFAULT 0, like_count INT DEFAULT 0, coin INT DEFAULT 0, favorite INT DEFAULT 0, share INT DEFAULT 0, reply INT DEFAULT 0, danmaku INT DEFAULT 0, PRIMARY KEY (bvid, stat_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE danmaku ( id BIGINT AUTO_INCREMENT PRIMARY KEY, bvid VARCHAR(20), time_offset INT, mode TINYINT, content VARCHAR(500), send_time INT, INDEX idx_bvid (bvid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明video_snapshot用(bvid, stat_date)联合主键同一天重复采集会覆盖而不是插入新行天然去重。danmaku表用自增主键因为弹幕内容可能重复不能用内容做唯一键。参数上content给 500 字符足够超长弹幕极少。4.2 Redis 指纹与 MySQL 去重的分工Scrapy-Redis 的RFPDupeFilter只对请求 URL 去重不保证数据不重复入库。我的做法是请求层用 Redis 指纹去重数据层用 MySQL 联合主键或INSERT IGNORE兜底。import pymysql def save_item(item): conn pymysql.connect(host127.0.0.1, userroot, password, dbbili_data, charsetutf8mb4) sql INSERT IGNORE INTO video_snapshot (bvid, stat_date, play, like_count, coin, favorite, share, reply, danmaku) VALUES (%s, CURDATE(), %s, %s, %s, %s, %s, %s, %s) with conn.cursor() as cur: cur.execute(sql, (item[bvid], item[play], item[like], item[coin], item[favorite], item[share], item[reply], item[danmaku])) conn.commit() conn.close()逻辑说明INSERT IGNORE在联合主键冲突时静默跳过避免重复采集导致数据膨胀。参数上CURDATE()让同一天多次采集只保留第一次的值如果要做日内趋势需要把stat_date换成DATETIME并调整主键。5. 避坑与排查五个让我返工三次的坑5.1 弹幕接口返回空数组但状态码 200现象弹幕接口返回{code:0,data:{elems:[]}}状态码 200但实际有弹幕。原因分片参数没传对或者该分片确实为空但代码没做空判断直接跳过。解决先请求一次拿分片总数再逐片拉取对空数组做日志记录统计空片比例超过 30% 说明分片参数有问题。5.2 Redis 内存暴涨导致爬虫卡死现象跑了几小时后爬虫节点全部卡住Redis 内存占用超过 80%。原因RFPDupeFilter的指纹集合只增不减千万级 URL 后内存占用很大。解决给 Redis 设maxmemory-policy allkeys-lru或者定期用脚本清理已完成任务的指纹。更稳妥的做法是把指纹单独放一个 Redis 实例和任务队列隔离。5.3 评论接口翻页到后面返回重复数据现象评论翻页到第 20 页后返回的数据和前面几页重复。原因评论接口的游标是基于时间戳的翻页过程中有新评论插入会导致游标偏移。解决记录每次翻页的最后一个评论 ID 作为游标而不是用页码同时接受一定程度的重复靠 MySQL 唯一键去重。5.4 用户信息接口触发风控返回 412现象连续请求用户信息接口后返回 412 状态码持续几十分钟。原因请求频率过高且 User-Agent 固定。解决DOWNLOAD_DELAY调到 2 秒以上User-Agent 池至少准备 20 个配合RANDOMIZE_DOWNLOAD_DELAY。412 出现后立即暂停该接口任务等 30 分钟再恢复。5.5 分布式节点时钟不一致导致快照日期错乱现象不同节点写入的stat_date差一天同一天的数据被拆成两行。原因节点服务器时区或时钟不同步。解决所有节点统一用 UTC 时间落库时在应用层转换时区不要依赖数据库的CURDATE()。6. 从采集到分析用 Pandas 做互动率归因的一个具体技巧数据落库只是第一步真正有价值的是把互动数据做成可对比的指标。我常用的一个技巧是互动率归一化把点赞、收藏、分享分别除以播放量得到三个比率再用这三个比率做聚类比直接看绝对值靠谱得多。import pandas as pd df pd.read_sql(SELECT * FROM video_snapshot WHERE stat_date CURDATE(), conn) df[like_rate] df[like_count] / df[play].replace(0, 1) df[coin_rate] df[coin] / df[play].replace(0, 1) df[fav_rate] df[favorite] / df[play].replace(0, 1) # 按互动率中位数分四象限 like_med df[like_rate].median() fav_med df[fav_rate].median() df[quadrant] df.apply( lambda r: 高赞高藏 if r[like_rate] like_med and r[fav_rate] fav_med else 高赞低藏 if r[like_rate] like_med else 低赞高藏 if r[fav_rate] fav_med else 低赞低藏, axis1) print(df.groupby(quadrant)[play].agg([count, mean]))逻辑说明replace(0, 1)防止除零。四象限划分后高赞低藏通常对应情绪型内容高赞高藏对应工具型内容低赞高藏往往是收藏夹吃灰型。参数上中位数比均值更抗极端值如果数据量小于 1000 条建议改用四分位数。验证方法很简单拿同一分区一周的数据跑一遍看四象限的播放量均值是否符合直觉。如果低赞低藏的播放量反而最高说明播放量有刷量嫌疑需要回到采集层检查是否有异常账号。我自己踩过最深的坑是早期没做互动率归一化直接按播放量排序做内容分析结果被几个刷量视频带偏了整整一周的结论。后来养成习惯任何聚合指标先做比率化再看分布最后才看绝对值。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Winlator 上手指南:在安卓手机上直接运行 Windows 程序,几分钟就能跑起来

Winlator 上手指南:在安卓手机上直接运行 Windows 程序,几分钟就能跑起来

Winlator 上手指南:在安卓手机上直接运行 Windows 程序,几分钟就能跑起来 【免费下载链接】winlator Android application for running Windows applications with Wine and Box86/Box64 项目地址: https://gitcode.com/GitHub_Trending/wi/winlator …

2026/10/13 9:31:12 阅读更多 →
数据库设计实战:学生选课系统的ER模型、事务与并发控制

数据库设计实战:学生选课系统的ER模型、事务与并发控制

简介:这是一份针对数据库原理与应用课程的高分课程设计资源,围绕某高校学生选课系统展开,着重演示数据库系统分析与建模思路。资源重点放在数据库部分,不含完整应用程序,内含课程设计报告和数据库脚本,适合…

2026/10/13 9:31:12 阅读更多 →
PostgreSQL索引访问方法属性调优实战指南

PostgreSQL索引访问方法属性调优实战指南

1. 为什么“访问方法属性”才是索引性能的真正开关很多人学PostgreSQL索引,一上来就背B-tree、Hash、GIN、GiST这些名词,抄几行CREATE INDEX ON table USING btree (col)就以为掌握了。我带过不少刚转数据库开发的同事,他们能熟练写出复合索引…

2026/10/13 9:31:12 阅读更多 →

最新新闻

FFMpeg 4.1音频检测实战:命令详解与批处理自动化

FFMpeg 4.1音频检测实战:命令详解与批处理自动化

做内容生产这一年多,我处理过最多的活儿不是剪辑,而是给一堆音频素材做体检。尤其是接手播客剪辑、视频课程批量转码这类项目时,几十个甚至上百个文件摆在面前,里面哪个有静音、哪个音量忽大忽小、哪个爆音了,光靠耳朵…

2026/10/13 10:15:43 阅读更多 →
DDR Training失败怎么办?从硬件到软件的完整排查指南

DDR Training失败怎么办?从硬件到软件的完整排查指南

做嵌入式硬件开发的人,大概都遇到过这样的噩梦:新板子打回来,上电,串口没有任何输出,系统起不来。查电源,电压都对;查时钟,晶振在振;查复位,复位释放了。最后…

2026/10/13 10:15:43 阅读更多 →
基于CNN的音乐推荐系统设计与实现|大数据专业项目|毕设项目|大数据专业设计|毕设

基于CNN的音乐推荐系统设计与实现|大数据专业项目|毕设项目|大数据专业设计|毕设

一、項目介绍 摘 要 在数字音乐蓬勃发展的当下,音乐库规模急剧膨胀,用户高效获取心仪音乐面临挑战,个性化音乐推荐系统的重要性日益凸显。然而,传统音乐推荐系统存在推荐效果参差不齐、冷启动等诸多问题。卷积神经网络&#xff0…

2026/10/13 10:15:43 阅读更多 →
Meta为Personal Agent推出一个新协议PAP

Meta为Personal Agent推出一个新协议PAP

Meta为Personal Agent推出一个新协议PAP1-PAP(Personal Agent Protocol)是一套由Meta和Sierra联合发起的开放标准,它规定了个人AI助理在代表你与商家打交道时,应该如何证明自己的身份、获得你的授权,以及能做什么、不能…

2026/10/13 10:15:43 阅读更多 →
无代码平台:企业数字化转型的新引擎

无代码平台:企业数字化转型的新引擎

1. 无代码平台行业现状与核心价值无代码平台正在重塑企业数字化建设的游戏规则。过去需要专业开发团队耗时数月才能完成的应用搭建,现在业务人员通过拖拽组件就能在几小时内实现。根据最新行业调研数据,2023年全球无代码开发平台市场规模已达到138亿美元…

2026/10/13 10:15:43 阅读更多 →
page_alloc alloc_pages_bulk_noprof

page_alloc alloc_pages_bulk_noprof

alloc_pages_bulk_noprof() 是内核提供的批量页面分配接口。它的核心设计目标是减少锁竞争和函数调用开销:通过一次持有 PCP(Per-CPU Pages)锁,连续分配多个页面填充到调用方提供的数组中,而不是反复调用单页分配接口。…

2026/10/13 10:14:40 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →