import time
import requests
import re
from urllib.parse import quote, unquote
import execjs
# 1、歌曲的下载url并不在歌曲播放页url对应的响应头中,通过js加密反爬知识我找到了以下js文件的url
# 这个js文件中包含了歌曲的MP3下载地址
# 通过分析他只有3个动态参数
# id=2090845 _=1625058238510 hash=66D3527724385C5E4BED75C35F511D15
# 歌曲专辑id(有些歌曲没有这个参数) 时间戳的毫秒整型单位 可能属于其他js文件
url = 'https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery191024918304319228413_1625127035031&hash=66D3527724385C5E4BED75C35F511D15&dfid=2AviMQ1KOZM23GqdHT1GA7o4&mid=110f34699790e9305bd8de4b81c08ba6&platid=4&album_id=2657404&_=1625127035032'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
'cookie': 'kg_mid=110f34699790e9305bd8de4b81c08ba6; kg_dfid=2AviMQ1KOZM23GqdHT1GA7o4; kg_dfid_collect=d41d8cd98f00b204e9800998ecf8427e; Hm_lvt_aedee6983d4cfc62f509129360d6bb3d=1623160563,1623239368,1623239605,1625054540; kg_mid_temp=110f34699790e9305bd8de4b81c08ba6; Hm_lpvt_aedee6983d4cfc62f509129360d6bb3d=1625054877',
'referer': 'https://www.kugou.com/'
}
# 请求数据
response = requests.get(url, headers=headers).text
# 匹配歌曲的MP3url
play_url = re.findall('"play_backup_url":"(.*?)"', response)[0]
# 对多余符号进行替换
play_url = play_url.replace('\\', '')
# 得到MP3url
print(play_url)
# 2、分析以上url的动态参数,在歌曲列表页我找到了一个js文件里面有,歌曲专辑id,hash值
# 分析他的url
# 通过不断搜索歌手和歌曲名观察他的url的变化,发现以下几个动态参数
'''
https://complexsearch.kugou.com/v2/search/song?callback=callback123&keyword=%E6%B1%AA%E5%B3%B0&page=1&pagesize=30&bitrate=0&isfuzzy=0&tag=em&inputtype=0&platform=WebFilter&userid=0&clientver=2000&iscorrection=1&privilege_filter=0&srcappid=2919&clienttime=1625060759083&mid=1625060759083&uuid=1625060759083&dfid=-&signature=AC278C3064E1FA3B5D40DFE4BD73227C #汪峰
https://complexsearch.kugou.com/v2/search/song?callback=callback123&keyword=%E5%91%A8%E6%9D%B0%E4%BC%A6&page=1&pagesize=30&bitrate=0&isfuzzy=0&tag=em&inputtype=0&platform=WebFilter&userid=0&clientver=2000&iscorrection=1&privilege_filter=0&srcappid=2919&clienttime=1625060712634&mid=1625060712634&uuid=1625060712634&dfid=-&signature=3525EC996E081FD184FBF1EC5EEAE6C7 # 周杰伦
https://complexsearch.kugou.com/v2/search/song?callback=callback123&keyword=faded&page=1&pagesize=30&bitrate=0&isfuzzy=0&tag=em&inputtype=0&platform=WebFilter&userid=0&clientver=2000&iscorrection=1&privilege_filter=0&srcappid=2919&clienttime=1625060627795&mid=1625060627795&uuid=1625060627795&dfid=-&signature=B243AC07C355AD66ACAFD65A19063F11 # faded
'''
# keyword=%E6%B1%AA%E5%B3%B0 搜索的关键字,密文形式
# clienttime=1625060759083&mid=1625060759083&uuid=1625060759083 毫秒单位的时间戳
# signature=AC278C3064E1FA3B5D40DFE4BD73227C 这个参数为32位,猜测是MD5加密,通过用歌手名,时间戳,加盐等各种方法尝试破解失败
# 于是我去全局搜索中搜索关键字signature找到了一个js文件,在文件中搜索signature
# 找到一句代码 h.signature = faultylabs.MD5(o.join(""))
# 分析这句代码o.join("")连接 faultylabs.MD5 md5加密
# 可怜的我看不懂js代码,但我看得懂MD5,猜测它及有可能是参数加密文件
# 通过这句代码打断点,看见了o变量中包含的参数
# 如下,除了时间戳其他为静态参数
'''
0: "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt"
1: "bitrate=0"
2: "callback=callback123"
3: "clienttime=1625116357427" # 时间戳
4: "clientver=2000"
5: "dfid=-"
6: "inputtype=0"
7: "iscorrection=1"
8: "isfuzzy=0"
9: "keyword=许巍"
10: "mid=1625116357427" # 时间戳
11: "page=1"
12: "pagesize=30"
13: "platform=WebFilter"
14: "privilege_filter=0"
15: "srcappid=2919"
16: "tag=em"
17: "userid=0"
18: "uuid=1625116357427" # 时间戳
19: "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt"
'''
# 3、用python模拟加密过程得到signature的值
# 接下来就好办了 复制signature参数的js加密文件代码,到pycharm新建一个js文件,将代码放进去
# 手动构造o变量中的参数
keyword = 'faded'
ttime = str(round(time.time() * 1000))
signa = f'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwtbitrate=0callback=callback123clienttime={ttime}clientver=2000dfid=-inputtype=0iscorrection=1isfuzzy=0keyword={keyword}mid={ttime}page=1pagesize=30platform=WebFilterprivilege_filter=0srcappid=2919tag=emuserid=0uuid={ttime}NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt'
# 读取js文件内容
with open("酷狗js逆向解析.js", "r", encoding='utf-8') as f:
js_str = f.read()
# 通过js文件中逻辑数据,对文件进行加密
# 把str转换为js数据
js_obj = execjs.compile(js_str)
# 调用函数 传参
signature = js_obj.call('faultylabs.MD5', signa)
# 得到加密参数
print(signature)
# 对列表页的url进行拼接
url_password = f"https://complexsearch.kugou.com/v2/search/song?callback=callback123&keyword={quote(keyword)}&page=1&pagesize=30&bitrate=0&isfuzzy=0&tag=em&inputtype=0&platform=WebFilter&userid=0&clientver=2000&iscorrection=1&privilege_filter=0&srcappid=2919&clienttime={ttime}&mid={ttime}&uuid={ttime}&dfid=-&signature={signature}"
# 得到动态参数的url
print(url_password)
# 爬虫步骤
# 1 模拟js加密过程,对列表页的url进行拼接
# 2 请求列表页的url,获取响应
# 3 提取需要的动态数据 id hash
# 4 用动态数据拼接歌曲详情页的url
# 5 请求数据,获取响应,提取MP3下载地址,保存数据