Python 自动化爬取网易云音乐歌手歌词实战教程

简介: Python 自动化爬取网易云音乐歌手歌词实战教程

网易云音乐歌词数据分散于多页面,手动复制效率低下、易出现内容遗漏,且无法满足批量采集需求。自动化爬取面临两大核心技术难点:其一,歌词数据通过 AJAX 异步动态加载,原生requests仅能获取静态空壳 HTML,无法直接解析有效数据;其二,平台反爬机制严苛,高频请求易触发 403 访问拦截、滑块验证等限制。
本文基于 Python 构建端到端企业级歌词爬取系统,覆盖 API 逆向分析、请求参数加密、请求头伪装、异常容错、本地持久化存储全流程,并集成亿牛云爬虫代理高效解决 IP 封禁问题,实现稳定、批量的歌手歌词采集。
一、环境依赖配置
各库核心作用:
requests:高性能 HTTP 请求客户端,负责发送网络请求、获取接口响应数据
demjson3:兼容非标准 JSON 格式解析,适配网易云音乐 API 非常规响应数据
cryptography:提供 AES 对称加密能力,用于生成平台接口必需的加密参数
二、API 逆向:加密参数生成
网易云音乐后端接口采用参数加密校验机制,是数据爬取的核心技术壁垒,请求参数需经过加密处理后才能正常调用。
核心加密参数说明
表格
参数名
功能说明
生成规则
params
封装业务请求参数(歌曲 ID、时间戳等)
AES-CBC 模式加密 + Base64 编码
encSecKey
加密密钥校验参数
随机生成 16 位十六进制字符串
nonce
防重放随机数
随机生成 16 位十六进制字符串
加密实现代码
python
运行
```import base64
import random
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

def generate_encrypted_params(params):
"""
网易云音乐API加密参数生成函数
:param params: 原始业务参数字典
:return: 加密后可直接用于请求的参数
"""

# 生成随机密钥与随机数
enc_sec_key = random.randbytes(16).hex()[:16]
nonce = random.randbytes(16).hex()[:16]

# 业务参数序列化
params_json = json.dumps(params)

# 网易云音乐固定加密密钥与偏移量
key = b'0CoJUmKQw8gw8ig'
iv = b'0102030405060708'

# AES-CBC加密 + Base64编码
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_data = cipher.encrypt(pad(params_json.encode('utf-8'), AES.block_size))
encrypted_params_b64 = base64.b64encode(encrypted_data).decode('utf-8')

return {
    'params': encrypted_params_b64,
    'encSecKey': enc_sec_key,
    'nonce': nonce
}

三、歌词接口请求封装
网易云音乐标准歌词 API 接口:https://music.163.com/weapi/song/lyric?csrf_token=
基于面向对象思想封装爬虫核心类,实现请求伪装、代理集成、异常处理一体化:
python
运行
```import requests
import random

