admin管理员组

文章数量:1650913

目标

主要目的为爬取酷狗音乐网站 mp3 任意音乐数据,并将爬取后的mp3数据保存为以.mp3后缀的音频文件

所用模块

hashlib => md5加密:提供了许多加密哈希算法,包括MD5、SHA-1、SHA-256等。
os      => 提供了访问操作系统功能的方法,例如文件操作、目录操作、进程管理等。
time    => 提供了处理时间的函数,包括获取当前时间、延时等功能。
requests=> 用于发送HTTP请求和处理响应。
re      => 提供了正则表达式操作的函数,用于在字符串中搜索、匹配和替换文本。
json    => 用于处理JSON数据,用于JSON格式转换。
tkinter => 提供了创建图形用户界面(GUI)的工具包,可以用于构建窗口、按钮、文本框等GUI元素。

基本思路

一 :音乐获取

打开浏览器,进入酷狗音乐网页,点击F12进入开发者模式

 点击上面一栏的媒体,可以看到mp3音乐文件

 可以发现这个文件就是当前音乐mp3媒体文件,但是我们需要找到它的来源处,可以在搜索出直接搜索横线那部分

 对比一下我们可以发现,我们所需要的mp3文件来源于此处。且位于play_url参数中,因此我们现在需要的就是爬取当前此文件。

 所以,我们先确定它的url。通过对比url与字符串参数发现,网页url只为“https://wwwapi.kugou/play/songinfo?”,其余都只是参数。

我们再取对比其他音乐的当前地址,发现他们只有“clienttime”,"encode","signature"这几个参数有变化。因此,我们可以知道,在爬取其他音乐时我们也只是需要改这几个参数便可以。

其中,发现“clienttime”为时间戳,"encode"为音乐ID,"signature"为md5加密的参数所以,我们只需要解密'signature'此参数,我们可以通过Ctrl+Swift+f打开全局搜索,搜索'signature',发现signature参数来与此js中,我们可以点击进入js查看

进入此处,我们在signature所需要的参数此行点击打上断电,并刷新页面。鼠标选中参数详细。会发现,此处就是我们所需要的参数。32位字母数字组合。再去查看它组合方式,就会发现它就是s的参数列表,转换成字符串直接用md5加密便可。而再通过对比一下s发现它的参数除了时间戳和音乐id完全一样,因此我们可以自己组合

而后组合加密得出signature参数,便可通过改变音乐id直接请求当前音乐的MP3信息。

二: 搜索指定获取

简单描述此操作 就是输入你想要的 音乐名 或 音乐名加作者名 搜索到音乐,获取 音乐ID 交给
第一步操作获得音乐mp3文件

我们现在需要模拟酷狗搜索栏操作,通过关键字搜索,并获取与关键字最相识的第一个音乐。

继续之前的操作,F12进入,并在左上角点击搜索按钮,搜索当前音乐名,点击进入。并进行对比发现song就是我们需要的数据

进入之后,我们会发现,搜索的所有的音乐内容都在lists中,我们只需要第一条的音乐内容,
并获取此音乐的ID与名字

再通过对比参数中signature发现和之前参数区别一样。因此,复制之前的操作便可

具体的步骤为同样搜索signature找到其js文件,打上断点调试,复制第一步的操作,得到signature参数,加入需要请求的url地址中“https://complexsearch.kugou/v2/search/song?”,便可得到搜索的音乐id  "EAlbumID"。将其给到第一步id中运行,便可以得到想要的音乐mp3数据。

三: 保存为mp3文件

获取到此音频地址后,对地址发送请求,将请求得到的数据以二进制的方式保存到指定目录文件夹中

代码部分

用到的模块
import hashlib
import os
import time
import requests
import re
import json
from tkinter import *
判断文件是否存在。不存在则创建改文件。        
此文件为获取后保存的文件

def directory_create():
    """判断文件是否存在。不存在则创建改文件"""
    directory = "./music_files"
    if not os.path.exists(directory):
        os.makedirs(directory)
