Python爬虫实战:节奏律动 - Billboard Hot 100 历史榜单深度采集实战!

㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~

㊙️本期爬虫难度指数:⭐⭐⭐

🉐福利: 一次订阅后,专栏内的所有文章可永久免费看,持续更新中,保底1000+(篇)硬核实战内容。

全文目录:

      • [🌟 开篇语](#🌟 开篇语)
      • [1️⃣ 摘要(Abstract)](#1️⃣ 摘要(Abstract))
      • [2️⃣ 背景与需求(Why)](#2️⃣ 背景与需求(Why))
      • [3️⃣ 合规与注意事项(必写)](#3️⃣ 合规与注意事项(必写))
      • [4️⃣ 技术选型与整体流程(What/How)](#4️⃣ 技术选型与整体流程(What/How))
      • [5️⃣ 环境准备与依赖安装(可复现)](#5️⃣ 环境准备与依赖安装(可复现))
      • [6️⃣ 核心实现:请求层(Fetcher)](#6️⃣ 核心实现:请求层(Fetcher))
      • [7️⃣ 核心实现:解析层(Parser)](#7️⃣ 核心实现:解析层(Parser))
      • [8️⃣ 数据存储与导出(Storage)](#8️⃣ 数据存储与导出(Storage))
      • [9️⃣ 运行方式与结果展示(必写)](#9️⃣ 运行方式与结果展示(必写))
      • [🔟 常见问题与排错(强烈建议写)](#🔟 常见问题与排错(强烈建议写))
      • [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
      • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
      • [🌟 文末](#🌟 文末)
        • [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
        • [✅ 互动征集](#✅ 互动征集)
        • [✅ 免责声明](#✅ 免责声明)

🌟 开篇语

哈喽,各位小伙伴们你们好呀~我是【喵手】。

运营社区: C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO

欢迎大家常来逛逛,一起学习,一起进步~🌟

我长期专注 Python 爬虫工程化实战 ,主理专栏 《Python爬虫实战》:从采集策略反爬对抗 ,从数据清洗分布式调度 ,持续输出可复用的方法论与可落地案例。内容主打一个"能跑、能用、能扩展 ",让数据价值真正做到------抓得到、洗得净、用得上

📌 专栏食用指南(建议收藏)

  • ✅ 入门基础:环境搭建 / 请求与解析 / 数据落库
  • ✅ 进阶提升:登录鉴权 / 动态渲染 / 反爬对抗
  • ✅ 工程实战:异步并发 / 分布式调度 / 监控与容错
  • ✅ 项目落地:数据治理 / 可视化分析 / 场景化应用

📣 专栏推广时间 :如果你想系统学爬虫,而不是碎片化东拼西凑,欢迎订阅专栏👉《Python爬虫实战》👈,一次订阅后,专栏内的所有文章可永久免费阅读,持续更新中。

💕订阅后更新会优先推送,按目录学习更高效💯~

1️⃣ 摘要(Abstract)

  • 工具栈: Python 3.10+、Requests(网络请求)、BeautifulSoup4(解析层)、Pandas/JSON(存储层)。
  • 产出: 包含 1958 年至今所有周榜详细数据(日期、排名、歌手、曲名、历史峰值等)的结构化 JSON/CSV 文件。
  • 学习收获: 掌握规律性 URL 序列生成、针对复杂嵌套 HTML 的精准解析逻辑、以及应对大规模爬取的容错重试机制。

2️⃣ 背景与需求(Why)

音乐数据是流行文化研究、市场趋势分析以及推荐系统的重要语料。

  • 需求: 批量获取历史榜单,用于分析如"哪位歌手霸榜时间最长"或"历史周次最多的歌曲"等课题。

  • 字段清单:

    • date: 榜单所属周次(YYYY-MM-DD)
    • rank: 当前排名(1-100)
    • title: 歌曲名称
    • artist: 歌手/组合名称
    • last_week: 上周排名(含 NEW/RE-ENTRY 标记)
    • peak_pos: 历史最高排名
    • weeks_on_chart: 在榜总周数

3️⃣ 合规与注意事项(必写)

  • 尊重 robots.txt: Billboard 官方通常允许爬取榜单公开页,但严禁高频攻击。
  • 频率控制: 设置 time.sleep(1-2),模拟人类浏览行为,不要挑战对方服务器的承受底线。
  • 中性采集: 本文仅讨论公开可见的榜单数据采集,不涉及破解付费会员专栏或采集用户私密信息。

4️⃣ 技术选型与整体流程(What/How)

Billboard 历史页(billboard.com/charts/hot-100/YYYY-MM-DD)虽然看起来现代感十足,但核心榜单数据仍渲染在静态 HTML 中。

  • 技术选型: 采用 Requests + BeautifulSoup4。Scrapy 略显繁重,Playwright 虽强但对于此类静态结构而言性价比较低。

  • 整体流程:

    Image of web scraping workflow: URL Generator -\> Request Fetcher -\> BS4 Parser -\> Data Cleaning -\> JSON Storage

    1. URL 生成器: 根据起始日期循环生成每周六(Billboard 惯例)的日期 URL。
    2. 采集层: 处理 Header 伪装与异常重试。
    3. 解析层: 利用 CSS 选择器提取嵌套在 o-chart-results-list-row-container 中的字段。
    4. 存储层: 将结果实时追加到本地 JSON 库中。

5️⃣ 环境准备与依赖安装(可复现)

推荐使用虚拟环境管理项目。

bash。 复制代码
### 5️⃣ 环境准备与依赖安装(可复现)

推荐使用虚拟环境管理项目。

```bash
# 创建并激活环境
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 安装核心依赖
pip install requests beautifulsoup4 pandas lxml

项目目录推荐:

text 复制代码
billboard_spider/
├── data/               # 存放抓取的 JSON/CSV
├── logs/               # 日志记录
├── spider.py           # 核心逻辑文件
└── utils/              # 辅助工具(如日期生成器)

6️⃣ 核心实现:请求层(Fetcher)

Billboard 对 User-Agent 有基础校验,若不设置 UA 可能会触发 403 错误。

python 复制代码
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def get_session():
    session = requests.Session()
    # 设置重试策略:针对 500/502/503/504 等服务器错误自动重试 3 次
    retries = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
    session.mount('https://', HTTPAdapter(max_retries=retries))
    
    session.headers.update({
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
        'Accept-Language': 'en-US,en;q=0.9'
    })
    return session

7️⃣ 核心实现:解析层(Parser)

这是最硬核的部分。Billboard 的页面结构使用了大量的 TBU(Tailwind-like)类名。我们需要通过父级容器精准定位。

python 复制代码
from bs4 import BeautifulSoup

def parse_chart(html_content, date_str):
    soup = BeautifulSoup(html_content, 'lxml')
    chart_data = []
    
    # 找到所有的列表行容器
    rows = soup.select('div.o-chart-results-list-row-container')
    
    for row in rows:
        # 获取基础字段,注意处理空值(容错处理)
        try:
            rank = row.select_one('span.c-label.a-font-primary-bold-l').get_text(strip=True)
            
            # 歌曲和歌手通常嵌套在特定的 ul/li 中
            title_tag = row.select_one('h3#title-of-a-story')
            title = title_tag.get_text(strip=True)
            
            # 歌手名在 h3 下方的第一个 span 中
            artist = title_tag.find_next('span').get_text(strip=True)
            
            # 历史指标(Last Week, Peak, Weeks on Chart)
            # 它们通常是一组 li 标签,根据索引提取
            stats = row.select('li.lrv-u-width-100p ul li span.c-label')
            # 过滤掉广告占位符,精确定位最后三个指标
            last_week = stats[0].get_text(strip=True) if len(stats) > 0 else "-"
            peak_pos = stats[1].get_text(strip=True) if len(stats) > 1 else "-"
            weeks_on_chart = stats[2].get_text(strip=True) if len(stats) > 2 else "-"

            chart_data.append({
                "date": date_str,
                "rank": rank,
                "title": title,
                "artist": artist,
                "last_week": last_week,
                "peak_pos": peak_pos,
                "weeks_on_chart": weeks_on_chart
            })
        except (AttributeError, IndexError) as e:
            # 记录解析异常但不中断程序
            continue
            
    return chart_data

8️⃣ 数据存储与导出(Storage)

由于是按日期抓取的序列数据,存储为 list[dict] 结构的 JSON 能够完美保留层级关系。

字段名 类型 示例值 说明
date String "2023-11-25" 榜单日期
rank Integer 1 排名
title String "Cruel Summer" 歌名
artist String "Taylor Swift" 歌手名
last_week String "2" 上周排名,新歌为 "NEW"

9️⃣ 运行方式与结果展示(必写)

主程序入口:

python 复制代码
import time
import json
import pandas as pd

def main():
    # 生成日期序列(示例:抓取 2023 年 11 月)
    dates = pd.date_range(start='2023-11-01', end='2023-11-30', freq='W-SAT').strftime('%Y-%m-%d')
    session = get_session()
    all_results = []

    for d in dates:
        print(f"🚀 正在抓取榜单: {d}...")
        url = f"https://www.billboard.com/charts/hot-100/{d}/"
        resp = session.get(url, timeout=15)
        
        if resp.status_code == 200:
            week_data = parse_chart(resp.text, d)
            all_results.extend(week_data)
            time.sleep(1.5) # 温柔抓取
        else:
            print(f"❌ 抓取失败: {d}, 状态码: {resp.status_code}")

    # 导出文件
    with open('billboard_history.json', 'w', encoding='utf-8') as f:
        json.dump(all_results, f, indent=4, ensure_ascii=False)

if __name__ == "__main__":
    main()

输出结果示例(JSON

json 复制代码
[
    {
        "date": "2023-11-25",
        "rank": "1",
        "title": "Cruel Summer",
        "artist": "Taylor Swift",
        "last_week": "2",
        "peak_pos": "1",
        "weeks_on_chart": "28"
    }
]

🔟 常见问题与排错(强烈建议写)

  • 遇到 403 Forbidden: 很有可能是被标记为爬虫。 更换 User-Agent 或检查是否请求频率过快。
  • 解析到的字段全为空: Billboard 偶尔会微调页面类名。对策: 使用 print(row.prettify()) 查看当前 HTML 结构,更新 CSS 选择器。
  • 日期跳变: Billboard 榜单通常是每周六更新,如果构造的日期不是周六,服务器会自动重定向到最近的周六。

1️⃣1️⃣ 进阶优化(可选但加分)

  • 异步化: 使用 httpx + asyncio。Billboard 响应稍慢,异步并发可以将抓取效率提升 5--10 倍。
  • *断点续爬 维护一个已爬取日期的文本文件,每次启动前读取,跳过已抓取的日期。
  • 数据库接入: 如果要处理从 1958 年至今的 3000+ 周数据,建议使用 SQLite ,通过 (date, rank) 作为联合主键进行去重。

1️⃣2️⃣ 总结与延伸阅读

通过本项目的实战,我们构建了一个鲁棒性极强的榜单采集器。这不仅仅是抓取几个名字,更是建立了一套应对复杂网页结构、处理时间序列数据的思维模板。

下一步: 可以尝试结合 Spotify API 抓取对应歌曲的音频特征(Energy, Danceability),做一个"流行音乐演变史"的数据可视化项目!

🌟 文末

好啦~以上就是本期的全部内容啦!如果你在实践过程中遇到任何疑问,欢迎在评论区留言交流,我看到都会尽量回复~咱们下期见!

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦~
三连就是对我写作道路上最好的鼓励与支持! ❤️🔥

✅ 专栏持续更新中|建议收藏 + 订阅

墙裂推荐订阅专栏 👉 《Python爬虫实战》,本专栏秉承着以"入门 → 进阶 → 工程化 → 项目落地"的路线持续更新,争取让每一期内容都做到:

✅ 讲得清楚(原理)|✅ 跑得起来(代码)|✅ 用得上(场景)|✅ 扛得住(工程化)

📣 想系统提升的小伙伴 :强烈建议先订阅专栏 《Python爬虫实战》,再按目录大纲顺序学习,效率十倍上升~

✅ 互动征集

想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战?

评论区留言告诉我你的需求,我会优先安排实现(更新)哒~


⭐️ 若喜欢我,就请关注我叭~(更新不迷路)

⭐️ 若对你有用,就请点赞支持一下叭~(给我一点点动力)

⭐️ 若有疑问,就请评论留言告诉我叭~(我会补坑 & 更新迭代)


✅ 免责声明

本文爬虫思路、相关技术和代码仅用于学习参考,对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。

使用或者参考本项目即表示您已阅读并同意以下条款:

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
52Hz1181 小时前
力扣131.分割回文串、35.搜索插入位置、74.搜索二维矩阵、34.在排序数组中查找...
python·算法·leetcode
二十雨辰1 小时前
[python]-多任务
python
癫狂的兔子1 小时前
【Python】【机器学习】集成算法(随机森林、提升算法)
python·算法·机器学习
kong79069281 小时前
Python核心语法-Matplotlib简介
开发语言·python·matplotlib
马克Markorg2 小时前
基于LLM的大模型的RAG(检索增强生成)实现对比
python·大模型·agent·rag·企业级知识库的框架·rag 知识库
yy.y--2 小时前
Java线程实现浏览器实时时钟
java·linux·开发语言·前端·python
Dontla2 小时前
Python Streamlit介绍(开源Python Web应用框架,快速将Python脚本转换成交互式Web应用,适合数据科学和机器学习项目快速展示)
前端·python·开源
少云清2 小时前
【UI自动化测试】12_web自动化测试 _验证码处理和cookie
前端·python·web自动化测试
Ama_tor2 小时前
Flask |零基础进阶(上)
后端·python·flask