资讯动态

Python爬虫数据分析实战:动漫数据采集清洗与可视化全链路方案

发布时间:2026/10/29 20:22:02 来源:尧图企业网站定制
简介这份资源面向Python爬虫与数据分析初学者以B站番剧为实战场景完整演示从数据采集到可视化呈现的项目流程。爬虫部分先抓取总榜获取各动漫的粗略信息与直达链接再逐条访问详情页补充追番人数、评分等字段可帮助读者理解分页请求、链接跟进与数据落库的基本思路。资源包共23个文件约3.58MB包含3个Jupyter Notebook分别对应爬虫、数据清洗与数据分析2个xlsx数据表存放原始与处理后数据10张png可视化结果图以及若干xml配置文件和一份md说明文档其中md文件详细记录了爬虫细节、处理逻辑与图表解读。已有2984人学习下载适合作为爬虫入门与数据分析练手的参考项目读者可据此掌握请求构造、数据清洗、评分分布与追番趋势分析等技能并对照结果图复现完整分析链路。1. 从“爬虫能跑”到“数据能讲”一套动漫数据分析方案的起点很多同学做 Python 爬虫 数据分析 数据可视化 这类课题第一反应是先把请求发出去、把页面抓下来结果脚本跑通了数据也存了几千条到了要交方案、要答辩的时候却卡住这些字段能说明什么图表画出来为什么像流水账导师一句“你的分析结论呢”就把人问住了。这套方案要解决的不是“怎么发一个请求”而是把抓取、清洗、存储、分析、可视化串成一条能自证逻辑的链路让每一张图背后都有明确的问题。它适合正在做数据采集与分析方向毕设、课程设计的学生也适合需要快速搭一套数据看板原型的从业者。下面按需求、设计、实现、验收的顺序拆开讲中间会给出可直接改参数复用的代码骨架和表结构。2. 需求与数据边界先想清楚抓什么、不抓什么2.1 把“动漫数据”拆成可采集的字段“动漫数据”是个很虚的词落到方案里必须变成字段清单。通常的做法是先确定分析目标再倒推字段。比如想做热度与评分的关系分析至少需要作品名称、作品类型番剧/国创/影视等、播放量、追番人数、弹幕数、评分、上线时间、简介标签。如果还想做时间趋势就要保留上线日期想做类型分布就要把类型字段单独拆出来。字段定不下来后面建表、清洗、画图全是返工。我一般会先画一张“问题—字段—图表”的对照表把每个分析问题对应到需要的字段和最终图表类型。这样采集阶段就知道哪些字段是必填、哪些可以缺失避免抓了一堆用不上的信息。下面这张表是这套方案里最基础的一张映射实际做的时候可以按自己的选题增删。分析问题必需字段目标图表各类型作品数量分布类型、作品名柱状图/饼图播放量与追番人数的关系播放量、追番人数散点图评分随时间的变化评分、上线时间折线图高热度作品的特征播放量、弹幕数、标签词云/雷达图字段确定后还要明确数据边界只采集公开可见的列表页和详情页信息不碰登录态、不碰需要授权的接口控制请求频率遵守目标站点的 robots 约定。这不是套话而是方案能不能长期跑下去的前提。很多脚本跑两天就被限流血泪经验就是一开始没做频率控制和失败重试。2.2 技术选型为什么是 requests BeautifulSoup pandas选型要能说清理由答辩时这是必问项。这套方案的主线是 requests 负责请求、BeautifulSoup 或 lxml 负责解析、pandas 负责清洗与聚合、matplotlib 或 pyecharts 负责出图、SQLite 或 MySQL 负责落库。理由很直接数据量在万级以内单机脚本足够不需要上分布式解析以静态 HTML 为主BeautifulSoup 上手快、容错好pandas 的 groupby 和 merge 能覆盖绝大多数分析需求SQLite 零配置适合课程设计直接交付。如果目标页面是动态渲染的通常的做法是优先找页面内嵌的 JSON 数据或公开的列表接口而不是一上来就上浏览器自动化。浏览器自动化资源占用高、稳定性差作为兜底手段可以作为主链路不合适。选型表可以这样写进方案环节选型理由请求requests 重试轻量、可控超时解析BeautifulSoup/lxml静态页面容错好存储SQLite课设/MySQL扩展零配置或便于多人访问分析pandas聚合、透视、缺失处理成熟可视化matplotlib/pyecharts静态图与交互图各有场景3. 总体架构设计四层结构与模块职责划分3.1 采集层、存储层、分析层、展示层的职责整套方案按四层拆采集层负责请求调度、页面解析、字段抽取存储层负责建表、去重、增量更新分析层负责清洗、特征构造、聚合统计展示层负责图表生成与结论输出。四层之间用明确的数据结构衔接采集层输出统一的字典列表存储层落成表分析层读表产出 DataFrame展示层只消费分析结果不直接碰原始页面。这样任何一层出问题都能单独替换比如解析规则变了只改采集层图表要换风格只改展示层。模块划分建议按功能而不是按文件堆fetcher请求与重试、parser解析与字段映射、storage建表与写入、cleaner清洗规则、analyzer统计指标、visualizer图表。每个模块一个文件入口脚本只做编排。这样写出来的代码在答辩时能讲清“哪块负责什么”而不是一坨脚本。3.2 数据流与增量更新策略数据流是种子列表页 → 详情页链接队列 → 逐条抓取 → 解析成记录 → 按唯一键去重写入 → 分析层读取。增量更新的关键是给每条记录一个稳定的唯一键通常用作品 ID 或详情页 URL 的哈希写入时用INSERT OR REPLACE或先查后写。这样重复跑脚本不会产生重复数据也方便后续补抓失败条目。提示增量更新一定要有唯一键否则第二次跑脚本数据翻倍后面所有统计全错这是最常见的翻车点之一。架构上不需要画复杂图用文字把“谁产出什么、谁消费什么”讲清楚就够了。采集层产出标准化记录存储层产出带时间戳的表分析层产出指标表展示层产出图。每层都留一个中间产物文件或表出问题时能定位到是哪一层的数据不对。4. 采集与存储实现请求、解析、建表的关键代码4.1 带重试与限速的请求封装请求层最容易翻车的是超时和限流。下面这段封装把超时、重试、随机间隔、UA 轮换放在一起参数都能按目标站点调整。注意Retry的backoff_factor不要设太小否则连续失败会触发更严格的限制。import time import random import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def build_session(retries3, backoff1.5): session requests.Session() # 失败重试连接错误、超时、5xx 都重试 retry Retry( totalretries, backoff_factorbackoff, status_forcelist[429, 500, 502, 503, 504], allowed_methods[GET], ) adapter HTTPAdapter(max_retriesretry) session.mount(http://, adapter) session.mount(https://, adapter) session.headers.update({ User-Agent: Mozilla/5.0 (compatible; DataCollector/1.0), Accept-Language: zh-CN,zh;q0.9, }) return session def fetch(session, url, timeout10): # 随机间隔降低被限流概率 time.sleep(random.uniform(0.8, 2.0)) resp session.get(url, timeouttimeout) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.textretries控制总重试次数backoff控制退避倍数timeout是单次请求上限。随机间隔的范围要按目标站点的响应速度调太短容易触发限制太长采集效率低。失败时先看返回状态码429 说明频率过高5xx 说明服务端问题超时则要检查网络和目标页大小。这套封装不追求高并发课程设计场景下单线程加间隔足够稳定也更容易讲清楚。4.2 解析规则与字段标准化解析层要把页面里的文本变成结构化字段重点是容错某个字段缺失时给默认值而不是抛异常。下面是一个解析函数的骨架字段名和选择器按实际页面替换。from bs4 import BeautifulSoup def parse_detail(html, url): soup BeautifulSoup(html, lxml) def text_of(selector, default): node soup.select_one(selector) return node.get_text(stripTrue) if node else default record { item_id: url.rstrip(/).split(/)[-1], # 唯一键 title: text_of(h1.title), category: text_of(.detail .type), play_count: text_of(.play-count, 0), follow_count: text_of(.follow-count, 0), score: text_of(.score, 0), publish_date: text_of(.publish-time), tags: ,.join(n.get_text(stripTrue) for n in soup.select(.tag-list a)), source_url: url, } return recorditem_id是去重和增量更新的依据必须稳定数值字段先按字符串取回清洗阶段再转数字避免解析阶段因为格式问题中断tags用逗号拼接方便后续拆分做词云。解析函数只负责“取”不负责“算”转换和校验放到清洗层职责清晰出问题好定位。4.3 建表与去重写入存储层用一张主表加一张采集日志表。主表存作品记录日志表记录每次采集的批次、成功数、失败数方便排查。下面以 SQLite 为例MySQL 只需把自增和类型稍作调整。CREATE TABLE IF NOT EXISTS anime_item ( item_id TEXT PRIMARY KEY, -- 唯一键去重依据 title TEXT NOT NULL, category TEXT, play_count INTEGER DEFAULT 0, follow_count INTEGER DEFAULT 0, score REAL DEFAULT 0, publish_date TEXT, tags TEXT, source_url TEXT, updated_at TEXT DEFAULT (datetime(now, localtime)) ); CREATE TABLE IF NOT EXISTS crawl_log ( batch_id INTEGER PRIMARY KEY AUTOINCREMENT, started_at TEXT, total INTEGER, success INTEGER, failed INTEGER );写入时用INSERT OR REPLACE INTO anime_item ...主键冲突就覆盖天然实现增量更新。updated_at记录最后更新时间分析时可以按批次筛选。日志表不是可有可无答辩时被问“你怎么知道数据完整”就靠它回答。数值字段在入库前统一转成整数或浮点空值给 0避免后面聚合时报类型错误。5. 分析与可视化实现从清洗规则到图表落地5.1 清洗规则与特征构造清洗层要处理四类问题数值字段带单位如“1.2亿”、日期格式不统一、标签分隔混乱、缺失值。通常的做法是写一组转换函数逐列应用并把转换失败的记录记入异常表而不是直接丢弃。下面这段是数值和日期的处理示例。import re import pandas as pd def to_number(value): # 把“1.2亿”“3456万”统一转成整数 if pd.isna(value): return 0 s str(value).strip() match re.match(r([\d.])\s*(亿|万)?, s) if not match: return 0 num float(match.group(1)) unit match.group(2) if unit 亿: num * 100_000_000 elif unit 万: num * 10_000 return int(num) def load_clean(db_path): df pd.read_sql(SELECT * FROM anime_item, fsqlite:///{db_path}) df[play_count] df[play_count].apply(to_number) df[follow_count] df[follow_count].apply(to_number) df[score] pd.to_numeric(df[score], errorscoerce).fillna(0) df[publish_date] pd.to_datetime(df[publish_date], errorscoerce) df[year] df[publish_date].dt.year df[tag_list] df[tags].fillna().apply( lambda x: [t for t in x.split(,) if t] ) return dfto_number处理中文单位errorscoerce把无法转换的评分变成 NaN 再填 0year是从日期派生的特征方便做年度趋势。清洗后的 DataFrame 就是分析层的唯一输入所有图表都从这里取数保证口径一致。异常记录建议单独存一张表答辩时能说明“数据质量是怎么控制的”。5.2 核心分析指标与聚合口径分析层至少产出四组指标类型分布按 category 计数、热度与追番的关系play_count 与 follow_count 的相关性、评分分布分箱统计、年度趋势按 year 聚合播放量中位数。聚合口径要写进方案比如“播放量取中位数而非均值避免头部作品拉偏”。下面是一个聚合示例。def build_metrics(df): metrics {} # 类型分布只保留出现次数大于 5 的类型 cat df[category].value_counts() metrics[category_dist] cat[cat 5] # 评分分箱 metrics[score_bins] pd.cut( df[score], bins[0, 6, 7, 8, 9, 10], include_lowestTrue ).value_counts().sort_index() # 年度播放量中位数 metrics[year_play] ( df.dropna(subset[year]) .groupby(year)[play_count] .median() .sort_index() ) # 相关系数判断热度与追番是否同步 metrics[corr] df[[play_count, follow_count]].corr().iloc[0, 1] return metricsvalue_counts后过滤低频类型避免图表被长尾类别撑爆pd.cut把连续评分变区间适合画柱状图中位数比均值稳健这是分析口径里要明确写出的选择相关系数用来说明两个热度指标是否同向变化答辩时能引出结论而不是只念数字。5.3 图表选型与结论表达图表不是越多越好每张图对应第 2 章那张映射表里的一个问题。类型分布用横向柱状图类别多时可读性比饼图好热度关系用散点图并加趋势线评分分布用柱状图年度趋势用折线图。下面是一个出图示例重点是标题、坐标轴标签和注释要写清楚图能自己说话。import matplotlib.pyplot as plt def plot_category(series, out_path): plt.rcParams[font.sans-serif] [SimHei] # 中文字体 plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(8, 5)) series.sort_values().plot(kindbarh, axax, color#4C72B0) ax.set_title(各类型作品数量分布) ax.set_xlabel(作品数量) ax.set_ylabel(类型) for i, v in enumerate(series.sort_values()): ax.text(v, i, str(v), vacenter) fig.tight_layout() fig.savefig(out_path, dpi150)中文字体不设置会显示方块这是新手最常见的翻车点barh适合类别多的场景数值标注让读者不用估读。每张图配一句结论比如“某类型作品数量最多但中位播放量偏低说明数量与热度不同步”结论要来自数据而不是主观感受。可视化层只消费指标不重新读原始表保证图表口径和分析口径一致。6. 避坑与排查这类项目最容易翻车的五个点6.1 反爬触发后脚本静默失败现象是脚本跑完显示成功但入库条数明显偏少。原因通常是返回了验证页或空列表而解析函数对空页面返回了默认值没有报错。解决方法是每次请求后校验关键字段是否存在连续 N 条解析为空就中止并记录日志同时检查状态码和页面长度。我一般会在解析函数里加一个“有效记录”判断标题为空直接抛异常进失败队列。6.2 数值字段带单位导致聚合全错现象是播放量求和结果小得离谱或者排序完全乱序。原因是“1.2亿”被当成字符串或只取到了 1.2。解决办法是入库前统一用转换函数处理转换失败的记录单独标记分析前先检查数值列的分布和最大值是否合理。血泪经验是不要相信页面上的数字格式永远一致同一站点不同页面可能混用“万”和“亿”。6.3 重复采集导致数据翻倍现象是第二次跑脚本后总数变成两倍图表比例失真。原因是没设唯一键或用了自增主键。解决办法是给作品 ID 或 URL 哈希建唯一索引写入用INSERT OR REPLACE并在采集前先查已存在的 ID 跳过。增量更新策略要在方案里写清楚答辩时这是加分项。6.4 中文字体缺失导致图表乱码现象是图表标题和标签全是方块。原因是 matplotlib 默认字体不含中文。解决办法是显式设置font.sans-serif为系统已有中文字体并关闭负号乱码。部署到不同机器时字体可能不同通常的做法是在方案里注明依赖字体或在代码里做字体回退检测。6.5 分析口径不一致导致结论自相矛盾现象是同一指标在两张图里数值对不上。原因是有的图用均值、有的用中位数或者一张图过滤了缺失值另一张没过滤。解决办法是把清洗和聚合口径集中在一个模块里所有图表从同一份指标表取数口径写进方案文档。这是最隐蔽的坑往往到答辩被追问才发现。7. 验收与答辩怎么证明这套方案真的跑通了验收不是看图表好不好看而是看链路能不能复现。我一般按三步走第一步清空数据库从零跑一遍采集记录总耗时、成功数、失败数和日志表对得上第二步用固定随机种子重跑分析脚本确认指标表数值一致第三步随机抽 10 条记录回源核对字段确认解析没有错位。这三步能覆盖采集、存储、分析三个环节答辩时直接展示日志表和核对记录比口头说“跑通了”有说服力。答辩陈述建议按“问题—方法—结果—局限”四段组织每段控制在两分钟内。问题段说清你要回答什么方法段讲四层架构和关键选型理由结果段展示三到四张核心图和对应结论每张图一句话局限段主动说明数据边界和未覆盖的分析维度比如只采集了公开列表页、时间跨度有限。主动说局限比被问出来好导师通常更看重你是否清楚方案的边界。下面这张验收清单可以直接放进方案附录逐项打勾验收项通过标准检查方式采集完整性成功数/总数 ≥ 90%对比日志表与队列数数据唯一性无重复 item_id主键约束 计数校验字段准确性抽查 10 条无错位回源人工核对分析可复现重跑指标一致固定种子重跑图表可读性标题轴标签完整逐图检查从那以后我每次带这类课题都会要求先交一张字段映射表和一份验收清单再动手写代码因为返工成本最高的从来不是写脚本而是口径没定清楚。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