通过搜索栏参数(音乐名)获取 搜索第一个的 音乐名 和 音乐id。  也就是第二个步骤(搜索指定获取)操作。
def audio_id_list(music_name):
    """
    通过搜索栏参数(音乐名)获取 搜索第一个的 音乐名 and ID
    :param music_name: 搜索栏参数(音乐名) 例如: 苏星婕 - 把回忆拼好给你
    :return: 音乐名  音乐ID(苏星婕 - 把回忆拼好给你   72jrv7fa)
    """
    timestamp = int(time.time() * 1000)

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
    }
    sign = MD5_sign_search(timestamp, music_name)
    datas = {
        'callback': 'callback123',
        'srcappid': '2919',
        'clientver': '1000',
        'clienttime': timestamp,
        'mid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'uuid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'dfid': '3MmrUf3e5zpy3cStkN3Bn9oS',
        'keyword': music_name,
        'page': '1',
        'pagesize': '30',
        'bitrate': '0',
        'isfuzzy': '0',
        'inputtype': '0',
        'platform': 'WebFilter',
        'userid': '2078452878',
        'iscorrection': '1',
        'privilege_filter': '0',
        'filter': '10',
        'token': '483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
        'appid': '1014',
        'signature': sign,
    }
    response = requests.get(url='https://complexsearch.kugou/v2/search/song?', headers=headers, params=datas)
    callback_dict = re.findall('callback123\((.*)\)', response.text)[0]
    jsurl = json.loads(callback_dict)
    fileName = jsurl['data']['lists'][0]['FileName']
    eMixSongID = jsurl['data']['lists'][0]['EMixSongID']
    return fileName, eMixSongID
通过音乐ID爬取当前音乐的md3地址。也就是第一个操作部分(音乐获取)
def fetch_url(audio_id):
    """
    通过音乐ID爬取当前音乐的md3地址
    :param audio_id: 音乐ID(72jrv7fa)
    :return:音乐url(........mp3)
    """
    timestamp = int(time.time() * 1000)
    print('audio_id:', audio_id)
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
    }
    sign = MD5_sign(timestamp, audio_id)
    datas = {
        'srcappid': '2919',
        'clientver': '20000',
        'clienttime': timestamp,
        'mid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'uuid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'dfid': '3MmrUf3e5zpy3cStkN3Bn9oS',
        'appid': '1014',
        'platid': '4',
        'encode_album_audio_id': audio_id,
        'token': '483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
        'userid': '2078452878',
        'signature': sign,
    }
    response = requests.get(url='https://wwwapi.kugou/play/songinfo?', headers=headers, params=datas)
    jsurl = response.json()
    # print('jsurl: ', jsurl)
    play_url = jsurl['data']['play_url']
    return play_url
获取到此音频地址后,对地址发送请求,将请求得到的数据以二进制的方式保存到指定目录文件夹中。
def download_url(file_name, url_mp3):
    """
    通过已经获取的mp3文件保存到文件夹中
    :param file_name: 音乐名
    :param url_mp3: 音乐url(.....mp3)
    :return: 无
    """
    response = requests.get(url_mp3)
    try:
        with open(f"./music_files/{file_name}.mp3", "wb") as f:
            f.write(response.content)
    except:
        with open(f"./music_files/{int(time.time() * 1000)}.mp3", "wb") as f:
            f.write(response.content)

    print(f'{file_name}-----下载成功')
搜索页面和播放页面的两个signature的解密操作:MD5加密
def MD5_sign(timestamp, audio_id):
    """
    通过音乐id解密详情页单个音乐的signature参数
    :param timestamp: 时间戳
    :param audio_id: 音乐id(例如:72jrv7fa)
    :return:
    """
    signature_list = ['NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt',
                      'appid=1014',
                      f'clienttime={timestamp}',
                      'clientver=20000',
                      'dfid=3MmrUf3e5zpy3cStkN3Bn9oS',
                      f'encode_album_audio_id={audio_id}',
                      'mid=c4de83c1ebb2e73fc5ae95304a674918',
                      'platid=4',
                      'srcappid=2919',
                      'token=483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
                      'userid=2078452878',
                      'uuid=c4de83c1ebb2e73fc5ae95304a674918',
                      'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt']
    string = "".join(signature_list)
    MD5 = hashlib.md5()
    MD5.update(string.encode('utf-8'))
    sign = MD5.hexdigest()  # md5 32位加密内容
    return sign


