B站动漫爬虫全流程:从requests+xpath采集到可视化看板

发布时间:2026/10/12 16:07:56
B站动漫爬虫全流程:从requests+xpath采集到可视化看板 简介面向Python爬虫与数据分析初学者的B站动漫数据项目以哔哩哔哩番剧为对象演示从总榜爬取动漫列表、访问详情页抓取追番人数与评分再到数据清洗、统计分析与可视化的完整流程适合作为爬虫与数据分析方向的入门实战参考。压缩包共23个文件体积约3.58MB包含3个交互式笔记代码文件、2份表格数据、10张可视化图片、1份说明文档及少量配置类文件覆盖爬取、预处理、分析三条主要环节能直观看到不同步骤的产出。目前已有2978人学习下载。借助这套资料读者可同时获得可运行代码、真实抓取数据和成图结果结合文档中对请求解析、字段抽取、数据清洗、图表绘制等细节的介绍能较快理解一类番剧数据项目的实现思路节省自行搭建和排错的时间。1. 先想清楚B站动漫爬虫项目到底要抓哪些数据想给B站动漫做数据分析第一个能落地的选题就是爬番剧列表标题、评分、播放量、追番人数、平台归属和动漫类型一个周末能出结果。别被“爬虫”两个字吓住这个项目的难点根本不在反爬而在数据清洗和口径选择——B站接口返回的score是一个嵌套字典play字段可能是字符串评分0的番剧到底算“没开分”还是“真烂”这些才是真正花时间的地方。适合三种人第一次写完整爬虫的新手、想把数据分析技能用在真实场景的学生、想观察二次元内容走势的从业者。下面我把从请求到可视化的完整路径拆给你照着跑晚上就能看到第一张B站动漫数据图表。2. 用requestsxpath抓B站动漫数据请求、解析与翻页参数2.1 为什么选requestsxpath而不是scrapy说到爬虫很多人的第一反应是scrapy但遇到这个项目我一般会劝新手先别上框架。scrapy的学习曲线体现在项目结构、Item Pipeline、Middleware 这些概念上而你要抓的不过是B站动漫索引这一个站点、几十个字段、几百条数据。用requests加xpath三步就能跑通发请求、拿响应、解析字段。等量级大到要天天跑、要防封、要分布式采集时再换scrapy也不迟。方案适用场景上手成本主要坑点requests xpath小批量、单站点、字段结构稳定低重试和频率得自己管scrapy大规模、多站点、需要长期维护中框架概念多调试成本高playwright页面要执行JS才能渲染高内存占用大运行慢选择逻辑很简单B站动漫索引页在服务端渲染了大部分列表内容HTML里能直接拿到标题和链接同时它的JSON接口也能返回格式化的字段。用requests就是把网络爬虫原理里的“请求—响应—解析”三步走完xpath负责从HTML静态结构里把节点摘出来。接口变动时还能切到JSON方案兜底两条路都不丢。2.2 抓取热播番剧列表请求接口的完整代码与参数说明我先给出用JSON接口抓取番剧列表的完整写法。B站有一个半开放的番剧索引接口返回的是结构清晰的JSON比从HTML里用xpath抠文本要稳。第一次请求时先把响应打出来看结构这步很重要后面所有清洗逻辑都建立在字段名正确的前提上。import requests from requests.adapters import HTTPAdapter import time session requests.Session() session.mount(https://, HTTPAdapter(max_retries2)) session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0 Safari/537.36, Referer: https://www.bilibili.com/anime/, Origin: https://www.bilibili.com, }) API_URL https://api.bilibili.com/pgc/season/index/result params { season_type: 1, # 1 代表番剧 type: sort, # 排序请求 sort_type: 2, # 2 按追番人数排序 page: 1, pagesize: 20, # 每页返回条数 } resp session.get(API_URL, paramsparams, timeout10) resp.raise_for_status() data resp.json() # 先看返回结构确认字段名再写后续逻辑 print(data[code], data[message]) print(data[data][list][0])这段代码里有三个地方新手容易忽略。第一session.mount配合HTTPAdapter可以把超时重试交给底层处理不用自己写try except套循环第二User-Agent和Referer是B站服务端识别请求来源的重要依据少了Referer很容易拿到412或403响应第三params里的season_type和sort_type决定你抓的是“番剧”还是“电影”是“按播放量排”还是“按追番数排”。先打印第一页数据确认score是字典还是数字确认platform存在与否再往下写。2.3 翻页、请求头与超时重试稳定采集三件套单页数据撑不起分析翻页才是采集的主体。翻页时有个血泪经验不要for page in range(1, 100)一口气跑完控制请求频率不是玄学是给服务端留响应时间也是给自己留后退的余地。每请求一页sleep 0.5到1秒看起来慢实际上20页只需要几十秒远比触发风控后换IP来得省事。def fetch_anime_page(page: int): params[page] page resp session.get(API_URL, paramsparams, timeout10) resp.raise_for_status() payload resp.json() if payload[code] ! 0: # 接口返回异常时先停下来看 message不要盲目重试 print(fpage {page} return code: {payload[code]}, msg: {payload[message]}) return [] items payload[data][list] rows [] for item in items: rows.append({ title: item.get(title), score: item.get(score), # 这里可能是 dict后面再拆 play: item.get(play), follow: item.get(follow), type_name: item.get(type_name), platform: item.get(platform), }) return rows all_rows [] for page in range(1, 11): # 先抓前10页200条 rows fetch_anime_page(page) print(fpage {page}: got {len(rows)} rows) all_rows.extend(rows) time.sleep(1) # 请求间隔控制频率代码逻辑不复杂但有两个设计取舍值得讲。第一fetch函数内部先判断payload[code]code不等于0时直接返回空列表并打印message。B站索引接口在请求过密时会返回风控提示把code打印出来能帮你区分“我方请求有问题”和“服务端拒绝服务”避免在错误方向上加戏。第二rows里统一用item.get()而不是item[]这样字段缺失时返回None而不是抛KeyError后面pandas清洗会省很多事。如果你遇到接口完全不可用另一个常见做法是用requests抓HTML再用lxml的etree和xpath解析列表节点核心区别只是在解析层请求层的session逻辑可以原样复用。3. 数据清洗与存储把JSON响应整理成能分析的DataFrame3.1 原始字段里哪些能用、哪些要拆开用抓回来的原始数据是列表套字典的结构直接塞进DataFrame之前先看清每个字段的“真实身份”。我在第一次跑这个项目时犯过一个典型错误天真地以为score是一列数字结果DataFrame里那一列全是字典对象排序直接翻车。B站接口里的score实际是嵌套结构包含score和user_count两个子字段必须提前拆开。原始字段实际类型分析用途清洗动作titlestr番剧名称主键去重、去空格playint 或 str播放量转数值处理非数字字符followint追番人数转数值scoredict 或 int评分和评分人数拆出score子字段type_namestr动漫类型如“搞笑”“战斗”分组聚合platformstr播出平台统计平台分布拆字段的操作其实在爬虫环节就能做。每一条item经过清洗后只保留分析需要的维度不要贪多。尤其是platform这个字段部分番剧是B站独播部分是多平台联播后面做类型分析时它是个很容易被忽略的维度。数据是黑匣子你存进去什么分析时就只能看到什么提前把字段拆干净比事后补救便宜。3.2 空值、字符串数字和评分0pandas清洗三板斧数据落地到pandas后第一件事不是画图是看df.info()和df.isna().sum()。这两个命令能告诉你哪些列有空值、哪些列类型不对。play和follow是数字但如果接口在某些情况下返回了带逗号的字符串排序时就会按字典序排1万播放会被排在2千后面。import pandas as pd df pd.DataFrame(all_rows) print(df.info()) # 字符串数字统一转成数值转不动的置为 NaN df[play] pd.to_numeric(df[play], errorscoerce) df[follow] pd.to_numeric(df[follow], errorscoerce) # score 是字典的拆出真正分数直接是数值的保留 def extract_score(value): if isinstance(value, dict): return value.get(score) return value df[score] df[score].apply(extract_score) df[score] pd.to_numeric(df[score], errorscoerce) # 去掉标题为空的记录保留数据完整的行用于评分分析 df_dedup df.drop_duplicates(subset[title]) df_dedup df_dedup.dropna(subset[title, play]) print(df_dedup.head()) print(df_dedup.isna().sum())这段清洗有两个参数值得讲清楚。pd.to_numeric里的errorscoerce作用是把无法解析的字符串置成NaN而不是抛异常这是处理脏数据的标准姿势你永远不知道接口里会出现什么奇怪的垃圾值。score的提取用isinstance判断类型兼容“接口返回字典”和“已经优化成数值”两种情况避免接口一变代码就报废。drop_duplicates和dropna的目的是保证后续排序、聚类的数据干净标题去重按番剧名播放量缺失的行直接丢弃因为排行分析里缺播放量就没有意义。3.3 存CSV还是SQLite按你的分析目标选清洗完的数据总要落盘。两种选择CSV适合一次性的分析流程打开即用Excel也能看SQLite适合你后面要反复查、要做条件筛选的场景。如果你只是写完这篇就交差CSV就够了如果你想在周末里反复调整分析逻辑SQLite的体验会好很多尤其数据量超过几千条时SQL的聚合效率远超一遍遍跑pandas。# 方案一CSV注意编码要用 utf-8-sig df_dedup.to_csv(bilibili_anime.csv, indexFalse, encodingutf-8-sig) # 方案二SQLite方便后续 SQL 查询 import sqlite3 conn sqlite3.connect(bilibili_anime.db) df_dedup.to_sql(anime, conn, if_existsreplace, indexFalse) conn.close()这两个参数是踩坑后才懂的to_csv的encodingutf-8-sig专门解决Excel打开中文乱码的问题稍后避坑章节会细说to_sql的if_existsreplace解决重复跑脚本时表已存在的问题replace会直接重建表适合分析阶段的反复迭代。至于列类型pandas会自动推断int列存成INTEGERfloat列存成REAL不需要手工建表省一步是一步。4. 数据分析播放量、评分和类型分布怎么算4.1 播放量TOP10与追番人数排行数据干净了分析才有底气。第一个必出的分析是播放量排行找头部番剧。这里有个细节nlargest比sort_values().head()更直接它在底层做了优化而且只对单列做排序不会打乱其他列的顺序。top10_play df_dedup.nlargest(10, play)[[title, play, follow, score]].copy() print(top10_play.to_string(indexFalse)) top10_follow df_dedup.nlargest(10, follow)[[title, follow, play, score]].copy() print(top10_follow.to_string(indexFalse))排行表出来后第一个结论往往就藏在对比里。你会发现播放量最高的番剧不一定追番人数最多有的番剧是“路人盘大”点开看一眼但不追番有的番剧是“粉丝粘性高”追番人数远超播放量。这两个指标一对比就能看出内容的出圈属性和粉丝属性。分析时不要只看绝对数把play和follow都打印出来再算一列“播放追番比”这个比值是内容运营里很常见的健康度指标。4.2 评分分布、类型结构与数据口径评分是这个项目里最容易翻车的维度。我的做法是先把评分切成区间看整体分布再按动漫类型分组看均值。pd.cut的bins参数决定着分的颗粒度B站番剧评分普遍偏高8分以下并不多见所以bins要按7.0到10.0的区间切切5分以下没有实际区分度。# 评分分箱看分布 bins [0, 6, 7, 8, 9, 10] labels [0-6, 6-7, 7-8, 8-9, 9-10] df_dedup[score_bin] pd.cut(df_dedup[score], binsbins, labelslabels, rightFalse) score_dist df_dedup[score_bin].value_counts().sort_index() print(score_dist) # 按动漫类型统计播放量groupby 后 agg 一次算三个指标 type_stats ( df_dedup.groupby(type_name)[play] .agg([sum, mean, count]) .sort_values(sum, ascendingFalse) ) print(type_stats.head(10))这段分析有两条逻辑要说明。第一pd.cut的rightFalse表示区间左闭右开8会进到8-9而不是7-8这个参数不设的话默认是右闭边界数据会分到意外的桶里。第二groupby之后用agg一次算总和、均值、条数sum体现类型总盘mean体现单部平均水平count体现作品数量三个数字并排看才能判断一个类型是“大而稀”还是“小而精”。至于波动大的分析结论先确认你的数据口径是“当前累计播放量”还是“某时间段增量”——B站公开接口返回的是累计值说“播放量”之前心里得有数。4.3 把分析结论收敛成可视化指标清单分析做得再深最后都是为了输出。我一般会在动手画图前先列一张指标和图表类型的对照表想清楚“谁要看什么”。要回答的问题核心指标推荐图表哪些番剧播放最猛play柱状图TOP10追番粘性和播放量是否一致play/follow比散点图评分高的番剧集中在哪个类型score均值 type_name箱线图或柱状图B站独播还是多平台联播占主流platform 占比饼图这张表的价值在于让你不盲目堆图表。数据分析的方法不是图越多越好而是每个图都要对应一个决策或一个判断。播放量TOP10用柱状图平台分布用饼图评分分布用箱线图。下一步画图时就有明确目标了尤其是做可视化大屏时指标清单能帮你挡住“再加个图会显得很厉害”的冲动。5. 避坑B站爬虫里的403、字段变动和编码问题排查5.1 请求被拒403UA、Referer与请求频率现象前两页抓得好好的第三页开始返回403随后连第一页都打不开。原因请求频率太快加上请求头里Referer缺失或错误。B站服务端会把“无Referer的Python请求”和“高频同IP请求”都识别为异常访问。解决给session配齐浏览器级的User-Agent和Referer每页间隔加到1秒以上。请求节奏上做退避而不是硬碰硬遇到403先停30秒再继续比一直重试有效。另外一个常用做法是把页码顺序打乱尤其抓几十页时从中间页开始抓能降低连续翻页的行为特征。5.2 xpath解析返回空列表先看响应再排查现象写好的xpath表达式在浏览器里能定位到节点代码里运行却一直返回空列表。原因B站的动漫索引页有些板块走接口后在客户端渲染HTML源码里根本没有你要的节点还有一种是class名带了动态变化的hash后缀每次请求都不一样。解决遇到空列表第一件事不是改xpath而是打印resp.text前500个字符看响应体里到底有什么。如果确定是接口渲染直接换JSON接口方案如果只是class动态变化用xpath的contains函数模糊匹配比如//p[contains(class,score)]而不是写死完整class名。5.3 评分全是0或NaN取字段与类型转换的坑现象清洗完的DataFrame里score列要么是0要么是NaN没有一个正常分数。原因接口返回的score是字典直接当成数值读出来自然是错另外部分未开分或评分人数太少的番剧接口给的score就是0。二者混在一起如果不处理分析时会把“没评分”和“低分差评”混为一谈。解决在爬虫解析层就用isinstance判断字典就取出score子字段数值就原样保留。分析时用布尔掩码区分两类数据df[df[score] 0]做评分分析df[df[score].isna()]单独看未开分作品占比。这个区分直接影响结论的准确性。5.4 CSV用Excel打开乱码编码选utf-8-sig现象CSV文件用Notepad看正常用Excel打开中文全是乱码。原因Excel打开CSV时默认按GBK解码而pandas默认用utf-8写入编解码不一致就乱了。解决写入参数改成encodingutf-8-sig。utf-8-sig会在文件开头写入BOM标记Excel就能自动识别为UTF-8编码。这个问题几乎每个入门者都会遇到早改早省心。5.5 翻页超过50页就没有数据接口页数上限现象循环翻到第50页左右返回的list为空但code还是0程序没有报错。原因B站索引接口对单次排序条件下能翻的页数有限制超过深度后返回空列表同时连续高频翻页也会触发限流。解决不要执着于“一页页翻到底”。要么增大pagesize到50减少请求次数要么换一个排序条件从另一个维度重新抓比如先按追番人数抓前50页再按播放量抓前50页。数据采集讲究用不同路径拼全景单一路径是有边界的。6. 用pyecharts做可视化落地从单图到HTML看板6.1 汇报场景用柱状图起手pyecharts是ECharts的Python封装画完直接生成HTML文件浏览器就能打开不需要搭建前端环境。对数据分析与可视化这个场景来说这是最省事的落地方式。柱状图是汇报场景的默认起手式因为排行信息的传达效率最高。from pyecharts.charts import Bar from pyecharts import options as opts top10 df_dedup.nlargest(10, play) bar Bar() bar.add_xaxis(top10[title].tolist()) bar.add_yaxis( 播放量, top10[play].tolist(), label_optsopts.LabelOpts(positionright), ) bar.set_global_opts( title_optsopts.TitleOpts(titleB站新番播放量TOP10), yaxis_optsopts.AxisOpts(name播放量), ) bar.set_series_opts(itemstyle_optsopts.ItemStyleOpts(color#fb7299)) bar.render(bilibili_anime_top10_play.html)有两点细节我是吃过亏后养成的习惯。add_xaxis和add_yaxis都接收list类型pandas的Series要先tolist()不然图表上会出现索引错位set_global_opts里的yaxis_opts用来改坐标轴名称不要省后期看图的同事不会知道你脑子里想的是播放量。颜色改成B站主色 #fb7299汇报时观感会好很多。6.2 多图合成一个HTML看板单张图只能回答问题的一个侧面把柱状图、饼图、表格拼进同一个HTML文件就形成了最简版可视化大屏。pyecharts的Page组件可以串起多个图表配套的SimplePageLayout会自动做上下布局不需要写HTML模板。from pyecharts.charts import Bar, Pie, Page import pandas as pd platform_stats df_dedup[platform].value_counts() pie Pie() pie.add_xaxis(platform_stats.index.tolist()) pie.add_yaxis(平台占比, platform_stats.values.tolist()) pie.set_global_opts(title_optsopts.TitleOpts(title播出平台分布)) page Page(layoutPage.SimplePageLayout) page.add(bar, pie) page.render(bilibili_anime_dashboard.html)Page组件会把bar和pie两个图表对象依次渲染进一个HTML文档里layout参数控制排列方式。做这种组合看板时我的教训是图表数量控制在三到四个以内指标清单里没有对应分析结论的图不要加。爬虫和数据清洗占掉了这个项目七成的时间可视化的价值在于让分析结论一眼被看懂而不是把图表堆成花架子。第一次跑通后你把pagesize改大、把season_type换成别的分类整套流程可以原样复用到B站电影和纪录片板块。数据分析需要学哪些最重要的不是工具数量而是从抓取到出结论这整条链路你都亲手走过一遍后面换任何数据源都只是微调参数的事。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询