当前位置: 首页 > news >正文

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?term=week&page=[page])。我们需要:

  1. 遍历大量历史页面(每周一页)。
  2. 从每个页面中精准定位并提取“周榜传说第一”的特殊条目。
  3. 处理可能存在的页面结构差异、反爬机制和数据缺失情况。

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.1
    • beautifulsoup4: 4.11.1
    • pandas: 1.5.0
    • lxml: 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文件

requests==2.28.1 beautifulsoup4==4.11.1 pandas==1.5.0 lxml==4.9.1

安装依赖: 打开终端或命令提示符,进入项目根目录,执行:

pip install -r requirements.txt

基础配置: 创建config.py文件,存放可配置参数。

# config.py # 爬虫配置 START_PAGE = 1 # 起始周次页码(通常最新一周为page=1,历史页码递增) END_PAGE = 50 # 结束周次页码(根据需求调整,可设置一个较大值,由程序判断终止) BASE_URL = "http://www.nicovideo.jp/ranking/genre/music?term=week&page={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;q=0.9,en;q=0.8', } REQUEST_TIMEOUT = 10 # 请求超时时间(秒) DELAY_BETWEEN_REQUESTS = 2 # 请求间隔时间(秒),避免对服务器造成压力 # 数据存储配置 RAW_HTML_DIR = "data/raw_html" OUTPUT_CSV_PATH = "data/weekly_first_legend.csv"

4. 页面解析逻辑拆解

这是爬虫的核心。我们需要分析Niconico周榜页面的HTML结构,找到“周榜传说第一”条目的特征。

步骤分析

  1. 获取页面:使用requests获取指定页码的周榜页面HTML。
  2. 定位特殊排名区域:观察HTML发现,“周榜传说第一”通常被包裹在一个特定的<div><section>中,其class可能包含“legend”“special”“first”等关键词。需要实际查看页面源代码确认。
  3. 提取关键信息:从该区域中,我们需要提取:
    • 周次/页码:从URL或页面标题中获取。
    • 排名:通常是“第xx位”。
    • 歌曲标题:链接文本。
    • 视频ID (sm号):从视频链接中提取,如sm12345678
    • P主 (投稿者)名称。
    • 达成传说的日期/时间(如果页面提供)。
    • 当周播放数
  4. 处理异常:某些周次可能没有“周榜传说第一”,解析函数需要能处理这种情况并返回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(text=re.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(r'rank')) data['rank'] = rank_elem.get_text(strip=True) if rank_elem else '第1位' # 提取视频标题和链接 title_link = special_section.find('a', href=re.compile(r'/watch/sm\d+')) if title_link: data['title'] = title_link.get_text(strip=True) href = title_link.get('href', '') # 从链接中提取视频ID,例如 /watch/sm12345678 -> sm12345678 match = re.search(r'sm\d+', href) data['video_id'] = match.group(0) if match else '' # 构建完整视频链接 data['video_url'] = f'https://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(r'user|author')) data['producer'] = author_elem.get_text(strip=True) if author_elem else '' # 提取播放数 (通常在一个class包含`view`或`count`的span里) count_elem = special_section.find(class_=re.compile(r'view|count')) if count_elem: count_text = count_elem.get_text(strip=True) # 去除逗号,转换为整数 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(text=re.compile(r'\d+月\d+日|\d+/\d+')) data['legend_date'] = date_elem.strip() if date_elem else '' return data

5. 爬虫核心与调度器实现

接下来,我们实现爬取单个页面和调度多个页面的逻辑。创建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_ok=True) def fetch_page(self, page_num: int) -> Optional[str]: """ 获取指定页码的周榜页面HTML。 Args: page_num: 周榜页码。 Returns: 页面的HTML文本,如果请求失败则返回None。 """ url = f"http://www.nicovideo.jp/ranking/genre/music?term=week&page={page_num}" try: print(f"正在抓取第 {page_num} 页...") response = self.session.get(url, timeout=REQUEST_TIMEOUT) response.raise_for_status() # 如果状态码不是200,抛出HTTPError # 可选:保存原始HTML用于调试 with open(os.path.join(RAW_HTML_DIR, f'page_{page_num}.html'), 'w', encoding='utf-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_data

6. 数据整合与持久化

爬取到的数据需要保存下来。我们使用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', inplace=True) df.reset_index(drop=True, inplace=True) # 添加序号列 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, index=False, encoding='utf-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 ...

验证

  1. 用Excel或文本编辑器打开CSV文件,检查数据是否完整(标题、P主、视频ID等)。
  2. 随机挑选几条记录中的video_url,在浏览器中打开,确认视频存在且标题等信息匹配。
  3. 检查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. 最佳实践与工程建议