def MD5_sign_search(timestamp, music_name):
    """
    通过音乐id解密搜索页的signature参数
    :param timestamp:时间戳
    :param music_name:搜索框音乐名(例如:把回忆拼好给你)
    :return:加密后32位md5参数(例如:72181cc6baf76ee0404837d5d657dd5c)
    """
    signature_list = ['NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt',
                      'appid=1014',
                      'bitrate=0',
                      'callback=callback123',
                      f'clienttime={timestamp}',
                      'clientver=1000',
                      'dfid=3MmrUf3e5zpy3cStkN3Bn9oS',
                      'filter=10',
                      'inputtype=0',
                      'iscorrection=1',
                      'isfuzzy=0',
                      f'keyword={music_name}',
                      'mid=c4de83c1ebb2e73fc5ae95304a674918',
                      'page=1',
                      'pagesize=30',
                      'platform=WebFilter',
                      'privilege_filter=0',
                      'srcappid=2919',
                      'token=483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
                      'userid=2078452878',
                      'uuid=c4de83c1ebb2e73fc5ae95304a674918',
                      'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt']
    string = "".join(signature_list)
    MD5 = hashlib.md5()
    MD5.update(string.encode('utf-8'))
    sign_lis = MD5.hexdigest()  # md5 32位加密内容
    return sign_lis

总代码

总结

import hashlib
import os
import time
import requests
import re
import json
from tkinter import *


def MD5_sign(timestamp, audio_id):
    """
    通过音乐id解密详情页单个音乐的signature参数
    :param timestamp: 时间戳
    :param audio_id: 音乐id(例如:72jrv7fa)
    :return:
    """
    signature_list = ['NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt',
                      'appid=1014',
                      f'clienttime={timestamp}',
                      'clientver=20000',
                      'dfid=3MmrUf3e5zpy3cStkN3Bn9oS',
                      f'encode_album_audio_id={audio_id}',
                      'mid=c4de83c1ebb2e73fc5ae95304a674918',
                      'platid=4',
                      'srcappid=2919',
                      'token=483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
                      'userid=2078452878',
                      'uuid=c4de83c1ebb2e73fc5ae95304a674918',
                      'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt']
    string = "".join(signature_list)
    MD5 = hashlib.md5()
    MD5.update(string.encode('utf-8'))
    sign = MD5.hexdigest()  # md5 32位加密内容
    return sign


def MD5_sign_search(timestamp, music_name):
    """
    通过音乐id解密搜索页的signature参数
    :param timestamp:时间戳
    :param music_name:搜索框音乐名(例如:把回忆拼好给你)
    :return:加密后32位md5参数(例如:72181cc6baf76ee0404837d5d657dd5c)
    """
    signature_list = ['NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt',
                      'appid=1014',
                      'bitrate=0',
                      'callback=callback123',
                      f'clienttime={timestamp}',
                      'clientver=1000',
                      'dfid=3MmrUf3e5zpy3cStkN3Bn9oS',
                      'filter=10',
                      'inputtype=0',
                      'iscorrection=1',
                      'isfuzzy=0',
                      f'keyword={music_name}',
                      'mid=c4de83c1ebb2e73fc5ae95304a674918',
                      'page=1',
                      'pagesize=30',
                      'platform=WebFilter',
                      'privilege_filter=0',
                      'srcappid=2919',
                      'token=483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
                      'userid=2078452878',
                      'uuid=c4de83c1ebb2e73fc5ae95304a674918',
                      'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt']
    string = "".join(signature_list)
    MD5 = hashlib.md5()
    MD5.update(string.encode('utf-8'))
    sign_lis = MD5.hexdigest()  # md5 32位加密内容
    return sign_lis


