尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
用Python爬虫分析动漫数据:从采集到可视化的完整实践
1. 为什么我要爬动漫数据而不是直接拿现成API1.1 项目从一个小问题开始这个项目的起因特别简单我在追番的时候总想找一个补番参考清单——什么值得看、什么类型口碑好、哪一年的番普遍质量高。按理说这种需求有现成的动漫数据库网站也有公开的API可以调直接拉数据做分析就行。但真正动了手才发现事情没那么顺。我最初基于python做过一次小范围尝试用requests去调几个公开API数据是能拿到但限制很尴尬部分接口有请求频率限制匿名调用每天只有几十次有些接口对字段做了裁剪比如拿不到细分的类型标签、制作公司、首播年份这些我分析时最需要的维度还有的平台反代套得很厚个人脚本想稳定调用得先处理一堆鉴权流程。与其在接口上跟人讨价还价不如直接基于python对动漫数据做爬取和分析从公开页面里把数据抓下来存到本地想怎么拆就怎么拆。所以这个项目的定位非常明确用Python爬虫抓取动漫信息列表、评分、类型、集数、首播年份等公开字段然后通过pandas做清洗和分析再用matplotlib把结果可视化。整套方案不依赖任何付费接口只要目标站点的页面结构不频繁变动就能稳定复现。1.2 数据源选型公开API与网页爬虫的取舍我第一版方案是用公开API主要看中了它的数据规整。但很快我就发现一个容易被忽略的问题API给的是平台想给你的不是你想分析的东西。比如我想按季度对比不同动画制作公司的口碑波动API文档找半天发现根本没有制作公司字段再比如我想把科幻和机战这两个标签拆开做交叉分析API返回的标签字段一团糟。换回网页爬虫之后情况反过来了。网页里呈现的信息虽然杂但内容反而是最完整的——一个评分详情区块至少包含名称、集数、首播年份、标签列表、评分人数、剧情简介有些还把制作公司、导演都列出来了。只要选择器写得准能拿到的字段比API多得多。当然我也不是完全否定API。如果你的目标数据恰好都在官方接口里且调用量不大直接调API是最省时的。但当需求涉及非标准字段跨平台对比榜单之外的冷门条目时网页爬虫的灵活性就体现出来了。我的建议是能API最好API给不全就爬爬的时候把页面当API用。2. 采集层设计requests加BeautifulSoup搞定绝大多数静态页面2.1 先观察页面结构再写爬虫代码很多新手一上来就写爬虫结果selector路径是猜的跑一次报一次错。我习惯先把目标页面用浏览器打开F12看DOM结构把需要抓取的字段对应到具体的HTML元素上。以典型动漫评分站点为例榜单页通常是一个列表页每条番剧信息被包在类似li classitem的标签里内部包含标题、链接、评分、信息摘要等。这里有两种常用的解析方案BeautifulSoup配合CSS选择器适合中小型项目代码直观、易调试。Scrapy配合XPath适合需要大规模抓取、断点续爬、中间件扩展的场景。我这个项目目标数据量大概在几千条到两万条用requests加BeautifulSoup完全够没必要上Scrapy。而且BeautifulSoup对结构不规整的页面容忍度高很多老站点的HTML写得混乱lxml解析器配合起来依然能稳定工作。2.2 核心采集代码与字段提取我的采集脚本结构很简单先请求列表页解析出每条番剧的详情页URL再逐个请求详情页提取详细字段最后把所有记录追加到本地CSV文件中。下面是一段核心示例结构参考常见动漫站点的列表页import time import random import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } def parse_list_page(html): 从列表页解析出番剧条目名称、链接、评分、标签文本 soup BeautifulSoup(html, lxml) items [] for li in soup.select(li.subject_item): title li.select_one(h3 a) if not title: continue name title.get_text(stripTrue) detail_url title.get(href) score_node li.select_one(span.rating) score score_node.get_text(stripTrue) if score_node else # 信息摘要里通常包含年份、集数等字符串后续清洗 info_node li.select_one(div.info) info_text info_node.get_text( , stripTrue) if info_node else items.append({ name: name, url: detail_url, score: score, info_text: info_text, }) return items def fetch_page(url): 请求页面并自动嗅探编码防止中文乱码 resp requests.get(url, headersHEADERS, timeout10) resp.encoding resp.apparent_encoding if resp.status_code ! 200: raise RuntimeError(f请求失败: {url} 状态码 {resp.status_code}) return resp.text这里有几个细节值得展开讲。第一resp.encoding resp.apparent_encoding这行代码很重要。很多动漫相关站点用的还是老式GBK或GB2312编码直接在代码里写死resp.encoding utf-8必乱码。requests的apparent_encoding会根据页面内容字节分布去嗅探编码虽然偶尔会判断成ISO-8859-1但在实际项目中绝大多数情况下能正确识别比手动改编码省心。第二CSS选择器最好自己写。用浏览器复制选择器功能虽然快但有些复制的路径太长含一堆div:nth-child(3)这种僵硬结构页面稍微改版就崩。我建议只锁定有明确语义的class和id比如h3 a、span.rating、li.subject_item这类选择器抗页面变动能力强很多。2.3 反爬策略频率控制、请求头、随机等待爬小众站点一般不会被太多反爬拦截但如果目标站点有一定流量防护还是得做一些基础应对。我处理的思路按风险从低到高排列设置合理的User-Agent。不要用requests默认的python-requests/x.x一眼就能识别为脚本。控制请求频率。每请求一个详情页后随机休眠0.5到1.5秒避免短时间内打大量请求。带上Referer和Cookie。部分站点的列表页和详情页之间有简单防盗链校验带上Referer能规避。def crawl_with_pause(detail_url): time.sleep(random.uniform(0.5, 1.5)) # 随机等待避免请求节奏过于机械 return fetch_page(detail_url)如果你发现目标站点频繁返回403、418那就不要硬扛了。先把请求频率降到3秒一次如果还被拦截说明对方启用了签名参数或浏览器指纹校验这种情况下要么换数据源要么改用无头浏览器方案。记住爬虫的本质是模拟普通用户访问访问行为越像人被拦截的概率越低。另外我强烈建议把爬到的原始数据先原样落盘不要急着清洗。原因很直接页面结构一变重爬成本高数据多爬一次就多一次被封风险。原始落盘的CSV文件就是你的数据保险单。3. 数据清洗与入库采集完才是真正考验的开始3.1 为什么不直接用爬下来的字符串做分析爬下来的数据一定不能直接做分析这个坑我踩过不止一次。列表页抓到的评分可能是个带空格的字符串也可能带了暂无评分这类说明年份可能藏在2008年4月这样一句话里集数可能写作全12话也可能写全12(共12話)字符形态还不一样。这些问题单独看都不大但汇总到一个数据集里就会让平均分、相关系数这些统计指标全部失真。所以清洗环节不是可选项是必选项。清洗的目标是把每个人类可读的字符串转成结构化字段并明确每个字段的取值范围和缺失值规则。3.2 清洗规则的设计思路我用pandas做清洗核心逻辑是三步提取、类型转换、范围校验。import pandas as pd df pd.read_csv(anime_raw.csv, index_col0) # 从信息文本中提取年份匹配 2008年、2008-04 等常见格式 df[year] df[info_text].str.extract(r(20\d{2}|19\d{2})).astype(float) # 从集数字段提取数字兼容 全12话、12集、2季 df[episodes] df[info_text].str.extract(r全?(\d)).astype(float) # 评分转浮点并过滤非法范围 df[score] df[score].astype(str).str.extract(r(\d\.?\d*)).astype(float) df.loc[df[score] 0, score] None df.loc[df[score] 10, score] None这里用正则提取而不是直接切字符串是因为动漫站点的信息文本格式太杂正则表达式的容错性最好。str.extract只提取第一处匹配的数字如果出现12话和共2季同时存在的情况它会匹配到第一个数字之后还得按需调整规则。有一个容易被忽略的问题年份中的空值。列表页里部分老番、剧场版条目会缺年份字段如果直接丢弃分析样本会缩水。我当时的做法是单独维护一份缺失值清单去详情页补爬补不回来的才标记为None。这个逻辑在脚本里体现为清洗后输出两份文件一份是清洗好的主数据集一份是清洗失败或缺失字段的待补记录。3.3 存储选型为什么我选了SQLite而不是CSV数据量在几万行以内很多人习惯用CSV我自己第一版也是CSV。但后来发现当我需要按标签查询、多字段组合筛选时CSV的处理效率明显下降。如果直接把URL字段、标签字段都塞进CSV字段里一旦有逗号、换行符CSV的引号转义会搅得一团糟。于是我换成了SQLite。原因很简单Python标准库自带sqlite3零额外依赖。支持索引按年份、评分范围查询很快。支持事务批量插入和更新不容易丢数据。后续如果规模扩大可以直接迁移到MySQL或PostgreSQL。存储代码大致是这样import sqlite3 conn sqlite3.connect(anime.db) df.to_sql(anime, conn, if_existsreplace, indexFalse)如果你更习惯表格数据工作流也可以继续用CSV或Parquet这取决于你在分析阶段更依赖pandas还是SQL。我的经验是单机分析项目用SQLite非常顺手分析层既可以用pandas读表也可以用SQL做聚合两头兼顾。4. 数据分析从评分分布、番剧类型、制作公司看行业门道4.1 评分分布先画出整体画像清洗完数据后我做的第一个分析是评分分布。这一步几乎是所有数据分析项目的起点它能最快暴露数据质量问题也能给后续分析提供整体参照。import pandas as pd import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False df pd.read_sql_query(SELECT * FROM anime, sqlite3.connect(anime.db)) score_valid df[score].dropna() fig, ax plt.subplots(figsize(10, 6)) ax.hist(score_valid, bins20, color#4472C4, edgecolorwhite) ax.set_xlabel(评分) ax.set_ylabel(番剧数量) ax.set_title(番剧评分分布) plt.savefig(score_distribution.png, dpi150)我跑出来的分布并不是想象中的中间高、两边低而是明显左偏——大量番剧评分集中在6到8分之间9分以上的占比很小低于4分的反而很少。这说明两个问题一是评分平台本身就存在评分通胀现象用户打分时容易往高了给二是很多低分冷门番根本没人看压根没有收录数据。这种分布特征直接影响了后续分析策略。如果直接用原始评分做线性回归结果容易被高分头部数据带偏。我后来在部分分析里对评分做了分桶处理分成低分档0-5中分档5-7高分档7-10按档位统计占比比直接比较平均值更稳定。4.2 类型维度的交叉分析我抓的数据里有一个类型标签字段通常是类似冒险、奇幻、战斗这样用顿号或逗号分隔的文本。做交叉分析前要先做拆分成多行或多列的处理。# 将类型字段拆分成数组再展开成多行 df[types] df[tags].str.split([、,/]) df_exploded df.explode(types) # 每种类型的数量与平均分 type_stats df_exploded.groupby(types)[score].agg([count, mean]) type_sorted type_stats[type_stats[count] 30].sort_values(mean, ascendingFalse)这个分析的结果很有意思。印象中治愈和日常两类番剧的平均分非常高而机甲和热血虽然数量不小平均分却偏低。这不一定代表制作水平差异更可能是因为受众评分习惯不同治愈系受众的情感打分倾向和热血受众的爽番就完事心态天然不一样。做这种交叉分析时建议同时看两个指标平均分和样本量。只看平均分会误导人因为样本太少的类型平均分波动极大。我给自己定的标准是至少要有30个样本才纳入分析范围低于30个的归入其他。4.3 年份与集数的关系一个反直觉的发现另外一个我比较关注的角度是首播年份对评分和集数的影响。看过不少行业讨论说现在的番越来越短了我用数据验证了一下。year_stats df.groupby(year)[episodes].agg([median, count]) year_score df.groupby(year)[score].mean()结论没有想象中那么夸张。近十年的番剧集数中位数确实从26集降到了12集但主要原因是季番模式成为主流。不过如果把半年番、年番单独筛出来集数分布并没有显著缩短。更有意思的是年份和评分的相关性我计算了一下近五年的新番平均分反而比十年前略高。原因可能是评分平台用户的构成变化也可能是这几年制作委员会更懂口碑营销用高完成度短篇故事取胜。这一块数据告诉我别轻易相信新番不如老番这类直觉拿年份分组算一算结论往往和想象不一样。5. 可视化呈现让分析结果直接能讲成故事5.1 matplotlib绘图实践与中文乱码处理数据分析的结果最终是要给人看的。我用的可视化工具是matplotlib加pandas的绘图接口没有额外装seaborn因为当前项目的图表量不大matplotlib足够而且seaborn的样式反而会让部分图表的信息密度下降。画图最烦的就是中文乱码。解决方案是在代码最前面强制设置字体# Linux环境常见写法 plt.rcParams[font.sans-serif] [WenQuanYi Micro Hei] # Windows环境常见写法 plt.rcParams[font.sans-serif] [SimHei] # 防止负号显示成方块 plt.rcParams[axes.unicode_minus] False注意不同操作系统可用的中文字体不一样如果你在服务器上跑系统里没有SimHei设置也无效。建议画图前先执行fc-list :langzh查看系统装了哪些中文字体选择一个实际存在的字体名填进去。5.2 图表组合一份完整的分析输出长什么样我最后产出的图表包含四类评分分布直方图用于展示整体口碑结构类型平均分横向条形图用于对比不同题材的口碑表现年份-平均分折线图用于呈现口碑随时间的波动趋势评分与集数的散点图用于快速判断是否存在线性相关。这些图表并不是孤立存在的我在整理项目文档时每张图都配了一段这个图说明了什么的文字。比如评分分布那张图我的说明是整体评分呈左偏分布6-8分段竞争最激烈能被用户打出9分以上的番在数据集中不足3%。这样的表达能把图表和业务结论绑在一起而不是光丢一张图让人猜。5.3 从图表到结论的注意事项可视化的关键不是画得多花哨而是能让人一眼看到核心差异。我踩过的一个典型坑是Y轴范围设置不当。有些图默认的Y轴起点不是0会放大微小差异看起来惊天动地实际数值差距极小。比如年份平均分折线图2012年均分7.42018年均分7.7用默认的Y轴范围画折线图波动幅度会显得很大但本质上只是0.3分的差距。我后来统一把这类图表的Y轴起点设为0或者在人为主观强调差异时在图标题里明确标注差异幅度仅为0.3分避免误导读者。6. 踩坑实录编码、动态加载、反爬、时间字段6.1 编码问题比想象中严重这个项目里我遇到最频繁的问题就是编码。有些动漫站点返回的是GBK编码有些页面嵌套iframe的编码还不一致。我用resp.apparent_encoding解决了大部分问题但也遇到过嗅探失败的情况——页面明明应该是GBKrequests却判断成了cp1252导致解析出来全是乱码。后来我总结了一套稳妥流程拿到响应后先看resp.encoding和页面meta标签里写的charset如果两者不一致优先认可meta标签里的charset如果meta里什么都没有再用apparent_encoding兜底。另外对已经抓下来但乱码的CSV文件可以尝试重新指定编码读取不要着急删数据。6.2 动态加载的评分数据差点让我放弃项目进行到一半我想加一个短评关键词字段结果发现目标页面里的短评是滚动加载出来的HTML源码里根本看不到。这算是典型的动态页面问题。我当时的处理方式是抓包分析接口。浏览器F12打开Network面板滚动页面触发加载找到返回JSON数据的XHR请求发现其实是一个非常简单的时间戳分页接口。于是我直接用requests请求那个JSON接口省掉了大量HTML解析工作。也正因为这个经历我养成了一个习惯遇到页面数据不完整先看Network面板有没有现成的JSON接口别急着上无头浏览器。当然如果JSON接口有请求签名那没必要死磕换技术方案更实际。Selenium、playwright这类工具虽然有资源开销但在真正的动态渲染面前反而是最高效的出路。6.3 好数据坏数据不分分析结论全崩这个坑最隐蔽。我第一次跑评分和集数的相关性分析时得到了一个集数越长评分越低的强负相关结果做完还挺高兴。后来仔细一查发现是因为一部分R18短篇番和泡面番集数很短但评分偏高还有一部分超长年番由于年代久远评分人数少评分失真。这些极端样本混在一起直接把相关性算歪了。从那以后我在分析前都会先做一轮分布检查把评分人数过少、信息缺失严重的样本单独筛出来。分析一百个字段前先看十个样本的原始数据比跑一百个模型都有用。7. 项目做完后的直接体会7.1 爬虫与分析其实各占一半精力做这个项目前我预估爬虫能占80%的时间分析可能只是跑几个函数而已。实际情况完全相反爬虫数据抓取、解析、反爬调试大概用了40%的时间数据清洗和分析逻辑设计占了50%的时间最后绘图和文档整理只占10%。这个比例让我意识到如果只看demo式的爬虫教程很容易低估数据清洗的重要性。评分字符串、集数文本、标签分隔符、年份缺失值每一个字段都需要单独设计处理规则。建议所有想入门数据分析项目的人在规划工期时把清洗和分析的权重提上来不要重爬轻析。7.2 爬虫与分析的工程结构一定要分离我在项目中后期把代码重新整理成了三层结构spider/放爬虫脚本analysis/放清洗和分析脚本output/放中间数据和图表。这样做最大的好处是爬虫改版只需要动spider层分析脚本完全不需要碰反过来想换一种分析思路重新写analysis层脚本就能直接读取原始数据不用重新跑爬虫。做数据项目最忌讳的是把抓取和清洗写在一个文件里一个环节出问题就得全链路重跑。分离开之后我甚至可以直接把爬虫脚本重跑一遍来更新数据新的分析脚本再基于新数据输出新结论。这个工程习惯让我省下了大量返工时间。7.3 项目的后续扩展方向这个项目做完后我觉得还有几个方向可以继续深入。一是加上评分人数和收藏人数的采集做一个冷门佳作筛选模型找出评分高但关注度低的番剧二是加入番剧短评的文本处理和词频分析看看不同类型作品里用户讨论最多的关键词是什么三是把采集范围扩展到多个平台做跨平台评分对比这比单一平台的分析更能看出评分差异背后的平台用户结构问题。这个项目本身规模不大但胜在覆盖了数据采集、清洗、分析、可视化、结论产出的完整链路。做完之后再看动漫数据感觉完全不一样了。以后想深入了解任何一个小众领域的数据我都可以用同样的方法快速搭建起一套分析漏斗。数据抓取只是入口真正有价值的永远是拿到数据之后你能从中发现什么别人没注意到的规律。
RELATED

