尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python数据工程实战:构建B站视频数据采集与分析系统

Python数据工程实战:构建B站视频数据采集与分析系统 这次我们来看一个关于B站视频播放量排名的数据分析项目。虽然标题本身像是一个盘点类内容但从技术实现的角度这背后涉及数据爬取、清洗、存储、分析和可视化等一系列完整的数据工程流程。对于开发者、数据分析师或是对B站生态感兴趣的技术爱好者而言如何系统性地获取并分析这类平台公开数据是一个极具实践价值的课题。本文将从一个技术实践的角度出发探讨如何构建一个本地化的B站视频数据采集与分析系统。核心不在于讨论具体哪条视频是“镇站之宝”而在于拆解实现这一结论所需的技术栈、工具链和完整工作流。我们会重点关注几个实用问题数据从哪里来合规爬取与API调用数据怎么处理清洗与存储分析结果如何呈现可视化与报告整个过程可以在普通开发机上完成无需特殊硬件重点考察的是代码的稳定性、数据处理的效率以及分析的可复现性。如果你对Python爬虫、数据分析Pandas、数据可视化Matplotlib/Plotly以及轻量级数据库如SQLite的应用感兴趣或者想了解如何构建一个端到端的数据分析项目那么这篇文章会提供一套清晰的实现思路和可操作的代码示例。1. 核心能力速览能力项说明项目类型数据爬取、清洗、分析与可视化系统技术栈Python (Requests, BeautifulSoup, Pandas, Matplotlib/Plotly), SQLite数据来源B站公开页面、官方API合规使用核心功能1. 自动化获取视频列表与元数据2. 数据清洗与结构化存储3. 多维度统计分析播放量、点赞、投币等4. 生成可视化图表与排名报告硬件门槛无特殊要求普通电脑即可运行依赖网络带宽进行数据抓取部署方式本地Python脚本运行支持定时任务输出成果结构化数据表CSV/SQLite、统计图表PNG/HTML、分析报告Markdown适合场景个人技术学习、竞品分析、内容趋势研究、数据工程练手项目2. 适用场景与使用边界这个本地化数据分析系统主要适合以下几类用户Python初学者/数据分析学习者作为一个完整的项目实践涵盖从数据获取到可视化的全流程。内容创作者或运营人员通过分析头部视频的数据特征如标题关键词、分区、时长为自己的内容策划提供数据参考。对B站生态感兴趣的研究者观察平台内容变迁、用户偏好趋势。需要构建数据管道练手的开发者学习如何设计稳定、可复用的数据采集与处理脚本。重要使用边界与合规提醒合规采集所有数据采集行为必须严格遵守robots.txt协议尊重网站服务器的负载能力设置合理的请求间隔如添加延时避免高频请求导致IP被封或对目标服务器造成压力。公开数据仅采集和处理页面公开显示的数据如播放量、点赞数、标题等严禁尝试获取任何非公开、个人隐私或需要登录才能访问的数据。版权与用途分析产生的图表、报告可用于个人学习、技术分享或内部参考。严禁将原始数据或分析结果用于任何商业售卖、诋毁平台或内容创作者、或进行其他非法及侵权活动。数据时效性平台数据实时变化本系统分析结果是基于抓取时刻的快照结论具有时效性。3. 环境准备与前置条件在开始编写代码前需要准备好基础的Python开发环境。基础环境清单操作系统Windows 10/11, macOS, 或 Linux 发行版均可。Python版本建议使用 Python 3.8 及以上版本。包管理工具使用pip进行Python包安装。代码编辑器VS Code, PyCharm 或任何你熟悉的编辑器。网络连接稳定的网络环境用于数据抓取。磁盘空间少量空间用于存储代码、数据和生成的图表。关键Python库我们将使用以下库请通过pip提前安装pip install requests beautifulsoup4 pandas matplotlib plotly sqlite3requests: 用于发送HTTP请求获取网页HTML或API数据。beautifulsoup4: 用于解析HTML提取所需数据。pandas: 数据处理与分析的核心库用于数据清洗、转换和统计分析。matplotlib: 基础绘图库用于生成静态图表如柱状图、折线图。plotly: 交互式绘图库可生成更美观且可交互的HTML图表。sqlite3: Python内置库用于轻量级数据存储。4. 项目结构与数据流程设计在写代码之前先规划好项目目录和数据处理流程这能让后续开发更清晰。推荐的项目目录结构bilibili_data_analysis/ ├── config.py # 配置文件如请求头、数据库路径 ├── crawler.py # 爬虫模块负责数据抓取 ├── data_processor.py # 数据处理模块负责清洗和存储 ├── analyzer.py # 数据分析模块负责统计和计算 ├── visualizer.py # 可视化模块负责生成图表 ├── main.py # 主程序协调各模块执行 ├── data/ # 数据存储目录 │ ├── raw_html/ # 存放原始HTML可选用于调试 │ ├── bilibili.db # SQLite数据库文件 │ └── output/ # 输出目录图表、报告 └── requirements.txt # 项目依赖列表数据处理流程抓取 (Crawl)crawler.py模拟浏览器请求从B站排行榜、搜索页或指定UP主页面获取视频列表的HTML或直接调用公开API。解析 (Parse)使用BeautifulSoup从HTML中提取结构化数据如视频标题、BV号、播放量、点赞、投币、收藏、发布时间等。清洗与存储 (Process Store)data_processor.py将提取的数据进行清洗处理缺失值、统一格式并存入SQLite数据库的表中。分析 (Analyze)analyzer.py从数据库读取数据使用Pandas进行排序、聚合、计算比率如点赞播放比等分析。可视化 (Visualize)visualizer.py根据分析结果调用Matplotlib或Plotly生成排名柱状图、趋势图等。报告 (Report)将分析结论和图表整合生成一份简单的Markdown报告。5. 核心模块代码实现下面我们分模块实现核心功能。请注意以下代码为示例模板实际URL和HTML解析规则需根据B站页面的实际结构进行调整。5.1 配置与数据库初始化 (config.py和 数据库部分)首先定义一些常量和创建数据库表。# config.py import sqlite3 from pathlib import Path # 项目基础路径 BASE_DIR Path(__file__).parent DATA_DIR BASE_DIR / data RAW_HTML_DIR DATA_DIR / raw_html OUTPUT_DIR DATA_DIR / output DB_PATH DATA_DIR / bilibili.db # 确保目录存在 for dir_path in [DATA_DIR, RAW_HTML_DIR, OUTPUT_DIR]: dir_path.mkdir(parentsTrue, exist_okTrue) # 请求头模拟浏览器访问 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://www.bilibili.com/ } # 数据库初始化 def init_database(): conn sqlite3.connect(DB_PATH) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS videos ( id INTEGER PRIMARY KEY AUTOINCREMENT, bvid TEXT UNIQUE, -- 视频BV号 title TEXT, play_count INTEGER, like_count INTEGER, coin_count INTEGER, favorite_count INTEGER, pub_date TEXT, owner_name TEXT, inserted_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) conn.commit() conn.close() if __name__ __main__: init_database() print(f数据库已初始化在{DB_PATH})5.2 数据抓取模块 (crawler.py)这个模块负责获取数据。这里以抓取B站“全站排行榜”单页为例。# crawler.py import requests import time from bs4 import BeautifulSoup from config import HEADERS, RAW_HTML_DIR import json def fetch_rank_page(page_num1, rank_type0): 抓取B站排行榜页面 :param page_num: 页码 :param rank_type: 排行榜类型0为全站其他值需根据实际情况调整 :return: 解析后的视频信息列表 # 示例URL实际地址可能需要通过浏览器开发者工具分析获取 url fhttps://api.bilibili.com/x/web-interface/ranking/v2?rid0typeallpage{page_num} # 或者使用带有参数的动态页面URL这里以API为例 # url “https://www.bilibili.com/v/popular/rank/all” # 旧版页面 try: print(f正在抓取: {url}) response requests.get(url, headersHEADERS, timeout10) response.raise_for_status() # 检查请求是否成功 # 可选保存原始HTML/JSON用于调试 # with open(RAW_HTML_DIR / frank_page_{page_num}.json, w, encodingutf-8) as f: # f.write(response.text) data response.json() video_list [] # 解析JSON结构提取视频信息。此结构为示例实际需根据API返回调整。 if data[code] 0: for item in data[data][list]: video_info { bvid: item.get(bvid), title: item.get(title), play_count: item.get(stat, {}).get(view), like_count: item.get(stat, {}).get(like), coin_count: item.get(stat, {}).get(coin), favorite_count: item.get(stat, {}).get(favorite), pub_date: item.get(pubdate), # 可能是时间戳 owner_name: item.get(owner, {}).get(name), } video_list.append(video_info) else: print(fAPI返回错误: {data[message]}) # 礼貌性延时避免请求过快 time.sleep(1) return video_list except requests.exceptions.RequestException as e: print(f请求失败: {e}) return [] except json.JSONDecodeError as e: print(fJSON解析失败: {e}) return [] if __name__ __main__: # 测试抓取第一页 test_data fetch_rank_page(1) print(f抓取到 {len(test_data)} 条视频信息) if test_data: print(test_data[0]) # 打印第一条看看结构5.3 数据处理与存储模块 (data_processor.py)该模块负责清洗数据并存入数据库。# data_processor.py import sqlite3 from config import DB_PATH def save_videos_to_db(video_list): 将视频信息列表存入数据库忽略重复的BV号 if not video_list: print(视频列表为空跳过存储。) return conn sqlite3.connect(DB_PATH) cursor conn.cursor() inserted_count 0 for video in video_list: # 数据清洗示例确保播放量为整数 play_count video.get(play_count) if isinstance(play_count, str): # 处理“万”、“亿”等单位此处简化处理 play_count int(float(play_count.replace(万, )) * 10000) if 万 in play_count else int(play_count) video[play_count] play_count try: cursor.execute( INSERT OR IGNORE INTO videos (bvid, title, play_count, like_count, coin_count, favorite_count, pub_date, owner_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?) , ( video.get(bvid), video.get(title), video.get(play_count), video.get(like_count), video.get(coin_count), video.get(favorite_count), video.get(pub_date), video.get(owner_name) )) if cursor.rowcount 0: inserted_count 1 except sqlite3.Error as e: print(f插入数据失败 {video.get(bvid)}: {e}) conn.commit() conn.close() print(f数据存储完成新增 {inserted_count} 条记录。) def get_top_videos(limit10, order_byplay_count): 从数据库获取排名靠前的视频 :param limit: 返回数量 :param order_by: 排序字段如 play_count, like_count :return: 包含视频信息的字典列表 conn sqlite3.connect(DB_PATH) # 使用 pandas 直接读取会更方便这里用 sqlite3 演示 cursor conn.cursor() # 注意SQL参数不能用于字段名这里直接拼接实际应用需注意防注入 query fSELECT * FROM videos ORDER BY {order_by} DESC LIMIT ? cursor.execute(query, (limit,)) columns [col[0] for col in cursor.description] results [dict(zip(columns, row)) for row in cursor.fetchall()] conn.close() return results5.4 数据分析模块 (analyzer.py)使用Pandas进行更复杂的分析。# analyzer.py import pandas as pd import sqlite3 from config import DB_PATH def load_data_from_db(): 从数据库加载所有视频数据到Pandas DataFrame conn sqlite3.connect(DB_PATH) # 直接使用Pandas读取SQL df pd.read_sql_query(SELECT * FROM videos, conn) conn.close() return df def perform_analysis(df): 执行一系列分析 if df.empty: print(数据库中没有数据。) return None analysis_results {} # 1. 基本统计 analysis_results[total_videos] len(df) analysis_results[play_mean] df[play_count].mean() analysis_results[play_max] df[play_count].max() analysis_results[play_min] df[play_count].min() # 2. 播放量TOP N top_n_by_play df.nlargest(10, play_count)[[title, play_count, owner_name]] analysis_results[top_by_play] top_n_by_play # 3. 计算互动率 (点赞数/播放量) df[like_ratio] df[like_count] / df[play_count] top_by_like_ratio df.nlargest(10, like_ratio)[[title, like_ratio, play_count, owner_name]] analysis_results[top_by_like_ratio] top_by_like_ratio # 4. 按UP主分组统计 owner_stats df.groupby(owner_name).agg({ play_count: sum, like_count: sum, bvid: count }).rename(columns{bvid: video_count}).sort_values(play_count, ascendingFalse).head(10) analysis_results[top_owners] owner_stats return analysis_results if __name__ __main__: df load_data_from_db() if not df.empty: results perform_analysis(df) print(播放量最高的视频) print(results[top_by_play]) print(\n点赞率最高的视频) print(results[top_by_like_ratio])5.5 数据可视化模块 (visualizer.py)生成静态和交互式图表。# visualizer.py import matplotlib.pyplot as plt import plotly.express as px import pandas as pd from config import OUTPUT_DIR def plot_top_videos_bar(df_top, save_pathNone): 使用Matplotlib绘制播放量TOP N柱状图 plt.figure(figsize(12, 6)) # 简化标题用于显示 short_titles [t[:20] ... if len(t) 20 else t for t in df_top[title]] bars plt.barh(short_titles, df_top[play_count]) plt.xlabel(播放量) plt.title(B站视频播放量TOP N) plt.gca().invert_yaxis() # 让最高的在最上面 # 在柱子上添加数值 for bar in bars: width bar.get_width() plt.text(width, bar.get_y() bar.get_height()/2, f {int(width):,}, vacenter) plt.tight_layout() if save_path: plt.savefig(save_path, dpi300, bbox_inchestight) print(f图表已保存至{save_path}) else: plt.show() plt.close() def plot_interactive_scatter(df, save_path_htmlNone): 使用Plotly绘制播放量、点赞数、投币数的交互式散点图 fig px.scatter(df, xplay_count, ylike_count, sizecoin_count, hover_nametitle, hover_data[owner_name, favorite_count], titleB站视频播放量 vs 点赞数 (气泡大小投币数), labels{play_count: 播放量, like_count: 点赞数}) fig.update_traces(markerdict(opacity0.7)) if save_path_html: fig.write_html(save_path_html) print(f交互式图表已保存至{save_path_html}) else: fig.show() if __name__ __main__: # 示例假设有一个DataFrame df_top # from analyzer import load_data_from_db, perform_analysis # df load_data_from_db() # results perform_analysis(df) # df_top results[top_by_play] # plot_top_videos_bar(df_top, save_pathOUTPUT_DIR / top_videos_bar.png) # plot_interactive_scatter(df.head(100), save_path_htmlOUTPUT_DIR / scatter_plot.html) pass5.6 主程序协调 (main.py)将以上模块串联起来形成完整工作流。# main.py from config import init_database from crawler import fetch_rank_page from data_processor import save_videos_to_db, get_top_videos from analyzer import load_data_from_db, perform_analysis from visualizer import plot_top_videos_bar, plot_interactive_scatter import pandas as pd def main(): print( B站视频数据分析系统启动 ) # 1. 初始化数据库 init_database() # 2. 抓取数据 (示例抓取排行榜前3页) all_videos [] for page in range(1, 4): print(f抓取第 {page} 页...) page_videos fetch_rank_page(page) all_videos.extend(page_videos) # 每抓取一页后可以立即存储也可以全部抓完再存 # save_videos_to_db(page_videos) # 3. 保存数据到数据库 print(f共抓取到 {len(all_videos)} 条原始数据。) save_videos_to_db(all_videos) # 4. 从数据库加载数据并分析 df load_data_from_db() if df.empty: print(未获取到有效数据程序结束。) return analysis_results perform_analysis(df) # 5. 可视化结果 if analysis_results: top_df analysis_results[top_by_play] print(\n【播放量TOP 10】) print(top_df.to_string(indexFalse)) # 生成柱状图 bar_chart_path ./data/output/top_10_play_bar.png plot_top_videos_bar(top_df, save_pathbar_chart_path) # 生成交互式散点图 (使用前100条数据) scatter_html_path ./data/output/play_vs_like_scatter.html plot_interactive_scatter(df.head(100), save_path_htmlscatter_html_path) # 6. 生成简易文本报告 report_path ./data/output/analysis_report.md with open(report_path, w, encodingutf-8) as f: f.write(# B站视频数据分析报告\n\n) f.write(f**分析时间** {pd.Timestamp.now()}\n) f.write(f**数据总量** {len(df)} 条视频记录\n\n) f.write(## 播放量排行榜TOP 10\n) f.write(top_df.to_markdown(indexFalse)) f.write(\n\n) f.write(f![播放量TOP 10柱状图]({bar_chart_path})\n\n) f.write(f**交互式分析图表** [点击查看]({scatter_html_path})\n) print(f\n分析报告已生成{report_path}) print( 数据分析流程执行完毕 ) if __name__ __main__: main()6. 运行与效果验证完成代码编写后按以下步骤验证系统是否正常工作。环境与依赖检查# 在项目根目录下确保已安装所有依赖 pip install -r requirements.txt # requirements.txt 内容 # requests # beautifulsoup4 # pandas # matplotlib # plotly初始化数据库 首次运行前先执行python config.py来创建数据库和目录结构。运行主程序python main.py观察控制台输出应该能看到抓取进度、数据存储提示和分析结果打印。验证输出 程序运行成功后检查以下输出data/bilibili.dbSQLite数据库文件应已创建并包含数据。data/output/top_10_play_bar.png应生成一张播放量TOP 10的柱状图。data/output/play_vs_like_scatter.html应生成一个可交互的散点图HTML文件用浏览器打开可查看详情。data/output/analysis_report.md应生成一份Markdown格式的简要分析报告。功能测试清单[ ] 爬虫模块能否成功获取到数据列表非空[ ] 数据是否被正确清洗并存入数据库无重复、格式统一[ ] 分析模块能否正确计算出播放量TOP N和互动率[ ] 可视化模块能否成功生成PNG和HTML图表文件[ ] 最终的报告文件是否包含了关键数据和图表链接7. 性能优化与扩展思路基础系统运行起来后可以考虑以下优化和扩展方向提升爬虫健壮性异常处理增加更全面的网络异常超时、连接错误和解析异常处理。重试机制对失败的请求进行有限次数的重试。代理池如果需要大规模抓取考虑使用代理IP池避免被封。分布式抓取使用Scrapy框架或Celery进行分布式任务调度。丰富数据维度多数据源不仅抓排行榜还可以抓取特定分区、UP主主页、搜索结果的视频。评论与弹幕通过视频AV/BV号进一步抓取评论和弹幕数据需注意频率和合规性。历史趋势定期如每天抓取数据存入数据库从而分析播放量、粉丝数随时间的变化趋势。深化分析能力文本分析对视频标题进行分词生成词云分析热门关键词。相关性分析计算播放量、点赞、投币、收藏、分享之间的相关系数。预测模型基于历史数据尝试简单的回归模型预测视频潜力需大量特征工程。完善系统架构任务调度使用APScheduler或操作系统定时任务Cron实现每日自动抓取与分析。Web展示使用Flask或Streamlit快速搭建一个内部看板实时展示分析结果。数据导出支持将数据导出为 Excel、PDF 等更多格式。8. 常见问题与排查方法在开发和运行过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案运行python main.py无任何输出或立即退出代码存在语法错误或导入模块失败在命令行分模块单独运行如python crawler.py查看具体报错信息。根据错误提示修复语法、安装缺失的包或检查模块路径。爬虫返回空列表或状态码非2001. 目标URL已变更2. 请求头被识别为爬虫3. 网站需要登录或验证1. 打印response.status_code和response.text前500字符。2. 使用浏览器开发者工具对比自己请求和浏览器请求的Headers差异。1. 更新URL或解析逻辑。2. 完善HEADERS添加Cookie、Referer等关键字段需合规获取。3. 检查是否有反爬机制如验证码考虑使用Selenium模拟浏览器更复杂。数据库写入失败或数据重复1. 数据库连接错误2. 表结构不匹配3. UNIQUE约束冲突1. 检查DB_PATH是否正确是否有写入权限。2. 检查INSERT语句的字段数与值的数量是否匹配。3. 查看具体SQLite错误信息。1. 确保数据库文件所在目录存在。2. 使用INSERT OR IGNORE或INSERT OR REPLACE处理重复数据。3. 在插入前打印数据检查格式。生成的图表是空白或乱码1. 绘图数据为空或格式错误2. 中文字体缺失1. 检查传递给绘图函数的数据框df是否为空列名是否正确。2. 在Matplotlib中设置中文字体。1. 在绘图前打印数据框形状df.shape和前几行df.head()。2. 添加字体设置代码plt.rcParams[‘font.sans-serif’] [‘SimHei’, ‘DejaVu Sans’]plt.rcParams[‘axes.unicode_minus’] False程序运行一段时间后被断开或IP被封请求频率过高检查代码中time.sleep()的间隔时间是否太短。增加请求间隔如2-5秒并尽量在访问低谷时段运行脚本。模拟人类浏览行为。Plotly生成的HTML图表在浏览器中不显示离线模式需要初始化未在非Jupyter环境下正确初始化Plotly离线模式。在生成图表前添加import plotly.io as pio; pio.renderers.default ‘browser’或确保输出为HTML文件。9. 最佳实践与使用建议从小规模开始首次运行先抓取1-2页数据确保整个流程抓取、解析、存储、分析、绘图全部跑通再扩大抓取范围。数据备份定期备份bilibili.db数据库文件。对于重要的原始HTML或JSON数据也可以考虑压缩存档。日志记录使用Python的logging模块替代print将程序运行状态、错误信息记录到文件便于后期排查。配置化管理将需要经常修改的参数如请求间隔、抓取页数、数据库路径、图表样式集中放在config.py或配置文件中。尊重平台规则这是最重要的原则。清晰区分技术学习与滥用之间的界限。本项目的代码和思路仅用于教育目的演示如何构建一个数据管道。关注数据质量定期检查数据是否完整、有无异常值如播放量为负数。在分析前进行必要的数据清洗。10. 总结通过这个完整的项目实践我们不仅回答了“如何找出B站播放量最高的视频”这个具体问题更重要的是掌握了一套构建本地化数据采集与分析系统的通用方法。从使用Requests和BeautifulSoup抓取数据到用Pandas进行灵活分析再到通过Matplotlib和Plotly实现可视化最后用SQLite持久化存储这套技术栈组合拳足以应对许多中小规模的数据处理需求。这个项目的最大价值在于其可扩展性。你可以轻易地修改数据源例如分析知乎、豆瓣、GitHub趋势增加分析维度例如加入情感分析、聚类或者改变输出形式例如搭建自动化报告邮件系统。它为你提供了一个坚实的起点而非一个固定的答案。下次当你再看到类似“XX平台最高播放视频”的盘点时不妨思考一下如果让你用代码来验证或发现这个结论你会怎么做这个项目或许就是你的第一块敲门砖。建议收藏本文的代码框架在需要处理类似公开数据问题时可以快速搭建起属于你自己的分析工具。
返回列表