def fetch_url(audio_id):
    """
    通过音乐ID爬取当前音乐的md3地址
    :param audio_id: 音乐ID(72jrv7fa)
    :return:音乐url(........mp3)
    """
    timestamp = int(time.time() * 1000)
    print('audio_id:', audio_id)
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
    }
    sign = MD5_sign(timestamp, audio_id)
    datas = {
        'srcappid': '2919',
        'clientver': '20000',
        'clienttime': timestamp,
        'mid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'uuid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'dfid': '3MmrUf3e5zpy3cStkN3Bn9oS',
        'appid': '1014',
        'platid': '4',
        'encode_album_audio_id': audio_id,
        'token': '483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
        'userid': '2078452878',
        'signature': sign,
    }
    response = requests.get(url='https://wwwapi.kugou/play/songinfo?', headers=headers, params=datas)
    jsurl = response.json()
    # print('jsurl: ', jsurl)
    play_url = jsurl['data']['play_url']
    return play_url


def audio_id_list(music_name):
    """
    通过搜索栏参数(音乐名)获取 搜索第一个的 音乐名 and ID
    :param music_name: 搜索栏参数(音乐名) 例如: 苏星婕 - 把回忆拼好给你
    :return: 音乐名  音乐ID(苏星婕 - 把回忆拼好给你   72jrv7fa)
    """
    timestamp = int(time.time() * 1000)

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
    }
    sign = MD5_sign_search(timestamp, music_name)
    datas = {
        'callback': 'callback123',
        'srcappid': '2919',
        'clientver': '1000',
        'clienttime': timestamp,
        'mid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'uuid': 'c4de83c1ebb2e73fc5ae95304a674918',
        'dfid': '3MmrUf3e5zpy3cStkN3Bn9oS',
        'keyword': music_name,
        'page': '1',
        'pagesize': '30',
        'bitrate': '0',
        'isfuzzy': '0',
        'inputtype': '0',
        'platform': 'WebFilter',
        'userid': '2078452878',
        'iscorrection': '1',
        'privilege_filter': '0',
        'filter': '10',
        'token': '483ef68936faa09268f3a42f7ab7ee31b584a3f155828a100c95fadf7c5ddd1e',
        'appid': '1014',
        'signature': sign,
    }
    response = requests.get(url='https://complexsearch.kugou/v2/search/song?', headers=headers, params=datas)
    callback_dict = re.findall('callback123\((.*)\)', response.text)[0]
    jsurl = json.loads(callback_dict)
    fileName = jsurl['data']['lists'][0]['FileName']
    eMixSongID = jsurl['data']['lists'][0]['EMixSongID']
    return fileName, eMixSongID


def download_url(file_name, url_mp3):
    """
    通过已经获取的mp3文件保存到文件夹中
    :param file_name: 音乐名
    :param url_mp3: 音乐url(.....mp3)
    :return: 无
    """
    response = requests.get(url_mp3)
    try:
        with open(f"./music_files/{file_name}.mp3", "wb") as f:
            f.write(response.content)
    except:
        with open(f"./music_files/{int(time.time() * 1000)}.mp3", "wb") as f:
            f.write(response.content)

    print(f'{file_name}-----下载成功')


def directory_create():
    """判断文件是否存在。不存在则创建改文件"""
    directory = "./music_files"
    if not os.path.exists(directory):
        os.makedirs(directory)


if __name__ == '__main__':
    directory_create()  # 判断music_flie文件是否存在

    music_name = '听说你'
    audio_id = audio_id_list(music_name)  # (苏星婕 - 把回忆拼好给你, 72jrv7fa)
    file_name = audio_id[0]  # 苏星婕 - 把回忆拼好给你
    emixsong_id = audio_id[1]  # 72jrv7fa
    time.sleep(2)
    url_mp3 = fetch_url(emixsong_id)  # 获取 ......mp3
    download_url(file_name, url_mp3)  # 下载保存

本次爬取结束,如果有什么其他问题或不懂可以私信哦~

另外还有GUI渲染过的的exe便携版,想要了解的话可以看下一篇文章

本文标签: 爬虫实战酷狗操作项目