class NetEaseMusicCrawler:
    def __init__(self, use_proxy=False, proxy_config=None):
        self.base_url = "https://music.163.com"
        self.use_proxy = use_proxy
        self.proxy_config = proxy_config

        # 模拟浏览器请求头,绕过基础反爬
        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': '*/*',
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Connection': 'close'
        }

    def get_lyric(self, song_id):
        """
        单首歌曲歌词获取
        :param song_id: 歌曲唯一标识ID
        :return: 原始歌词文本 / None
        """
        # 构造业务参数
        params = {'id': song_id, 'lv': -1, 'tv': -1, 'csrf_token': ''}
        encrypted_params = generate_encrypted_params(params)
        url = f"{self.base_url}/weapi/song/lyric?csrf_token="

        # 代理配置
        proxies = self._get_proxies()

        try:
            # 发送POST请求
            resp = requests.post(
                url, data=encrypted_params, headers=self.headers,
                proxies=proxies, timeout=10
            )

            # 状态码容错处理
            if resp.status_code == 200:
                return self._parse_lyric(resp.text)
            elif resp.status_code == 429:
                print(f"请求频繁(429),建议延长请求间隔")
            elif resp.status_code == 403:
                print(f"访问被拦截(403),建议切换IP或更新请求头")
            return None

        except Exception as e:
            print(f"请求异常: {str(e)}")
            return None

    def _parse_lyric(self, response_text):
        """非标准JSON歌词数据解析"""
        try:
            data = demjson3.decode(response_text)
            return data.get('lrc', {}).get('lyric', '') if data.get('code') == 200 else None
        except Exception:
            return None

    def _get_proxies(self):
        """代理获取工具方法"""
        if not self.use_proxy or not self.proxy_config:
            return None
        proxy_meta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % self.proxy_config
        proxies = {"http": proxy_meta, "https": proxy_meta}
        self.headers["Proxy-Tunnel"] = str(random.randint(1, 10000))
        return proxies

四、批量爬取歌手全量歌曲
通过歌手 ID 获取热门歌曲列表,实现批量歌词自动化下载与本地存储:
python
运行
```import os
import time

def get_artist_songs(self, artist_id):
"""获取歌手热门歌曲列表(单次最多50首)"""
url = f"{self.base_url}/weapi/artist/top/song"
params = {'id': artist_id, 'offset': 0, 'limit': 50, 'total': True}
encrypted_params = generate_encrypted_params(params)
proxies = self._get_proxies()

try:
    resp = requests.post(url, data=encrypted_params, headers=self.headers, proxies=proxies, timeout=10)
    if resp.status_code == 200:
        data = demjson3.decode(resp.text)
        return data.get('songs', []) if data.get('code') == 200 else []
except Exception:
    return []
return []

def batch_download_lyrics(self, artist_id, save_dir='netease_lyrics'):
"""
批量下载歌手歌词
:param artist_id: 歌手ID
:param save_dir: 歌词保存目录
"""
os.makedirs(save_dir, exist_ok=True)
songs = self.get_artist_songs(artist_id)
print(f"成功获取{len(songs)}首歌曲")

success_count = 0
for song in songs:
    song_id = song.get('id')
    song_name = song.get('name', '未知歌曲')
    artist_name = song.get('ar', [{}])[0].get('name', '未知歌手')

    print(f"正在下载: {artist_name} - {song_name}")
    lyric = self.get_lyric(song_id)

    if lyric:
        # 过滤文件名非法字符,避免保存失败
        valid_filename = "".join([c for c in f"{artist_name}-{song_name}" if c.isalnum() or c in (' ', '-', '_')])
        filepath = os.path.join(save_dir, f"{valid_filename}.lrc")

        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(lyric)
        print(f"  ✓ 保存成功")
        success_count += 1
    else:
        print(f"  ✗ 下载失败")

    # 控制请求频率,规避反爬
    time.sleep(random.uniform(1, 3))

print(f"\n任务完成:成功下载{success_count}/{len(songs)}首歌词")
return success_count

绑定方法到类

NetEaseMusicCrawler.get_artist_songs = get_artist_songs
NetEaseMusicCrawler.batch_download_lyrics = batch_download_lyrics


五、代理 IP 集成与反爬规避
网易云音乐对单 IP 请求频率、请求总量实施严格限制,高频访问会直接触发滑块验证、IP 永久封禁。亿牛云爬虫代理通过动态 IP 池技术,可有效分散请求来源,突破反爬限制。
代理配置与启动示例
python
运行

```def main():
    # 亿牛云隧道代理配置
    proxy_config = {
        "host": "t.16yun.cn",
        "port": "31111",
        "username": "your_username",
        "password": "your_password"
    }

    # 初始化爬虫(开启代理模式)
    crawler = NetEaseMusicCrawler(use_proxy=True, proxy_config=proxy_config)

    # 批量爬取歌词(示例:周杰伦 歌手ID=6452)
    crawler.batch_download_lyrics(artist_id="6452", save_dir='netease_lyrics')

if __name__ == '__main__':
    main()

代理核心优势
隧道代理技术:固定代理入口,每次请求自动分配独立出口 IP
海量 IP 资源:标准版 IP 池 30 万 +,加强版 80 万 +
高性能:网络延迟低至 100ms,支持毫秒级 IP 切换
高并发:QPS 上限 5-300 次 / 秒,适配批量采集场景
六、边界场景处理与性能优化
文件名合法性校验:歌曲名常包含/ \ : ?等系统非法字符,需过滤后再保存文件
HTTPS IP 粘性问题:HTTPS 请求默认存在连接复用,添加Connection: Close请求头可强制切换 IP
异常容错:新增网络超时、解析失败、空数据等场景的降级处理,提升系统稳定性
七、完整可运行代码
整合所有模块,提供开箱即用的完整实现:
```import requests
def init(self, use_proxy=False, proxy_config=None):
self.base_url = "https://music.163.com"
self.use_proxy = use_proxy
self.proxy_config = proxy_config
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://music.163.com/',
'Accept': '
/*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'close'
}

def _get_proxies(self):
    if not self.use_proxy or not self.proxy_config:
        return None
    proxy_meta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % self.proxy_config
    proxies = {"http": proxy_meta, "https": proxy_meta}
    self.headers["Proxy-Tunnel"] = str(random.randint(1, 10000))
    return proxies

def get_lyric(self, song_id):
    params = {'id': song_id, 'lv': -1, 'tv': -1, 'csrf_token': ''}
    encrypted_params = generate_encrypted_params(params)
    url = f"{self.base_url}/weapi/song/lyric?csrf_token="
    proxies = self._get_proxies()
    try:
        resp = requests.post(url, data=encrypted_params, headers=self.headers, proxies=proxies, timeout=10)
        if resp.status_code == 200:
            return self._parse_lyric(resp.text)
        return None
    except:
        return None

def _parse_lyric(self, response_text):
    try:
        data = demjson3.decode(response_text)
        return data.get('lrc', {}).get('lyric', '') if data.get('code') == 200 else None
    except:
        return None

def get_artist_songs(self, artist_id):
    url = f"{self.base_url}/weapi/artist/top/song"
    params = {'id': artist_id, 'offset': 0, 'limit': 50, 'total': True}
    encrypted_params = generate_encrypted_params(params)
    proxies = self._get_proxies()
    try:
        resp = requests.post(url, data=encrypted_params, headers=self.headers, proxies=proxies, timeout=10)
        if resp.status_code == 200:
            data = demjson3.decode(resp.text)
            return data.get('songs', []) if data.get('code') == 200 else []
    except:
        return []
    return []

def batch_download_lyrics(self, artist_id, save_dir='netease_lyrics'):
    os.makedirs(save_dir, exist_ok=True)
    songs = self.get_artist_songs(artist_id)
    success_count = 0
    for song in songs:
        song_id = song.get('id')
        song_name = song.get('name', '未知')
        artist_name = song.get('ar', [{}])[0].get('name', '未知')
        lyric = self.get_lyric(song_id)
        if lyric:
            valid_fn = "".join([c for c in f"{artist_name}-{song_name}" if c.isalnum() or c in (' ', '-', '_')])
            with open(os.path.join(save_dir, f"{valid_fn}.lrc"), 'w', encoding='utf-8') as f:
                f.write(lyric)
            success_count += 1
        time.sleep(random.uniform(1, 3))
    print(f"完成:{success_count}/{len(songs)}")

def main():
proxy_config = {"host": "t.16yun.cn", "port": "31111", "username": "your_user", "password": "your_pwd"}
crawler = NetEaseMusicCrawler(use_proxy=True, proxy_config=proxy_config)
crawler.batch_download_lyrics("6452")

if name == 'main':
main()

```

相关文章
|
7天前
|
缓存 人工智能 自然语言处理
我对比了8个Claude API中转站,踩了不少坑,总结给你
本文是个人开发者耗时1周实测的8大Claude中转平台横向评测,聚焦Claude Code真实体验:以加权均价(¥/M token)、内部汇率、缓存支持、模型真实性及稳定性为核心指标。
2973 20
|
19天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
16956 53
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
14天前
|
人工智能 JavaScript Ubuntu
低成本搭建AIP自动化写作系统:Hermes保姆级使用教程,长文和逐步实操贴图
我带着怀疑的态度,深度使用了几天,聚焦微信公众号AIP自动化写作场景,写出来的几篇文章,几乎没有什么修改,至少合乎我本人的意愿,而且排版风格,也越来越完善,同样是起码过得了我自己这一关。 这个其实OpenClaw早可以实现了,但是目前我觉得最大的区别是,Hermes会自主总结提炼,并更新你的写作技能。 相信就冲这一点,就值得一试。 这篇帖子主要就Hermes部署使用,作一个非常详细的介绍,几乎一步一贴图。 关于Hermes,无论你赞成哪种声音,我希望都是你自己动手行动过,发自内心的选择!
3117 29
|
4天前
|
人工智能 测试技术 API
阿里Qwen3.6-27B正式开源:网友直呼“太牛了”!
阿里云千问3.6系列重磅开源Qwen3.6-27B稠密大模型!官网:https://t.aliyun.com/U/JbblVp 仅270亿参数,编程能力媲美千亿模型,在SWE-bench等权威基准中表现卓越。支持多模态理解、本地部署及OpenClaw等智能体集成,已开放Hugging Face与ModelScope下载。
|
3天前
|
机器学习/深度学习 缓存 测试技术
DeepSeek-V4开源:百万上下文,Agent能力比肩顶级闭源模型
DeepSeek-V4正式开源!含V4-Pro(1.6T参数)与V4-Flash(284B参数)双版本,均支持百万token上下文。首创混合注意力架构,Agent能力、世界知识与推理性能全面领先开源模型,数学/代码评测比肩顶级闭源模型。
1593 6
|
3天前
|
人工智能 JSON BI
DeepSeek V4 来了!超越 Claude Sonnet 4.5,赶紧对接 Claude Code 体验一把
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro 的真实体验与避坑记录 本文记录我将 Claude Code 对接 DeepSeek 最新模型(V4Pro)后的真实体验,测试了 Skills 自动化查询和积木报表 AI 建表两个场景——有惊喜,也踩
1258 6