将一个小脚本提升为更健壮、可维护的项目,可以考虑以下优化:

  1. 配置化管理:已将URL、请求头、路径等放入config.py,方便后续修改。可考虑使用.env文件管理敏感或环境相关配置。
  2. 日志记录:使用Python内置的logging模块替代print,可以输出不同级别(INFO, WARNING, ERROR)的日志到文件和控制台,便于后期排查。
  3. 异常处理与重试:网络请求不稳定,可以为fetch_page函数添加重试机制(如使用tenacity库)。
  4. 增量爬取:首次全量爬取后,后续只需爬取新页面。可以将已爬取的最大页码记录在文件或数据库中,下次从该页码+1开始。
  5. 数据去重与校验:保存数据前,检查video_id是否已存在,避免重复。对关键字段进行非空校验。
  6. 遵守Robots协议:检查https://www.nicovideo.jp/robots.txt,尊重网站的爬虫规则。合理设置爬取间隔,避免对服务器造成负担。
  7. 代码模块化:如本文所示,将爬虫、解析器、配置、工具分离,使代码结构清晰,易于测试和扩展。
  8. 考虑使用Scrapy框架:如果爬取需求变得非常复杂(需要处理登录、JavaScript渲染、大量并发等),迁移到Scrapy框架是更专业的选择。

10. 总结与扩展方向

通过本文,我们完成了一个从零开始的、针对特定目标数据的网络爬虫项目。你不仅获得了一份珍贵的“术力口周榜传说第一”数据集,更重要的是掌握了分析网页结构、设计解析逻辑、构建稳健爬虫流程的实战能力。

核心收获

  • 需求分析:明确了“周榜传说第一”这一特殊排名的定义和数据价值。
  • 工具链:熟悉了requests+BeautifulSoup+pandas这一经典爬虫与数据处理组合。
  • 实战流程:经历了环境搭建、页面分析、代码实现、数据存储、问题排查的完整开发周期。
  • 工程思维:引入了配置管理、模块化设计、错误处理和礼貌爬取等工程化实践。

扩展方向

  • 数据可视化:使用matplotlibplotly绘制“每月传说第一数量趋势图”、“热门P主统计图”等。
  • 数据库存储:将CSV数据导入SQLite或MySQL,便于进行更复杂的查询分析。
  • 扩展爬取范围:修改解析器,同时抓取周榜普通排名数据,进行更全面的分析。
  • 构建简单Web应用:使用FlaskStreamlit创建一个展示此数据集的简单网站。

技术是为兴趣和研究服务的。希望这个工具能帮助你更高效地探索术力口文化的数字轨迹。如果在实践过程中遇到新的问题或有了有趣的发现,不妨在社区分享你的经验。

http://www.cnnetsun.cn/news/3942739.html

相关文章:

  • 空洞骑士模组管理终极指南:Scarab让模组安装变得如此简单
  • 正则指引——匹配模式
  • 自定义内存分配器性能优化与实现解析
  • Flask+微信小程序构建三端在线考试系统实践
  • Django + MySQL + Vue 实现在线租房平台
  • C++20概念:编译期类型契约,提升泛型编程安全与表达力
  • Python+Django构建家庭光伏发电监控系统实践
  • 济阳网站建设哪家好?揭秘本地企业如何打造高转化、低成本的专业网站
  • Python第三次作业解析:控制结构、函数与文件操作实战
  • 2026年Go开发者Git工作流优化指南
  • 面向对象编程核心概念与实践指南
  • Blender到Unity FBX导出终极指南:解决坐标、缩放与动画问题
  • Selenide:简化Web自动化测试的智能封装库
  • 基于大语言模型与AI Agent构建个人理财智能助手实战指南
  • ESLyric-LyricsSource:Foobar2000终极逐字歌词解决方案
  • 杭州微跑网站建设公司深度解析:如何用技术赋能中小企业数字化转型与品牌升级之路
  • # 2026年8月更新:ChatGPT Plus、Pro 与 Codex 进入可观测性时代,AI Coding Agent 为什么也需要 Trace、Metric 和 Audit(GPT-5.6技术分
  • Zed代码编辑器-Day12
  • Codex 项目实战:从 0 搭建 AGENTS.md、Rules、Test 与 AI Code Review 工作流?一次讲清plus pro量
  • 自制驱动保护和隐藏进程工具。
  • VC++运行库一键安装合集:原理、版本选择与实战部署指南
  • UABEA:跨平台Unity资源处理工具,实现AssetBundle深度解析与自动化
  • Istio生产环境实战:从部署到优化的避坑指南
  • Go Web框架性能对比与选型指南
  • 衡水网站建设03181688深耕本地市场助力企业数字化转型升级之道
  • Unity 2020安卓打包环境配置指南:JDK 8与NDK r19避坑手册
  • 2026工业与集团企业数字大脑建设指南:全系统API中枢与多Agent自治工作流落地实战
  • TRPO 为什么要用自然梯度优化
  • C++实战:从零构建2048游戏,掌握二维数组与游戏循环核心
  • 游戏角色技能系统与宣发素材自动化生产管线实战