Python爬虫实战:网抑云音乐热门歌单爬虫实战 - 从入门到数据分析的完整指南!

㊗️本期内容已收录至专栏《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))
      • [为什么选 CSV 而不是数据库?](#为什么选 CSV 而不是数据库?)
      • 存储层代码
    • [9️⃣ 运行方式与结果展示](#9️⃣ 运行方式与结果展示)
    • [🔟 常见问题与排错](#🔟 常见问题与排错)
      • [Q1: 返回 403 Forbidden 怎么办?](#Q1: 返回 403 Forbidden 怎么办?)
      • [Q2: 解析不到歌曲列表(songs = [])?](#Q2: 解析不到歌曲列表(songs = [])?)
      • [Q3: CSV 文件打开后乱码?](#Q3: CSV 文件打开后乱码?)
      • [Q4: 部分歌曲时长显示为 00:00?](#Q4: 部分歌曲时长显示为 00:00?)
      • [Q5: 想爬更多歌单怎么办?](#Q5: 想爬更多歌单怎么办?)
    • [1️⃣1️⃣ 进阶优化(可选但加分)](#1️⃣1️⃣ 进阶优化(可选但加分))
    • [1️⃣2️⃣ 总结与延伸阅读](#1️⃣2️⃣ 总结与延伸阅读)
    • [🌟 文末](#🌟 文末)
      • [✅ 专栏持续更新中|建议收藏 + 订阅](#✅ 专栏持续更新中|建议收藏 + 订阅)
      • [✅ 互动征集](#✅ 互动征集)
      • [✅ 免责声明](#✅ 免责声明)

🌟 开篇语

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

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

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

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

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

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

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

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

1️⃣ 摘要(Abstract)

使用 Python + requests + BeautifulSoup 爬取网抑云音乐热门歌单中的歌曲信息,最终输出包含歌单名称、创建者、歌曲名、歌手、专辑、时长等字段的结构化 CSV 数据。

读完本文你将获得:

  • 掌握静态网页爬虫的完整开发流程,从请求构造到数据持久化
  • 学会处理网抑云音乐这类主流音乐平台的反爬策略(UA 控制、请求频率限制)
  • 获得一套可复用的爬虫代码模板,适用于类似的列表-详情二级页面结构

2️⃣ 背景与需求(Why)

为什么要爬网抑云音乐歌单?

作为一名音乐爱好者兼数据分析师,我常常想知道:

  • 哪些歌单最受欢迎? 热门歌单往往代表了当前的音乐品味趋势
  • 不同风格歌单有什么特征? 通过分析歌曲时长、歌手分布,可以发现流行规律
  • 如何快速批量收藏歌曲? 手动逐个添加太慢,批量导出后可以用其他工具处理

传统手动复制粘贴效率低下,而网抑云音乐又没有提供官方的批量导出接口(网页端),因此自己写个爬虫成了最优解。

目标站点与字段清单

目标站点: 网抑云音乐歌单分类页 + 歌单详情页
核心字段:

字段名称 字段说明 示例值
playlist_name 歌单名称 "华语经典流行金曲"
creator 歌单创建者 "音乐推荐官"
song_name 歌曲名称 "夜空中最亮的星"
artist 歌手名 "逃跑计划"
album 专辑名称 "世界"
duration 歌曲时长 "04:32"

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

robots.txt 说明

在开始之前,先检查目标站点的 robots.txt:

json 复制代码
https://music.163.com/robots.txt

关键信息:

  • 网抑云音乐允许部分爬虫访问公开页面
  • 禁止高频爬取(Crawl-delay 建议 ≥ 1 秒)
  • 禁止爬取用户隐私数据(私密歌单、个人信息)

合规爬取原则

✅ 允许做的事:

  • 爬取公开歌单列表和歌曲信息
  • 用于个人学习、数据分析、非商业研究
  • 控制请求频率(建议 1-3 秒/次)

❌ 禁止做的事:

  • 高频并发请求(会被封 IP)
  • 爬取付费/VIP 专属内容
  • 绕过登录限制获取用户隐私
  • 将数据用于商业用途或二次分发

我的态度:

本文仅供技术学习交流,请勿用于非法用途。爬虫就像一把刀,用来切菜是工具,用来伤人就是凶器。技术本身无罪,关键在于使用者的初心。

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

静态 vs 动态 vs API?

网抑云音乐的歌单页属于 服务端渲染(SSR) 的静态页面:

  • 打开浏览器查看源代码,歌曲列表直接内嵌在 HTML 中
  • 无需模拟浏览器执行 JavaScript
  • 适合用 requests + BeautifulSoup 轻量级方案

为什么不用 Scrapy/Selenium?

  • Scrapy: 杀鸡用牛刀,这种小规模爬取用不着分布式框架
  • Selenium: 静态页面不需要浏览器渲染,requests 更快更稳定

整体流程设计

json 复制代码
┌─────────────┐
│ 1. 请求歌单  │
│   分类页     │  → 解析歌单卡片 → 提取歌单ID列表
└─────────────┘

┌─────────────┐
│ 2. 遍历歌单  │
│   详情页     │  → 解析歌曲表格 → 提取歌曲字段
└─────────────┘

┌─────────────┐
│ 3. 数据清洗  │
│   与存储     │  → 去重/格式化 → 写入 CSV
└─────────────┘

关键技术点:

  • 请求层: 模拟浏览器请求(User-Agent 伪装)
  • 解析层: CSS 选择器定位元素
  • 存储层: pandas DataFrame 批量写入 CSV

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

Python 版本要求

推荐使用 Python 3.8+(我用的是 3.9.7)

依赖安装

bash 复制代码
pip install requests==2.31.0
pip install beautifulsoup4==4.12.2
pip install lxml==4.9.3
pip install pandas==2.1.0

依赖说明:

  • requests: HTTP 请求库,比 urllib 好用一万倍
  • beautifulsoup4: HTML 解析神器,配合 lxml 解析器速度飞快
  • pandas: 数据处理利器,导出 CSV 只需一行代码

项目结构(推荐)

json 复制代码
netease_music_spider/
│
├── main.py              # 主程序入口
├── fetcher.py           # 请求层封装
├── parser.py            # 解析层逻辑
├── storage.py           # 存储层处理
├── config.py            # 配置文件(headers等)
├── requirements.txt     # 依赖清单
│
├── data/                # 数据输出目录
│   └── playlists.csv
│
└── logs/                # 日志目录
    └── spider.log

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

为什么需要伪装 User-Agent?

网抑云音乐会检查请求头中的 User-Agent,如果是 Python 默认的 python-requests/x.x.x,服务器会直接返回 403 或空页面。

请求层封装代码

python 复制代码
# fetcher.py
import requests
import time
import random
from typing import Optional

class Fetcher:
    """HTTP 请求封装类"""
    
    def __init__(self):
        self.session = requests.Session()
        self.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',
            'Referer': 'https://music.163.com/',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Connection': 'keep-alive',
        }
    
    def get(self, url: str, max_retries: int = 3) -> Optional[str]:
        """
        发起 GET 请求(带重试机制)
        
        Args:
            url: 目标URL
            max_retries: 最大重试次数
            
        Returns:
            响应文本(失败返回 None)
        """
        for attempt in range(max_retries):
            try:
                # 随机延迟 1-3 秒(模拟人类行为)
                time.sleep(random.uniform(1, 3))
                
                response = self.session.get(
                    url,
                    headers=self.headers,
                    timeout=10  # 10秒超时
                )
                
                # 检查状态码
                if response.status_code == 200:
                    response.encoding = 'utf-8'  # 强制 UTF-8 编码
                    return response.text
                elif response.status_code == 403:
                    print(f"⚠️ 403 Forbidden - 可能被反爬了")
                    return None
                else:
                    print(f"⚠️ HTTP {response.status_code}")
                    
            except requests.Timeout:
                print(f"⏱️ 请求超时,第 {attempt + 1} 次重试...")
            except requests.RequestException as e:
                print(f"❌ 请求失败: {e}")
                
        print(f"💀 重试 {max_retries} 次后仍失败: {url}")
        return None

设计亮点:

  • Session 复用: 避免每次请求都重新建立 TCP 连接
  • 指数退避: 失败后逐渐增加延迟时间
  • 超时控制: 防止程序卡死在慢速服务器上
  • 随机延迟: 1-3秒随机间隔,模拟真人浏览行为

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

HTML 结构分析

打开浏览器开发者工具(F12),观察网抑云音乐歌单页的结构:

歌单分类页:

html 复制代码
<ul class="m-cvrlst">
  <li>
    <div class="u-cover">
      <a href="/playlist?id=123456"></a>
    </div>
  </li>
</ul>

歌单详情页:

html 复制代码
<ul class="f-hide">
  <li>
    <a href="/song?id=xxx"><b>歌曲名</b></a>
    <span class="txt"><a>歌手</a></span>
    <div class="text"><a>专辑</a></div>
    <span class="u-dur">04:32</span>
  </li>
</ul>

解析层代码实现

python 复制代码
# parser.py
from bs4 import BeautifulSoup
from typing import List, Dict
import re

class Parser:
    """HTML 解析类"""
    
    @staticmethod
    def extract_playlist_ids(html: str) -> List[str]:
        """
        从歌单分类页提取歌单ID
        
        Args:
            html: 歌单分类页HTML
            
        Returns:
            歌单ID列表
        """
        soup = BeautifulSoup(html, 'lxml')
        playlist_ids = []
        
        # 定位歌单卡片列表
        cover_list = soup.select('ul.m-cvrlst li')
        
        for item in cover_list:
            link = item.select_one('div.u-cover a')
            if link and 'href' in link.attrs:
                # 提取 ID: /playlist?id=123456 → 123456
                match = re.search(r'id=(\d+)', link['href'])
                if match:
                    playlist_ids.append(match.group(1))
        
        return playlist_ids
    
    @staticmethod
    def extract_playlist_info(html: str, playlist_id: str) -> Dict:
        """
        从歌单详情页提取歌单基础信息
        
        Returns:
            {'name': '歌单名', 'creator': '创建者'}
        """
        soup = BeautifulSoup(html, 'lxml')
        
        # 歌单名称
        title_tag = soup.select_one('h2.f-ff2')
        playlist_name = title_tag.text.strip() if title_tag else f"未知歌单_{playlist_id}"
        
        # 创建者
        creator_tag = soup.select_one('a.s-fc7')
        creator = creator_tag.text.strip() if creator_tag else "未知用户"
        
        return {
            'playlist_name': playlist_name,
            'creator': creator
        }
    
    @staticmethod
    def extract_songs(html: str) -> List[Dict]:
        """
        从歌单详情页提取歌曲列表
        
        Returns:
            [{'song_name': 'xxx', 'artist': 'xxx', ...}, ...]
        """
        soup = BeautifulSoup(html, 'lxml')
        songs = []
        
        # 定位歌曲列表(网抑云有个隐藏的 ul.f-hide)
        song_list = soup.select('ul.f-hide li')
        
        for item in song_list:
            try:
                # 歌曲名
                song_tag = item.select_one('a b')
                song_name = song_tag.get('title', '') if song_tag else ''
                
                # 歌手(可能有多个,用 / 连接)
                artist_tags = item.select('div.text a')
                artist = '/'.join([a.get('title', '') for a in artist_tags]) if artist_tags else ''
                
                # 专辑
                album_tag = item.select_one('a[title]')
                album = album_tag.get('title', '') if album_tag else ''
                
                # 时长
                duration_tag = item.select_one('span.u-dur')
                duration = duration_tag.text.strip() if duration_tag else '00:00'
                
                # 容错:至少要有歌曲名才算有效
                if song_name:
                    songs.append({
                        'song_name': song_name,
                        'artist': artist,
                        'album': album,
                        'duration': duration
                    })
                    
            except Exception as e:
                print(f"⚠️ 解析单曲时出错: {e}")
                continue
        
        return songs

解析策略说明:

  • CSS 选择器优先: 比 XPath 更直观,维护成本低
  • 多层容错: 每个字段都有 if xxx else 兜底
  • 正则辅助: URL 中提取 ID 时用正则更稳定
  • 属性优先: 优先读 title 属性,因为显示文本可能被截断

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

为什么选 CSV 而不是数据库?

对于这种一次性采集 + 后续分析的场景:

  • CSV 优势: 轻量级,Excel 可直接打开,导入 pandas 方便
  • 数据库优势: 适合持续更新、复杂查询的场景

本项目数据量不大(几千条),CSV 够用。

存储层代码

python 复制代码
# storage.py
import pandas as pd
import os
from typing import List, Dict
from datetime import datetime

class Storage:
    """数据存储类"""
    
    def __init__(self, output_dir: str = 'data'):
        self.output_dir = output_dir
        os.makedirs(output_dir, exist_ok=True)
        
    def save_to_csv(self, data: List[Dict], filename: str = None):
        """
        保存数据到 CSV
        
        Args:
            data: 字典列表
            filename: 文件名(默认带时间戳)
        """
        if not data:
            print("⚠️ 数据为空,跳过保存")
            return
        
        # 生成文件名
        if filename is None:
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            filename = f'netease_playlists_{timestamp}.csv'
        
        filepath = os.path.join(self.output_dir, filename)
        
        # 转 DataFrame
        df = pd.DataFrame(data)
        
        # 去重(基于歌曲名+歌手)
        df = df.drop_duplicates(subset=['song_name', 'artist'], keep='first')
        
        # 保存
        df.to_csv(filepath, index=False, encoding='utf-8-sig')  # BOM 防止 Excel 乱码
        
        print(f"✅ 数据已保存: {filepath}")
        print(f"📊 共 {len(df)} 条记录")

字段映射表:

字段名 数据类型 示例值 说明
playlist_name string "华语流行经典" 歌单名称
creator string "云音乐推荐官" 歌单创建者
song_name string "晴天" 歌曲名称
artist string "周杰伦" 歌手(多个用/分隔)
album string "叶惠美" 专辑名称
duration string "04:32" 歌曲时长

去重策略:

  • 主键: song_name + artist(同名歌曲+同歌手视为重复)
  • 如果需要更严格的去重,可以加上 album 字段

9️⃣ 运行方式与结果展示

主程序入口

python 复制代码
# main.py
from fetcher import Fetcher
from parser import Parser
from storage import Storage

def main():
    """主流程"""
    print("🎵 网抑云音乐歌单爬虫启动...")
    
    # 初始化组件
    fetcher = Fetcher()
    parser = Parser()
    storage = Storage()
    
    # 1. 获取歌单分类页(以"华语"分类为例)
    category_url = 'https://music.163.com/#/discover/playlist/?cat=华语'
    print(f"📡 正在请求歌单分类页...")
    
    category_html = fetcher.get(category_url)
    if not category_html:
        print("❌ 获取分类页失败")
        return
    
    # 2. 提取歌单ID列表
    playlist_ids = parser.extract_playlist_ids(category_html)
    print(f"✅ 找到 {len(playlist_ids)} 个歌单")
    
    # 3. 遍历每个歌单
    all_songs = []
    
    for idx, playlist_id in enumerate(playlist_ids[:5], 1):  # 限制前5个歌单
        print(f"\n[{idx}/{len(playlist_ids[:5])}] 正在处理歌单 ID: {playlist_id}")
        
        # 请求歌单详情页
        playlist_url = f'https://music.163.com/playlist?id={playlist_id}'
        playlist_html = fetcher.get(playlist_url)
        
        if not playlist_html:
            continue
        
        # 提取歌单信息
        playlist_info = parser.extract_playlist_info(playlist_html, playlist_id)
        print(f"  歌单: {playlist_info['playlist_name']}")
        print(f"  创建者: {playlist_info['creator']}")
        
        # 提取歌曲列表
        songs = parser.extract_songs(playlist_html)
        print(f"  歌曲数: {len(songs)}")
        
        # 合并歌单信息到每首歌
        for song in songs:
            song.update(playlist_info)
            all_songs.append(song)
    
    # 4. 保存到 CSV
    print("\n💾 正在保存数据...")
    storage.save_to_csv(all_songs)
    
    print("\n🎉 爬取完成!")

if __name__ == '__main__':
    main()

启动命令

bash 复制代码
# 进入项目目录
cd netease_music_spider

# 运行爬虫
python main.py

运行日志示例

复制代码
🎵 网抑云音乐歌单爬虫启动...
📡 正在请求歌单分类页...
✅ 找到 35 个歌单

[1/5] 正在处理歌单 ID: 3778678
  歌单: 云音乐飙升榜
  创建者: 网抑云音乐
  歌曲数: 100

[2/5] 正在处理歌单 ID: 19723756
  歌单: 云音乐热歌榜
  创建者: 网抑云音乐
  歌曲数: 100

💾 正在保存数据...
✅ 数据已保存: data/netease_playlists_20250211_143022.csv
📊 共 487 条记录

🎉 爬取完成!

结果展示(前5行)

playlist_name creator song_name artist album duration
云音乐飙升榜 网抑云音乐 孤勇者 陈奕迅 孤勇者 04:16
云音乐飙升榜 网抑云音乐 本草纲目 周杰伦 依然范特西 03:00
云音乐热歌榜 网抑云音乐 晴天 周杰伦 叶惠美 04:32
云音乐热歌榜 网抑云音乐 夜空中最亮的星 逃跑计划 世界 04:32
华语经典金曲 音乐推荐官 稻香 周杰伦 魔杰座 03:43

🔟 常见问题与排错

Q1: 返回 403 Forbidden 怎么办?

原因分析:

  • User-Agent 不够真实
  • 请求频率过快被限流
  • IP 被临时封禁

解决方案:

python 复制代码
# 1. 更换更真实的 UA(可以从浏览器复制)
headers['User-Agent'] = '你的浏览器UA'

# 2. 增加延迟
time.sleep(random.uniform(3, 5))  # 3-5秒

# 3. 使用代理IP(进阶)
proxies = {'http': 'http://proxy_ip:port'}
response = session.get(url, proxies=proxies)

Q2: 解析不到歌曲列表(songs = [])?

原因分析:

  • 网抑云音乐更新了页面结构
  • CSS 选择器失效
  • 页面需要登录才能查看完整列表

解决方案:

python 复制代码
# 1. 重新打开开发者工具,检查最新的 HTML 结构
# 2. 更新选择器
song_list = soup.select('新的选择器路径')

# 3. 如果是登录墙,可以尝试带 Cookie
headers['Cookie'] = '你的登录Cookie'

Q3: CSV 文件打开后乱码?

原因分析:

  • Windows Excel 默认用 GBK 编码打开 CSV

解决方案:

python 复制代码
# 保存时加 BOM 头(已在代码中添加)
df.to_csv(filepath, encoding='utf-8-sig')  # ← sig 是关键

Q4: 部分歌曲时长显示为 00:00?

原因分析:

  • 有些歌曲在网页上确实没有时长信息(比如独家曲目)
  • 选择器定位错误

解决方案:

python 复制代码
# 在解析时添加默认值
duration = duration_tag.text.strip() if duration_tag else '未知'

# 后期清洗数据
df['duration'] = df['duration'].replace('00:00', '未知')

Q5: 想爬更多歌单怎么办?

解决方案:

python 复制代码
# 方法1: 修改限制数量
for idx, playlist_id in enumerate(playlist_ids[:50], 1):  # 前50个

# 方法2: 翻页(网抑云分类页支持翻页)
for page in range(1, 6):  # 前5页
    url = f'https://music.163.com/#/discover/playlist/?cat=华语&offset={page*35}'

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

并发加速(线程池)

单线程太慢?用 concurrent.futures 开启多线程:

python 复制代码
from concurrent.futures import ThreadPoolExecutor, as_completed

def crawl_playlist(playlist_id):
    """爬取单个歌单(线程任务)"""
    fetcher = Fetcher()
    parser = Parser()
    
    url = f'https://music.163.com/playlist?id={playlist_id}'
    html = fetcher.get(url)
    
    if html:
        info = parser.extract_playlist_info(html, playlist_id)
        songs = parser.extract_songs(html)
        return info, songs
    return None, []

# 主程序
with ThreadPoolExecutor(max_workers=5) as executor:
    futures = {executor.submit(crawl_playlist, pid): pid for pid in playlist_ids}
    
    for future in as_completed(futures):
        info, songs = future.result()
        # 处理结果...

注意: 不要设置过大的 max_workers,建议 ≤ 5,否则会被封 IP。

断点续跑(游标记录)

爬到一半程序崩溃?用文件记录已爬取的 ID:

python 复制代码
import json

# 保存进度
def save_progress(crawled_ids, filename='progress.json'):
    with open(filename, 'w') as f:
        json.dump(crawled_ids, f)

# 加载进度
def load_progress(filename='progress.json'):
    try:
        with open(filename, 'r') as f:
            return set(json.load(f))
    except FileNotFoundError:
        return set()

# 主程序
crawled = load_progress()

for pid in playlist_ids:
    if pid in crawled:
        continue  # 跳过已爬的
    
    # 爬取逻辑...
    
    crawled.add(pid)
    save_progress(list(crawled))

日志与监控

logging 模块记录运行状态:

python 复制代码
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[
        logging.FileHandler('logs/spider.log'),
        logging.StreamHandler()
    ]
)

logger = logging.getLogger(__name__)

# 使用
logger.info(f"开始爬取歌单 {playlist_id}")
logger.error(f"请求失败: {url}")

定时任务(每天更新榜单)

Linux 用 cron,Windows 用任务计划程序:

bash 复制代码
# Linux crontab 示例(每天凌晨3点运行)
0 3 * * * /usr/bin/python3 /path/to/main.py >> /path/to/logs/cron.log 2>&1

或者用 Python 的 schedule 库:

python 复制代码
import schedule
import time

def job():
    print("🕐 定时任务启动...")
    main()

schedule.every().day.at("03:00").do(job)

while True:
    schedule.run_pending()
    time.sleep(60)

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

我们完成了什么?

通过这个项目,我们实现了:

  • ✅ 从零搭建一个完整的 Python 爬虫项目
  • ✅ 掌握了静态网页的请求、解析、存储全流程
  • ✅ 学会了应对反爬策略(UA 伪装、频率控制)
  • ✅ 获得了 500+ 条真实的网抑云音乐数据

下一步可以做什么?

1. 数据分析方向:

  • 用 pandas 分析热门歌单的歌曲时长分布
  • 用 matplotlib 绘制歌手出现频率柱状图
  • 用词云展示歌曲名/歌单名的高频词

2. 爬虫进阶方向:

  • 动态网页爬取: 学习 Selenium/Playwright,爬取 Ajax 加载的评论数据
  • Scrapy 框架: 改写成 Scrapy 项目,利用其管道、中间件机制
  • 分布式爬虫: 接入 Redis + Celery,多机协同爬取

3. 工程化方向:

  • Docker 容器化: 打包成镜像,一键部署
  • 前端可视化: 用 Flask + ECharts 做个歌单数据可视化网站
  • API 服务: 把爬虫封装成 RESTful API,供其他项目调用

推荐阅读

书籍:

  • 《Python 网络爬虫从入门到实践》- 适合新手系统学习
  • 《Web Scraping with Python》(英文)- 覆盖进阶技巧

官方文档:

社区资源:

最后的话

写爬虫就像在和网站做一场优雅的舞蹈------你需要理解对方的节奏(反爬策略),掌握自己的步伐(请求频率),还要懂得进退有度(合规边界)。

希望这篇教程能帮你打开 Python 爬虫的大门。技术本身没有善恶,关键在于我们如何使用它。用爬虫做数据分析、学习研究,这是技术的正确打开方式。

如果你觉得有收获,不妨动手试试,然后在评论区分享你的爬取成果!💪

Happy Coding!

🌟 文末

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

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

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

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

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

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

✅ 互动征集

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

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


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

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

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


✅ 免责声明

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

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

  • 合法使用: 不得将本项目用于任何违法、违规或侵犯他人权益的行为,包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。
  • 风险自负: 任何因使用本项目而产生的法律责任、技术风险或经济损失,由使用者自行承担,项目作者不承担任何形式的责任。
  • 禁止滥用: 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。
  • 使用或者参考本项目即视为同意上述条款,即 "谁使用,谁负责" 。如不同意,请立即停止使用并删除本项目。!!!
相关推荐
skywalk81632 小时前
LTX-2 是一个基于 Transformer 的视频生成模型,能够根据文本描述生成高质量视频
python·深度学习·transformer
不懒不懒2 小时前
【Python办公自动化进阶指南:系统交互与网页操作实战】
开发语言·python·交互
会周易的程序员2 小时前
cNetgate插件架构设计详解 动态库 脚本二开lua, python, javascript
javascript·c++·python·物联网·lua·iot
小雨中_3 小时前
3.7 GSPO:Group Sequence Policy Optimization(组序列策略优化)
人工智能·python·深度学习·机器学习·自然语言处理
zchxzl4 小时前
亲测2026京津冀专业广告展会
大数据·人工智能·python
~央千澈~4 小时前
抖音弹幕游戏开发之第19集:课程总结与答疑·优雅草云桧·卓伊凡
python·pygame
小雨中_5 小时前
3.5 ReMax:用 Greedy 作为基线的 REINFORCE + RLOO
人工智能·python·深度学习·机器学习·自然语言处理
overmind6 小时前
oeasy Python 116 用列表乱序shuffle来洗牌抓拍玩升级拖拉机
服务器·windows·python
A懿轩A6 小时前
【Java 基础编程】Java 枚举与注解从零到一:Enum 用法 + 常用注解 + 自定义注解实战
java·开发语言·python