相关推荐

Winxvideo:AI全能工具,智能修复老视频照片、清理噪音、录屏剪辑超方便

Winxvideo:AI全能工具,智能修复老视频照片、清理噪音、录屏剪辑超方便

Winxvideo AI 是一款由 Digiarty 公司开发的 AI 驱动多媒体工具软件,主打视频、图片和音频的增强与处理。它把 AI 增强、格式转换、压缩、录屏和基础编辑等功能整合在一起,适合普通用户一站式解决媒体文件的质量问题和处理需求。 它就是一个“AI 媒体万能…

📅 2026/10/11 8:50:51
模糊机会约束规划:新能源消纳调度从确定性到不确定性的实战指南

模糊机会约束规划:新能源消纳调度从确定性到不确定性的实战指南

把风电和光伏同时接入电网那一刻,调度员手里的难题就没有“标准答案”了。肉眼可见的云层飘过来,光伏出力直线掉;风一停,整个系统的频率就要靠火电去顶。过去我们用确定性模型做日前计划,拿一个预测值当“真值”&#…

📅 2026/10/11 8:50:51
实测ToDesk新终端:跨端会话恢复+多终端并行,远程Vibe Coding的完整体验报告

实测ToDesk新终端:跨端会话恢复+多终端并行,远程Vibe Coding的完整体验报告

