ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Python爬虫与Hadoop的B站短视频数据分析系统实战

基于Python爬虫与Hadoop的B站短视频数据分析系统实战 1. 项目概述这个系统到底解决了什么问题做毕业设计那段时间我给自己选了个难度不低的题目——基于大数据爬虫和Hadoop的B站短视频热门趋势分析与创作者测量研究系统。简单说就是用Python爬虫采集B站视频的公开数据把数据落地到HDFS上再用Hadoop生态里的工具做离线统计最后把B站短视频热门趋势、UP主的账号表现量化成一堆可解释的指标用网页展示出来。当时选这个题一是自己天天刷B站对内容生态感兴趣二是这条链路足够长能把爬虫、数据清洗、分布式存储、离线计算、数据可视化全部串在一起非常适合大数据专业的毕业设计也方便在论文里写出完整的故事线。这个项目做下来之后我的感受是它并没有想象中那么高不可攀但也绝对不是一个晚上能肝出来的东西。真正耗时间的不是写爬虫也不是搭Hadoop而是把“数据口径”想清楚——什么叫热门怎么衡量一个创作者同一份数据把统计维度换一下结论就可能完全不一样。这篇文章就是把我从选题、搭建开发环境、设计爬虫采集、Hadoop写入、Hive统计到写论文和准备答辩PPT的全过程以及中间踩过的大大小小的坑系统性地整理出来。如果你也想做类似的大数据系统设计或者正在被“爬虫Hadoop”类题目折磨这篇应该能帮你少走不少弯路。1.1 选题背景和核心目标B站是一个以用户自制内容为主的视频社区和传统视频网站相比它有非常明显的“UP主驱动”特征。一个视频火不火不只看播放量还要看点赞、投币、收藏、分享、评论这些互动行为。这给数据分析带来了很好的素材每条视频都有清晰的属性字段每个UP主都有公开的数据主页而且内容本身有分区、时长、发布时间等结构化信息。相比纯电商数据或者社交文本B站视频数据更贴近“内容产品”的分析场景。我把项目的核心目标拆成三个采集B站短视频的公开元数据形成一份可持续更新的本地数据集基于Hadoop做离线统计分析理解不同分区、不同时间段的热门走势设计一套创作者测量指标尽量从“内容质量”和“用户互动”的角度给UP主画像而不是只看粉丝数。简单说这个系统的输出有两类一类是面向视频的热门趋势分析另一类是面向创作者的测量报告。前者回答“什么样的内容在什么时间段更火”后者回答“一个UP主为什么火、他的成长健康度怎么样”。1.2 为什么是爬虫 Hadoop 的组合很多人问做B站数据分析直接用Excel或者MySQL就够了为什么非要绕一大圈用Hadoop我的回答是从纯功能上看确实够但从毕业设计和技术训练的完整度上看搭一套Hadoop生态能学到的内容完全不同。爬虫解决“数据从哪来”的问题。B站的数据没有提供官方批量下载服务但网页端有很多公开的数据接口我们自己写Python脚本去采集这是整个系统最底层的起点。Hadoop则解决“数据怎么算”的问题。当你只采集几百条视频数据时一条SQL或者一个Pandas脚本就能秒出结果感觉不到分布式的价值可一旦你把采集时间拉长把每天的热门榜、每个分区的内容都抓下来数据会越攒越多这时候就会遇到小文件多、统计口径不一致、多张表join起来计算变慢的问题。Hadoop里的HDFS适合存这种大批量、一次写入多次读取的文件MapReduce和Hive适合做离线的全量统计。还有一个很现实的原因大数据专业的毕业设计要求用到大数据的经典技术栈。Hadoop是目前各大高校课程里最常出现的入门框架网上资料多、实验环境成熟、答辩时老师也熟悉。选Hadoop而不是直接上Spark一方面是因为Hadoop离线批处理的思路更直观容易把流程讲清楚另一方面是如果你已经掌握Hadoop后续再学Spark也不会费太大劲。1.3 系统整体数据流设计项目整体可以画成一条单向的数据流我不太推荐一开始就设计得太复杂先把主干跑通再慢慢加分支会比较稳妥。整个流程是采集层Python使用Requests请求B站网页端的公开数据接口拿到JSON格式的数据暂存层把JSON解析成结构化字段清洗去重后存成CSV文件存储层把CSV上传到HDFS指定目录同时把计算结果回写到MySQL中方便后面做可视化查询计算层用Hive写离线SQL统计播放、点赞、投币、评论等维度应用层使用Flask ECharts搭建简单的Web页面展示趋势曲线、热榜排行和创作者雷达图。我特别说明一下为什么中间要加一个MySQL。虽然HDFS能存原始数据但它不适合做高频的在线查询。分析完的结果通常只有几百到几千行属于小数据放到MySQL里查询速度很快Web后端写起来也顺手。这样既满足了“大数据处理”的主题又保证了最终演示系统好用。这个“Hadoop算数、MySQL出报表”的混搭方案在实际毕业设计中是很好用的。2. 数据采集层B站短视频爬虫的完整实现2.1 采集对象与数据字段设计写爬虫之前必须先想清楚要采集哪些字段。我的做法是先看B站网页端能提供哪些字段再反过来设计数据库表结构。我最终采集的核心字段如下视频信息bvid、标题、分区、主分区、发布时间、视频时长互动数据播放量、弹幕数、评论数、点赞数、投币数、收藏数、分享数创作者信息UP主mid、昵称、粉丝数、签名、是否认证。这里有个经验分区字段一定要同时保留“主分区”和“二级分区”。一开始我只采集了二级分区后来做热门趋势分析时发现二级分区分得太细样本量不够很多统计结果没有说服力。后来重新回头把主分区补上分析维度就灵活多了。B站网页端的接口返回的是JSON结构很规范字段名基本是英文小写比如aid、bvid、view、like、coin、favorite。用Requests直接请求就能拿到不需要复杂的浏览器模拟。说实话这个项目的爬虫难度并不高难的是怎么优雅地处理接口偶发异常和数据结构变化。2.2 接口调用代码与请求头伪装下面是一个核心的视频详情采集函数我尽可能把逻辑简化方便你直接改来用。import requests import time import random def fetch_video_detail(bvid): url https://api.bilibili.com/x/web-interface/view params {bvid: bvid} headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://www.bilibili.com/, } try: resp requests.get(url, paramsparams, headersheaders, timeout10) if resp.status_code ! 200: print(f请求失败: {bvid} - {resp.status_code}) return None data resp.json() if data[code] ! 0: print(f接口返回错误: {bvid} - {data[message]}) return None detail data[data] return { bvid: bvid, title: detail.get(title, ), pubdate: time.strftime( %Y-%m-%d %H:%M:%S, time.localtime(detail.get(pubdate, 0)) ), duration: detail.get(duration, 0), view: detail.get(stat, {}).get(view, 0), like: detail.get(stat, {}).get(like, 0), coin: detail.get(stat, {}).get(coin, 0), favorite: detail.get(stat, {}).get(favorite, 0), share: detail.get(stat, {}).get(share, 0), reply: detail.get(stat, {}).get(reply, 0), mid: detail.get(owner, {}).get(mid, 0), author: detail.get(owner, {}).get(name, ), } except Exception as e: print(f解析失败: {bvid} - {e}) return None一个容易被忽略的点是请求头里的Referer。B站部分接口会校验Referer如果你不带或者带错了很容易拿到异常结果。另外不要把User-Agent伪装得过于“完美”我用一个正常的浏览器UA就够用了没必要去模拟什么冷门浏览器。2.3 并发采集与保存策略单线程跑请求虽然稳定但效率太低了。我第一版写的单线程爬虫采集一万条视频跑了将近两个小时后来改成多线程并发时间缩短到二十分钟以内。我用的是Python标准库里的ThreadPoolExecutor控制最大并发数在4到6之间不需要上协程也不用Scrapy这个体量用线程池刚好。每个请求完成之后加一个小小的随机休眠模拟真实用户的操作节奏。from concurrent.futures import ThreadPoolExecutor, as_completed def batch_fetch(bvid_list, workers4): results [] with ThreadPoolExecutor(max_workersworkers) as pool: future_map { pool.submit(fetch_video_detail, bvid): bvid for bvid in bvid_list } for future in as_completed(future_map): bvid future_map[future] result future.result() if result: results.append(result) time.sleep(random.uniform(0.2, 0.8)) return results这里有一个很重要的坑并发数不是越大越好。我把并发调大到20时B站很快就返回412状态码连续几次之后整个IP的访问就会受到影响。通常情况下接口访问频次保持每秒不超过3次是比较稳妥的。采集到的数据怎么存我建议不要立刻上传HDFS先在本地落成CSV。原因很简单HDFS不适合频繁覆盖修改而爬虫阶段的数据质量还很不稳定先存本地便于反复清洗。2.4 数据清洗与去重策略这一块直接决定后面分析的靠谱程度。B站返回的原始数据并不干净常见的坑有几个字段值是字符串和数字混着来的比如有些接口返回的数字是字符串需要在清洗时统一转成int发布时间是Unix时间戳需要转换成可读的日期格式部分视频会失效返回的数据里缺少stat字段要设置默认值重复采集会造成bvid重复必须以bvid为主键去重。清洗逻辑可以用Pandas做也可以用纯Python写。我习惯用Pandas因为它的类型转换和缺失值处理都比较方便。import pandas as pd def clean_data(df): df df.drop_duplicates(subset[bvid], keeplast) df df.dropna(subset[bvid, title]) df[view] pd.to_numeric(df[view], errorscoerce).fillna(0).astype(int) df[like] pd.to_numeric(df[like], errorscoerce).fillna(0).astype(int) df[pubdate] pd.to_datetime(df[pubdate], errorscoerce) return df处理完的数据最终保存成统一的CSV字段顺序固定后期无论导入Hive还是MySQL都很方便。清洗这块我建议多花时间因为论文里“数据预处理”这一章能不能写出东西完全看这个阶段你沉淀了多少细节。3. 数据落地Hadoop 环境搭建与数据上 HDFS3.1 伪分布式还是真实集群很多同学看到Hadoop就紧张觉得要准备三台服务器。其实做毕业设计先搭伪分布式完全够用而且更利于排错。伪分布式就是只有一个节点同时充当NameNode、DataNode、ResourceManager和NodeManager所有分布式组件都在一台机器上运行。我当时的建议是开发阶段用伪分布式论文最后如果条件允许再在三台真实机器上跑一遍同样的统计任务对比一下性能。这样论文里既有实验过程的细节又有“分布式环境下扩展”的讨论内容会丰满不少。如果你实验室有现成的大数据集群申请一个目录权限就行没必要自己从头搭三节点。3.2 三个核心配置文件Hadoop的配置看似很多但绝大多数情况下只需要改四个文件core-site.xml、hdfs-site.xml、yarn-site.xml和mapred-site.xml。我以Hadoop 3.x版本为例给出最简配置。core-site.xm用来设置文件系统入口和临时目录configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property property namehadoop.tmp.dir/name value/home/hadoop/hadoop_tmp/value /property /configurationfs.defaultFS是客户端访问HDFS的入口地址我这里用的是localhost如果你是多节点集群这里要换成NameNode所在的主机名或者IP。hdfs-site.xml里最需要关心的是副本数。伪分布式环境默认会设置成3但只有一个DataNode副本数为3会一直报冗余警告虽然不致命但看起来很难受。configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/home/hadoop/hadoop_tmp/namenode/value /property property namedfs.datanode.data.dir/name value/home/hadoop/hadoop_tmp/datanode/value /property /configurationyarn-site.xml主要是给运行MapReduce任务调内存用。如果机器内存只有8G不要贪心控制在2G以下。configuration property nameyarn.nodemanager.resource.memory-mb/name value2048/value /property /configuration配置文件改完后不要急着直接启动。第一件要做的事是格式化NameNodehdfs namenode -format。这个命令只执行一次第二次再跑会把旧的元数据清掉很多新手在这上面翻车。启动之后用jps命令检查进程正常情况下能看到NameNode、DataNode、ResourceManager和NodeManager四个Java进程。3.3 数据导入 HDFS清洗完的CSV数据可以直接用命令行上传到HDFS。我习惯先建好目录结构避免所有数据都堆在一个目录里后期不好管理。hdfs dfs -mkdir -p /data/bilibili/raw hdfs dfs -put bilibili_videos.csv /data/bilibili/raw/ hdfs dfs -ls /data/bilibili/raw上传之后可以在命令行里用hdfs dfs -tail查看文件末尾确认内容是否正常。这里要提醒一下如果CSV文件是空文件HDFS也会上传成功但后面Hive查出来就是零行所以上传前一定要检查本地文件大小。3.4 Hive 离线统计最常用的几条 QueryHive的好处是能让你用SQL去查HDFS上的数据不需要手写MapReduce。这里我把Hive表设计成外部表数据文件放在HDFS上表删了数据也不会丢。建表语句如下CREATE EXTERNAL TABLE IF NOT EXISTS bili_video( bvid STRING, title STRING, category STRING, pubdate STRING, view INT, like_count INT, coin_count INT, favorite_count INT, share_count INT, reply_count INT ) ROW FORMAT DELIMITED FIELDS TERMINATED BY , STORED AS TEXTFILE LOCATION /data/bilibili/raw TBLPROPERTIES (skip.header.line.count1);注意最后一行的参数如果你的CSV带表头需要让Hive跳过第一行否则会把“bvid,title”当成一条数据导进去。接下来就可以做一些简单的统计了。比如按分区统计平均播放量SELECT category, COUNT(*) AS video_num, ROUND(AVG(view), 2) AS avg_view, ROUND(AVG(like_count), 2) AS avg_like FROM bili_video GROUP BY category ORDER BY avg_view DESC;这类SQL在答辩演示时非常直观。老师一看就知道你确实把Hadoop链路打通了而不是只拿Excel糊弄。4. 核心算法热门趋势指数与创作者测量模型4.1 热门指数的计算口径做趋势分析第一步就是定义“热门”。直接用播放量排名是最懒的办法也很容易被评委追问。因为一个发布了两年的老视频和一条发布两小时的视频播放量完全不具备可比性。我设计的热门指数综合考虑了几类指标并按照时间窗口做了归一化。计算思路大致是时间窗口取近7天为分析周期发布时间超过窗口的视频不参与“突发热度”计算播放增速用视频当日新增播放量除以上一日播放量突出增长性互动率点赞、投币、收藏、分享、评论分别除以播放量得到互动深度完播相关把视频时长作为修正项因为短视频和长视频的互动规律不一样时长超过20分钟的视频天然更容易获得更高的完播时长但不一定有同样高的点赞率。最后加权得到一个0到100之间的热门指数hot_score 0.25 * view_growth_score 0.20 * like_score 0.15 * coin_score 0.15 * favorite_score 0.15 * share_score 0.10 * comment_score每个分项都先做最大最小归一化把原始数值压缩到0到1之间。权重的取值可以谈我在论文里用的是熵权法让数据自己决定权重减少“拍脑袋”的感觉。你不用完全照搬只要保证每个指标都有清晰的业务含义并能说出为什么这样加权就行。4.2 创作者测量比“粉丝数”更值得看的指标“创作者测量”是这套系统里听起来最高端、实际也最需要解释清楚的部分。我做的不是简单把所有UP主按粉丝数排个序而是设计了一套“创作者健康度”指标从四个维度衡量一个账号更新活跃度近30天发布视频的条数以及发布周期的稳定性互动效率近30天所有视频的点赞投币收藏总数除以播放总数爆款能力该UP主近30天进入热门指数TOP10%的视频数量粉丝粘性评论互动、视频回复评论的比例这里用的是公开的评论数据。四个维度分别归一化后加权得到综合得分最终分成“高潜创作者”“稳定创作者”“流量型创作者”“衰退型创作者”四类。这种分类在演示时非常有意思你可以指着某个分区说这个UP主粉丝不多但互动率极高属于高潜类型。创作者测量的重点不是算法本身多复杂而是你要能做到“指标可解释”。答辩时间有限老师大概率会盯着某一个指标问你“这个是怎么算出来的”“这个指标能说明什么”。我在论文里把每个指标的公式、字段来源、归一化方法都用表格列了出来这一章写完后论文的核心创新点就有了落脚点。4.3 可视化展示分析结果最终要给人看。我用Flask做了一个简单的后端数据从MySQL读取前端用ECharts渲染图表。页面主要分成三块趋势总览展示近30天B站视频发布量和播放总量的走势折线图横轴是日期纵轴是数值分区热榜选择一个分区后展示热门指数TOP20的视频榜单用表格加进度条展示各项互动数据创作者画像输入UP主的mid展示他的各项指标和健康度等级的雷达图。这样一个可视化系统在答辩演示时效果很好因为老师能直观看到数据链路的价值而不是只对着命令行和SQL干瞪眼。5. 从零到答辩项目开发节奏与资料整理5.1 功能迭代路线如果你打算在两个月内搞定这个项目我建议把时间切成五段每个时间段都有明确交付物第一周搭好Hadoop伪分布式环境跑通HDFS上传下载第二周写爬虫脚本采集第一批数据完成本地清洗第三周数据导入HDFS用Hive写出基础统计SQL第四周设计热门指数和创作者测量模型把统计结果导出到MySQL第五周开发Flask可视化页面整理演示Demo第六周写论文、做PPT、准备答辩问答。这个节奏有两点很关键第一不要一开始就沉迷爬虫因为Hadoop环境搭建的未知问题更多先把它解决掉心里才不慌第二每周都要有一个能演示的中间产物比如第二周你虽然还没搭完Hadoop但手里已经有一份清洗好的CSV进度就没那么焦虑。5.2 论文结构建议论文结构我按最常见的六章来安排第一章 绪论写背景、国内外研究现状、研究内容和意义第二章 相关技术介绍Python爬虫、Hadoop、Hive、可视化技术第三章 需求分析与总体设计包括功能需求、非功能需求、系统架构图、数据库设计第四章 数据采集与预处理重点写B站数据源分析、爬虫实现、数据清洗规则第五章 大数据统计分析实现写Hadoop环境部署、Hive建表与统计、热门指数计算第六章 系统实现与测试写可视化页面、功能测试结果和性能分析。写论文的时候记住一个原则不要整段贴代码要把代码里蕴含的设计思路讲出来。比如爬虫为什么要用线程池Hive为什么建外部表这些解释比代码本身更能证明你理解了项目。5.3 答辩PPT和演示细节答辩PPT不用太长15到20页足够了。我建议的PPT结构是选题背景与意义1页系统整体架构图1页爬虫设计与数据字段说明2页Hadoop环境与数据上传流程2页热门趋势分析算法2页创作者测量模型2页系统演示截图4页遇到的问题和解决方法2页总结与展望1页。演示是答辩里的重头戏。学院机房环境一般比较卡我强烈建议提前录制一个演示视频时间控制在三分钟以内。视频内容包括打开爬虫脚本、上传数据到HDFS、执行一条Hive查询、打开系统页面点击几个功能。即使现场服务器崩了视频也能兜底。6. 踩坑实录爬虫、Hadoop和答辩的实战教训6.1 爬虫层容易翻车的四个坑第一接口返回412。这个状态码我一开始完全没遇到过查了半天才知道是请求频率太高触发了风控。解决方法是把并发线程数降下来请求间隔加上随机值并且在代码里判断状态码一旦出现412就先暂停几分钟。第二字段值忽然变成空。B站接口偶尔会针对部分视频返回不完整的stat字段如果代码里直接detail[stat][view]就会报KeyError。处理办法是使用get方法并设置默认值同时在清洗阶段用Pandas把字符串型的数字统一转成int。第三时间戳类型错误。有一次我为了省事直接把pubdate存成Unix时间戳后面做Hive统计时发现“日粒度”根本没法直接Group By。后来重新清洗把时间戳转成yyyy-MM-dd HH:mm:ss格式并单独抽出一个日期字段。第四CSV文件里字段内容包含换行符和逗号。视频标题本身就可能带逗号如果你直接用字符串拼接去生成CSV行文件解析就会错位。这里必须用Python的csv模块或者Pandas的to_csv让框架自动处理转义。6.2 Hadoop 集群上的三个经典报错第一个是NameNode启动失败。最常见的原因是格式化后修改了配置文件或者重复执行了hdfs namenode -format。如果遇到这种情况保存好已有数据把hadoop_tmp目录清掉重新格式化再启动。第二个是进程都启动了但访问不了Web界面。Hadoop 3.x的NameNode Web端口已经从50070改成了9870ResourceManager的端口变成了8088。如果你照着旧教程访问50070当然什么都看不到。这个问题在答辩现场也很容易被老师提出来提前确认端口很重要。第三个是YARN内存分配失败。默认配置遇到大的MapReduce任务经常报内存不足我那时候把yarn.nodemanager.resource.memory-mb和mapreduce.map.memory.mb都调低了问题才缓解。个人开发环境内存有限不要一上来就配置几十个G。6.3 答辩问答中最值得准备的10个问题很多同学做完项目很自信但一被老师问到“为什么数据量不到一万也要用Hadoop”就卡住了。我把答辩遇到的典型问题整理成一个速查表你们可以直接当参考问题参考回答思路数据量多大为什么用Hadoop数据量目前是近10万条单体数据库也能处理但项目以大数据全流程实践为目标HDFSMapReduce能支撑后续数据增长并且便于扩展到全量数据热门指数权重怎么定的先用文献调研确定指标方向再用熵权法做客观赋权权重会根据数据时间窗口动态更新爬虫合规吗只采集公开接口中展示的数据控制频率不涉及隐私数据和付费内容为什么用Hive不用Spark项目定位为离线分析Hive生态成熟和Hadoop集成度高后续数据量增大可平滑迁移到Spark创作者健康度有什么实际价值可以帮助MCN机构评估UP主合作价值也可以辅助内容推荐决策如果实时分析怎么做引入Kafka和Spark Streaming把离线链路扩展成Lambda架构数据清洗为什么做这么久因为原始字段缺失、类型不一致、重复数据多清洗质量直接影响统计结果如何保证采集系统稳定设置重试机制、动态频率控制、每次采集写入独立文件、定期检查接口变化可视化数据从哪来Hive统计结果会回写到MySQLWeb系统查询MySQL避免直接压HDFS项目最大的创新点是什么把热门指数和创作者测量模型结合实现了从数据采集到指标计算再到可视化的完整链路最后一个问题“项目最大的创新点”听起来像套话但几乎必问。我的建议是不要说自己发明了全新算法而是强调你做了一个“可复现、可扩展、端到端”的分析系统并对指标口径提出了自己的设计方案。最后分享一个我自己做完项目之后特别有感触的经验不要把“能调到接口”当成“会爬虫”也不要把“能启动Hadoop”当成“懂大数据”。这个项目真正的训练发生在你被迫处理脏数据、统一指标口径、把统计结果反推回业务解释的时候。只要答辩时你能讲清楚每个指标怎么算、为什么这样算、换一种算法会有什么影响这个毕业设计就已经达到了它该有的目的。
返回列表