Python爬虫实战:自动化采集Niconico周榜传说第一数据

发布时间:2026/8/10 8:34:34
Python爬虫实战:自动化采集Niconico周榜传说第一数据 最近在整理术力口VOCALOID相关数据时发现“周榜传说第一特殊排名”这个数据维度非常有趣它记录了P主Producer创作者在特定周次达成“传说入”即播放量超过100万的里程碑并且是当周首次达成此成就的排名。对于数据爱好者、术力口文化研究者乃至P主本人来说完整收集这份榜单不仅能梳理历史高光时刻更能洞察社区热度变迁。然而相关数据分散在Niconico动画的历史周榜页面中手动收集耗时费力且易出错。本文将分享一套基于Python的自动化数据采集与整理方案从环境搭建、页面解析到数据清洗、持久化存储手把手带你构建一个“周榜传说第一特殊排名”全收集工具。无论你是想为自己的研究项目积累数据还是单纯想学习网络爬虫与数据分析的实战技巧这篇文章都能提供完整的代码和清晰的思路。1. 背景与核心概念解析在深入代码之前我们有必要厘清几个关键概念这有助于理解我们到底要爬取什么以及为什么这么做。术力口与Niconico动画术力口是VOCALOID音乐文化圈的中文俗称其创作视频主要发布在日本视频网站Niconico动画上。Niconico的“周刊VOCALOID排行榜”是衡量作品人气的重要指标。“传说入”与“周榜传说第一”传说入指一首VOCALOID原创歌曲的播放量突破100万次。这是一个极具纪念意义的里程碑。周榜传说第一并非指每周排行榜的第一名而是一个特殊排名。它指的是在某一特定周次的周刊榜上首次达成“传说入”即播放量从999,999突破至1,000,000及以上的歌曲。该周可能有多首歌传说入但“周榜传说第一”特指其中排名最靠前的那一首通常按周刊排名顺序。数据价值收集所有“周榜传说第一”的记录相当于绘制了一份“百万播放里程碑达成时刻”的编年史。我们可以分析哪些P主频繁在周榜登顶时达成传说传说入的密集期对应了哪些社区大事件或潮流从首次传说到周榜传说第一的时间间隔有何规律技术挑战目标数据存在于Niconico历史周榜页面如http://www.nicovideo.jp/ranking/genre/music?termweekpage[page]。我们需要遍历大量历史页面每周一页。从每个页面中精准定位并提取“周榜传说第一”的特殊条目。处理可能存在的页面结构差异、反爬机制和数据缺失情况。2. 环境准备与版本说明本项目主要使用Python进行开发核心库包括requests用于网络请求BeautifulSoup用于HTML解析pandas用于数据整理。以下为推荐环境操作系统Windows 10/11, macOS, 或 Linux (如Ubuntu)。本文示例在Windows 11下演示。Python版本3.8 或更高版本。本文使用 Python 3.9。开发工具任意你喜欢的IDE或文本编辑器如PyCharm、VS Code、Jupyter Notebook。关键第三方库requests: 2.28.1beautifulsoup4: 4.11.1pandas: 1.5.0lxml: 4.9.1 (作为BeautifulSoup的解析器速度更快)项目结构 在开始前建议创建如下目录结构vocaloid_weekly_rank_crawler/ │ ├── main.py # 主程序入口 ├── crawler.py # 爬虫核心逻辑模块 ├── parser.py # 页面解析逻辑模块 ├── utils.py # 工具函数如日志、请求头处理 ├── config.py # 配置文件如起始页、请求间隔 ├── data/ # 数据存储目录 │ ├── raw_html/ # 存放爬取的原始HTML页面可选用于调试 │ └── weekly_first_legend.csv # 最终生成的CSV数据文件 └── requirements.txt # 项目依赖列表3. 核心依赖安装与配置首先创建并激活一个Python虚拟环境推荐然后安装依赖。创建requirements.txt文件requests2.28.1 beautifulsoup44.11.1 pandas1.5.0 lxml4.9.1安装依赖 打开终端或命令提示符进入项目根目录执行pip install -r requirements.txt基础配置 创建config.py文件存放可配置参数。# config.py # 爬虫配置 START_PAGE 1 # 起始周次页码通常最新一周为page1历史页码递增 END_PAGE 50 # 结束周次页码根据需求调整可设置一个较大值由程序判断终止 BASE_URL http://www.nicovideo.jp/ranking/genre/music?termweekpage{page} REQUEST_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, Accept-Language: ja,en-US;q0.9,en;q0.8, } REQUEST_TIMEOUT 10 # 请求超时时间秒 DELAY_BETWEEN_REQUESTS 2 # 请求间隔时间秒避免对服务器造成压力 # 数据存储配置 RAW_HTML_DIR data/raw_html OUTPUT_CSV_PATH data/weekly_first_legend.csv4. 页面解析逻辑拆解这是爬虫的核心。我们需要分析Niconico周榜页面的HTML结构找到“周榜传说第一”条目的特征。步骤分析获取页面使用requests获取指定页码的周榜页面HTML。定位特殊排名区域观察HTML发现“周榜传说第一”通常被包裹在一个特定的div或section中其class可能包含“legend”、“special”、“first”等关键词。需要实际查看页面源代码确认。提取关键信息从该区域中我们需要提取周次/页码从URL或页面标题中获取。排名通常是“第xx位”。歌曲标题链接文本。视频ID (sm号)从视频链接中提取如sm12345678。P主 (投稿者)名称。达成传说的日期/时间如果页面提供。当周播放数。处理异常某些周次可能没有“周榜传说第一”解析函数需要能处理这种情况并返回None或空值。让我们编写parser.py# parser.py from bs4 import BeautifulSoup import re def parse_weekly_first_legend(html_content, page_num): 从周榜页面HTML中解析出“周榜传说第一”的信息。 Args: html_content (str): 页面的HTML内容。 page_num (int): 当前页码用于标识周次。 Returns: dict or None: 包含解析后数据的字典如果未找到则返回None。 soup BeautifulSoup(html_content, lxml) # 关键步骤1定位特殊排名区域 # 需要根据实际页面结构调整选择器。这里是一个示例选择器。 # 假设特殊条目在一个class为weekly-ranking-special的div里并且包含伝説入り文字。 special_section soup.find(div, class_weekly-ranking-special) if not special_section: # 尝试其他可能的选择器或者直接搜索包含“伝説入り”和“第1位”的文本的父元素 # 这里采用更稳健的文本搜索方式 legend_text_elem soup.find(textre.compile(r伝説入り.*第1位)) if legend_text_elem: special_section legend_text_elem.find_parent(li) or legend_text_elem.find_parent(div) if not special_section: # 本周可能没有传说第一 return None # 关键步骤2提取详细信息 data {page: page_num} # 提取排名通常是“第1位” rank_elem special_section.find(class_re.compile(rrank)) data[rank] rank_elem.get_text(stripTrue) if rank_elem else 第1位 # 提取视频标题和链接 title_link special_section.find(a, hrefre.compile(r/watch/sm\d)) if title_link: data[title] title_link.get_text(stripTrue) href title_link.get(href, ) # 从链接中提取视频ID例如 /watch/sm12345678 - sm12345678 match re.search(rsm\d, href) data[video_id] match.group(0) if match else # 构建完整视频链接 data[video_url] fhttps://www.nicovideo.jp{href} if href.startswith(/) else href else: data[title] data[video_id] data[video_url] # 提取P主信息 (通常在一个class包含user或author的span或a标签里) author_elem special_section.find(class_re.compile(ruser|author)) data[producer] author_elem.get_text(stripTrue) if author_elem else # 提取播放数 (通常在一个class包含view或count的span里) count_elem special_section.find(class_re.compile(rview|count)) if count_elem: count_text count_elem.get_text(stripTrue) # 去除逗号转换为整数 data[weekly_views] int(re.sub(r[^\d], , count_text)) if count_text else 0 else: data[weekly_views] 0 # 提取传说达成时间如果存在可能在某个小字标签里 date_elem special_section.find(textre.compile(r\d月\d日|\d/\d)) data[legend_date] date_elem.strip() if date_elem else return data5. 爬虫核心与调度器实现接下来我们实现爬取单个页面和调度多个页面的逻辑。创建crawler.py。# crawler.py import requests import time import os from typing import Optional, Dict from .config import REQUEST_HEADERS, REQUEST_TIMEOUT, DELAY_BETWEEN_REQUESTS, RAW_HTML_DIR from .parser import parse_weekly_first_legend class WeeklyRankCrawler: def __init__(self): self.session requests.Session() self.session.headers.update(REQUEST_HEADERS) # 确保原始HTML存储目录存在 os.makedirs(RAW_HTML_DIR, exist_okTrue) def fetch_page(self, page_num: int) - Optional[str]: 获取指定页码的周榜页面HTML。 Args: page_num: 周榜页码。 Returns: 页面的HTML文本如果请求失败则返回None。 url fhttp://www.nicovideo.jp/ranking/genre/music?termweekpage{page_num} try: print(f正在抓取第 {page_num} 页...) response self.session.get(url, timeoutREQUEST_TIMEOUT) response.raise_for_status() # 如果状态码不是200抛出HTTPError # 可选保存原始HTML用于调试 with open(os.path.join(RAW_HTML_DIR, fpage_{page_num}.html), w, encodingutf-8) as f: f.write(response.text) return response.text except requests.exceptions.RequestException as e: print(f抓取第 {page_num} 页失败: {e}) return None def crawl_single_page(self, page_num: int) - Optional[Dict]: 爬取并解析单个页面。 Args: page_num: 周榜页码。 Returns: 解析后的数据字典如果页面无数据或解析失败则返回None。 html self.fetch_page(page_num) if not html: return None data parse_weekly_first_legend(html, page_num) return data def crawl_range(self, start_page: int, end_page: int) - list: 爬取指定范围内的所有页面。 Args: start_page: 起始页码。 end_page: 结束页码。 Returns: 所有成功解析的数据字典列表。 all_data [] for page in range(start_page, end_page 1): page_data self.crawl_single_page(page) if page_data: all_data.append(page_data) print(f第 {page} 页解析成功: {page_data.get(title, N/A)}) else: print(f第 {page} 页无‘周榜传说第一’数据或抓取失败。) # 礼貌性延迟避免请求过快 time.sleep(DELAY_BETWEEN_REQUESTS) return all_data6. 数据整合与持久化爬取到的数据需要保存下来。我们使用pandas来处理和保存为CSV文件。创建main.py作为程序入口。# main.py import pandas as pd from crawler import WeeklyRankCrawler from config import START_PAGE, END_PAGE, OUTPUT_CSV_PATH def main(): print( 术力口周榜传说第一特殊排名收集工具启动 ) # 初始化爬虫 crawler WeeklyRankCrawler() # 开始爬取 print(f开始爬取页码范围: {START_PAGE} 到 {END_PAGE}) collected_data crawler.crawl_range(START_PAGE, END_PAGE) if not collected_data: print(未收集到任何数据。请检查网络、页面结构或起始页码。) return # 转换为DataFrame df pd.DataFrame(collected_data) # 数据清洗与排序按页码/周次排序 df.sort_values(page, inplaceTrue) df.reset_index(dropTrue, inplaceTrue) # 添加序号列 df.insert(0, id, range(1, len(df) 1)) # 定义列顺序 column_order [id, page, rank, title, producer, video_id, video_url, weekly_views, legend_date] df df[column_order] # 保存到CSV df.to_csv(OUTPUT_CSV_PATH, indexFalse, encodingutf-8-sig) # utf-8-sig支持Excel直接打开显示中文 print(f数据已成功保存至: {OUTPUT_CSV_PATH}) print(f共收集到 {len(df)} 条记录。) # 打印前几条数据预览 print(\n数据预览:) print(df.head().to_string()) if __name__ __main__: main()7. 运行与结果验证在项目根目录下运行程序python main.py程序将开始逐页抓取并在控制台输出进度。完成后你会在data/weekly_first_legend.csv中得到一个CSV文件。预期输出CSV文件内容示例id,page,rank,title,producer,video_id,video_url,weekly_views,legend_date 1,1,第1位,【初音ミク】Example Song【オリジナル】,ExampleP,sm12345678,https://www.nicovideo.jp/watch/sm12345678,1054321,2023/10/26 2,2,第1位,【鏡音リン】Another Tune【オリジナル曲】,MusicCreator,sm87654321,https://www.nicovideo.jp/watch/sm87654321,1123456,2023/10/19 ...验证用Excel或文本编辑器打开CSV文件检查数据是否完整标题、P主、视频ID等。随机挑选几条记录中的video_url在浏览器中打开确认视频存在且标题等信息匹配。检查page列是否连续是否有大量缺失可能对应没有“传说第一”的周次。8. 常见问题与排查思路在爬虫开发与运行过程中你可能会遇到以下问题问题现象可能原因解决思路程序报错ModuleNotFoundError依赖库未安装或虚拟环境未激活。确认在项目目录下并执行pip install -r requirements.txt。爬取很快但返回空数据或None1. 页面结构已更新CSS选择器失效。2. 网站返回了错误页面或重定向。3. IP或请求频率被限制。1.最重要使用浏览器开发者工具重新检查目标页面的HTML结构更新parser.py中的选择器。2. 打印response.text的前几百字符查看是否包含预期内容。3. 增加DELAY_BETWEEN_REQUESTS或尝试使用代理。请求超时或连接被拒绝网络问题或目标服务器暂时不可用。检查网络连接增加REQUEST_TIMEOUT稍后重试。数据乱码编码问题。确保在读写文件时指定正确的编码如utf-8。保存CSV时使用utf-8-sig便于Excel识别。爬取到一定页数后停止END_PAGE设置不够大或网站历史页面只到某个截止点。观察控制台输出如果连续多页返回“无数据”可能是已到历史数据尽头。可以设置一个较大的END_PAGE让程序自然结束。被封IP请求过于频繁。务必遵守DELAY_BETWEEN_REQUESTS建议设置在3秒以上。对于大规模爬取应考虑使用代理IP池。调试技巧保存原始HTMLcrawler.py中已将每页HTML保存。当解析失败时打开对应的page_xx.html文件用浏览器打开并检查元素修正parser.py中的选择器逻辑。使用print调试在解析函数中临时打印soup的片段确认定位是否正确。分步测试单独运行parser.py用一个本地保存的HTML文件测试解析逻辑。9. 最佳实践与工程建议将一个小脚本提升为更健壮、可维护的项目可以考虑以下优化配置化管理已将URL、请求头、路径等放入config.py方便后续修改。可考虑使用.env文件管理敏感或环境相关配置。日志记录使用Python内置的logging模块替代print可以输出不同级别INFO, WARNING, ERROR的日志到文件和控制台便于后期排查。异常处理与重试网络请求不稳定可以为fetch_page函数添加重试机制如使用tenacity库。增量爬取首次全量爬取后后续只需爬取新页面。可以将已爬取的最大页码记录在文件或数据库中下次从该页码1开始。数据去重与校验保存数据前检查video_id是否已存在避免重复。对关键字段进行非空校验。遵守Robots协议检查https://www.nicovideo.jp/robots.txt尊重网站的爬虫规则。合理设置爬取间隔避免对服务器造成负担。代码模块化如本文所示将爬虫、解析器、配置、工具分离使代码结构清晰易于测试和扩展。考虑使用Scrapy框架如果爬取需求变得非常复杂需要处理登录、JavaScript渲染、大量并发等迁移到Scrapy框架是更专业的选择。10. 总结与扩展方向通过本文我们完成了一个从零开始的、针对特定目标数据的网络爬虫项目。你不仅获得了一份珍贵的“术力口周榜传说第一”数据集更重要的是掌握了分析网页结构、设计解析逻辑、构建稳健爬虫流程的实战能力。核心收获需求分析明确了“周榜传说第一”这一特殊排名的定义和数据价值。工具链熟悉了requestsBeautifulSouppandas这一经典爬虫与数据处理组合。实战流程经历了环境搭建、页面分析、代码实现、数据存储、问题排查的完整开发周期。工程思维引入了配置管理、模块化设计、错误处理和礼貌爬取等工程化实践。扩展方向数据可视化使用matplotlib或plotly绘制“每月传说第一数量趋势图”、“热门P主统计图”等。数据库存储将CSV数据导入SQLite或MySQL便于进行更复杂的查询分析。扩展爬取范围修改解析器同时抓取周榜普通排名数据进行更全面的分析。构建简单Web应用使用Flask或Streamlit创建一个展示此数据集的简单网站。技术是为兴趣和研究服务的。希望这个工具能帮助你更高效地探索术力口文化的数字轨迹。如果在实践过程中遇到新的问题或有了有趣的发现不妨在社区分享你的经验。