《不用守在电脑前了!我随手抄起平板,让AI把项目跑完了》最近ToDesk上了个新版本,重点优化了终端功能。说实话,AI 发展到今天,远控软件如果只会"远控",那确实不够看了。作为一个 Vibe Coding重度依…

📅 2026/10/11 8:50:51
MORE NEWS

更多资讯

📰

Docker本地部署Home Assistant:从零搭建私有智能家居平台

如果你最近在研究智能家居,大概率会反复听到一个名字:Home Assistant,以及一个动词:Docker 部署。这两个词凑在一起,基本就是当前自托管智能家居最主流的一套玩法——HA 负责把不同品牌、不同协议的设备拉到同一个平台…

📰

有源配电网SOP规划:为何必须考虑DG时序特性与MINLP建模

简介:本资源是一套面向电力系统方向毕业设计与科研实践的MATLAB仿真代码包,聚焦有源配电网中智能软开关(SOP)的规划建模与求解,适用于电气工程、新能源并网及智能配网研究领域的高年级本科生与研究生。资源完整复现了知…

📰

屏幕故障不用怕:黑屏、花屏、闪屏的定位排查全流程

屏幕出问题的时候,大多数人第一反应是“显示器坏了”或者“显卡挂了”。我经手过不少机器,真正一上来就换硬件解决的,其实只占一小部分。黑屏、花屏、闪屏这类现象,背后的原因可能藏在信号线、供电、驱动、系统设置,甚…

📰

ApplyPilot六阶段流水线全景解析:从职位发现到自动提交的完整工作原理

【免费下载链接】ApplyPilot AI agent that applies to jobs for you. Any site. Any form. 项目地址: https://gitcode.com/gh_mirrors/ap/ApplyPilot 点击查看 免费下载 ApplyPilot 是一款开源 AI 求职代理(job application agent)&#x…

📰

AI Agent工具调用模块和MCP:把MCP endpoint改到TaoToken的配置与验证

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

📰

单片机计算机毕设之基于 51 单片机的小型燃气机房 CO 与温湿度阈值可调告警装置设计 基于物联网的食堂后厨一氧化碳泄漏远程监测智能排风系统设计(030124)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