python 根据列表批量下载网易云音乐的免费音乐


Posted in Python onDecember 03, 2020

运行效果

python 根据列表批量下载网易云音乐的免费音乐

代码

# -*- coding:utf-8 -*-
import requests, hashlib, sys, click, re, base64, binascii, json, os
from Crypto.Cipher import AES
from http import cookiejar

"""
Website:http://cuijiahua.com
Author:Jack Cui
Refer:https://github.com/darknessomi/musicbox
"""

class Encrypyed():
	"""
	解密算法
	"""
	def __init__(self):
		self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
		self.nonce = '0CoJUm6Qyw8W8jud'
		self.pub_key = '010001'

	# 登录加密算法, 基于https://github.com/stkevintan/nw_musicbox脚本实现
	def encrypted_request(self, text):
		text = json.dumps(text)
		sec_key = self.create_secret_key(16)
		enc_text = self.aes_encrypt(self.aes_encrypt(text, self.nonce), sec_key.decode('utf-8'))
		enc_sec_key = self.rsa_encrpt(sec_key, self.pub_key, self.modulus)
		data = {'params': enc_text, 'encSecKey': enc_sec_key}
		return data

	def aes_encrypt(self, text, secKey):
		pad = 16 - len(text) % 16
		text = text + chr(pad) * pad
		encryptor = AES.new(secKey.encode('utf-8'), AES.MODE_CBC, b'0102030405060708')
		ciphertext = encryptor.encrypt(text.encode('utf-8'))
		ciphertext = base64.b64encode(ciphertext).decode('utf-8')
		return ciphertext

	def rsa_encrpt(self, text, pubKey, modulus):
		text = text[::-1]
		rs = pow(int(binascii.hexlify(text), 16), int(pubKey, 16), int(modulus, 16))
		return format(rs, 'x').zfill(256)

	def create_secret_key(self, size):
		return binascii.hexlify(os.urandom(size))[:16]


class Song():
	"""
	歌曲对象,用于存储歌曲的信息
	"""
	def __init__(self, song_id, song_name, song_num, song_url=None):
		self.song_id = song_id
		self.song_name = song_name
		self.song_num = song_num
		self.song_url = '' if song_url is None else song_url

class Crawler():
	"""
	网易云爬取API
	"""
	def __init__(self, timeout=60, cookie_path='.'):
		self.headers = {
			'Accept': '*/*',
			'Accept-Encoding': 'gzip,deflate,sdch',
			'Accept-Language': 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
			'Connection': 'keep-alive',
			'Content-Type': 'application/x-www-form-urlencoded',
			'Host': 'music.163.com',
			'Referer': 'http://music.163.com/search/',
			'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
		}
		self.session = requests.Session()
		self.session.headers.update(self.headers)
		self.session.cookies = cookiejar.LWPCookieJar(cookie_path)
		self.download_session = requests.Session()
		self.timeout = timeout
		self.ep = Encrypyed()

	def post_request(self, url, params):
		"""
		Post请求
		:return: 字典
		"""

		data = self.ep.encrypted_request(params)
		resp = self.session.post(url, data=data, timeout=self.timeout)
		result = resp.json()
		if result['code'] != 200:
			click.echo('post_request error')
		else:
		  return result

	def search(self, search_content, search_type, limit=9):
		"""
		搜索API
		:params search_content: 搜索内容
		:params search_type: 搜索类型
		:params limit: 返回结果数量
		:return: 字典.
		"""

		url = 'http://music.163.com/weapi/cloudsearch/get/web?csrf_token='
		params = {'s': search_content, 'type': search_type, 'offset': 0, 'sub': 'false', 'limit': limit}
		result = self.post_request(url, params)
		return result

	def search_song(self, song_name, song_num, quiet=True, limit=9):
		"""
		根据音乐名搜索
		:params song_name: 音乐名
		:params song_num: 下载的歌曲数
		:params quiet: 自动选择匹配最优结果
		:params limit: 返回结果数量
		:return: Song独享
		"""

		result = self.search(song_name, search_type=1, limit=limit)

		if result['result']['songCount'] <= 0:
			click.echo('Song {} not existed.'.format(song_name))
		else:
			songs = result['result']['songs']
			if quiet:
				song_id, song_name = songs[0]['id'], songs[0]['name']
				song = Song(song_id=song_id, song_name=song_name, song_num=song_num)
				return song

	def get_song_url(self, song_id, bit_rate=320000):
		"""
		获得歌曲的下载地址
		:params song_id: 音乐ID<int>.
		:params bit_rate: {'MD 128k': 128000, 'HD 320k': 320000}
		:return: 歌曲下载地址
		"""

		url = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token='
		csrf = ''
		params = {'ids': [song_id], 'br': bit_rate, 'csrf_token': csrf}
		result = self.post_request(url, params)
		# 歌曲下载地址
		song_url = result['data'][0]['url']

		# 歌曲不存在
		if song_url is None:
			click.echo('Song {} is not available due to copyright issue.'.format(song_id))
		else:
			return song_url

	def get_song_by_url(self, song_url, song_name, song_num, folder):
		"""
		下载歌曲到本地
		:params song_url: 歌曲下载地址
		:params song_name: 歌曲名字
		:params song_num: 下载的歌曲数
		:params folder: 保存路径
		"""
		if not os.path.exists(folder):
			os.makedirs(folder)
		fpath = os.path.join(folder, str(song_num) + '_' + song_name + '.mp3')
		if sys.platform == 'win32' or sys.platform == 'cygwin':
			valid_name = re.sub(r'[<>:"/\\|?*]', '', song_name)
			if valid_name != song_name:
				click.echo('{} will be saved as: {}.mp3'.format(song_name, valid_name))
				fpath = os.path.join(folder, str(song_num) + '_' + valid_name + '.mp3')
		
		if not os.path.exists(fpath):
			resp = self.download_session.get(song_url, timeout=self.timeout, stream=True)
			length = int(resp.headers.get('content-length'))
			label = 'Downloading {} {}kb'.format(song_name, int(length/1024))

			with click.progressbar(length=length, label=label) as progressbar:
				with open(fpath, 'wb') as song_file:
					for chunk in resp.iter_content(chunk_size=1024):
						if chunk:
							song_file.write(chunk)
							progressbar.update(1024)


class Netease():
	"""
	网易云音乐下载
	"""
	def __init__(self, timeout, folder, quiet, cookie_path):
		self.crawler = Crawler(timeout, cookie_path)
		self.folder = '.' if folder is None else folder
		self.quiet = quiet

	def download_song_by_search(self, song_name, song_num):
		"""
		根据歌曲名进行搜索
		:params song_name: 歌曲名字
		:params song_num: 下载的歌曲数
		"""

		try:
			song = self.crawler.search_song(song_name, song_num, self.quiet)
		except:
			click.echo('download_song_by_serach error')
		# 如果找到了音乐, 则下载
		if song != None:
			self.download_song_by_id(song.song_id, song.song_name, song.song_num, self.folder)

	def download_song_by_id(self, song_id, song_name, song_num, folder='.'):
		"""
		通过歌曲的ID下载
		:params song_id: 歌曲ID
		:params song_name: 歌曲名
		:params song_num: 下载的歌曲数
		:params folder: 保存地址
		"""
		try:
			url = self.crawler.get_song_url(song_id)
			# 去掉非法字符
			song_name = song_name.replace('/', '')
			song_name = song_name.replace('.', '')
			self.crawler.get_song_by_url(url, song_name, song_num, folder)

		except:
			click.echo('download_song_by_id error')


if __name__ == '__main__':
	timeout = 60
	output = 'Musics'
	quiet = True
	cookie_path = 'Cookie'
	netease = Netease(timeout, output, quiet, cookie_path)
	music_list_name = 'music_list.txt'
	# 如果music列表存在, 那么开始下载
	if os.path.exists(music_list_name):
		with open(music_list_name, 'r') as f:
			music_list = list(map(lambda x: x.strip(), f.readlines()))
		for song_num, song_name in enumerate(music_list):
			netease.download_song_by_search(song_name,song_num + 1)
	else:
		click.echo('music_list.txt not exist.')

以上就是python 根据列表批量下载网易云音乐的免费音乐的详细内容,更多关于python 网易云音乐下载的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
c++生成dll使用python调用dll的方法
Jan 20 Python
利用 python 对目录下的文件进行过滤删除
Dec 27 Python
wxPython之解决闪烁的问题
Jan 15 Python
pandas带有重复索引操作方法
Jun 08 Python
python2与python3中关于对NaN类型数据的判断和转换方法
Oct 30 Python
python 字典操作提取key,value的方法
Jun 26 Python
关于django 1.10 CSRF验证失败的解决方法
Aug 31 Python
tensorflow自定义激活函数实例
Feb 04 Python
Python tkinter布局与按钮间距设置方式
Mar 04 Python
如何用用Python将地址标记在地图上
Feb 07 Python
关于PySnooper 永远不要使用print进行调试的问题
Mar 04 Python
Python控制台输出俄罗斯方块的方法实例
Apr 17 Python
python中字符串的编码与解码详析
Dec 03 #Python
python 爬取百度文库并下载(免费文章限定)
Dec 04 #Python
filter使用python3代码进行迭代元素的实例详解
Dec 03 #Python
python3代码输出嵌套式对象实例详解
Dec 03 #Python
python3代码中实现加法重载的实例
Dec 03 #Python
python判断all函数输出结果是否为true的方法
Dec 03 #Python
django中cookiecutter的使用教程
Dec 03 #Python
You might like
让PHP支持断点续传的源码
2010/05/16 PHP
php读取csv实现csv文件下载功能
2013/12/18 PHP
使用新浪微博API的OAuth认证发布微博实例
2015/03/27 PHP
twig模板获取全局变量的方法
2016/02/05 PHP
javascript一点特殊用法
2008/05/28 Javascript
javascript 一段左右两边随屏滚动的代码
2009/06/18 Javascript
jquery 事件执行检测代码
2009/12/09 Javascript
SlideView 图片滑动(扩展/收缩)展示效果
2010/08/01 Javascript
利用webqq协议使用python登录qq发消息源码参考
2013/04/08 Javascript
JavaScript实现GriwView单列全选(自写代码)
2013/05/13 Javascript
jQuery截取指定长度字符串代码
2014/08/21 Javascript
JavaScript模拟数组合并concat
2016/03/06 Javascript
Javascript农历与公历相互转换的简单实例
2016/10/09 Javascript
js中小数向上取整数,向下取整数,四舍五入取整数的实现(必看篇)
2017/02/13 Javascript
Vue单页式应用(Hash模式下)实现微信分享的实例
2017/07/21 Javascript
详解基于Angular4+ server render(服务端渲染)开发教程
2017/08/28 Javascript
webpack配置打包后图片路径出错的解决
2018/04/26 Javascript
vue实现员工信息录入功能
2020/06/11 Javascript
vue 路由缓存 路由嵌套 路由守卫 监听物理返回操作
2020/08/06 Javascript
[01:01:41]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Magma BO3 第二场 1月31日
2021/03/11 DOTA
Python的Tornado框架实现图片上传及图片大小修改功能
2016/06/30 Python
Tensorflow卷积神经网络实例
2018/05/24 Python
在PYQT5中QscrollArea(滚动条)的使用方法
2019/06/14 Python
详解Python中的路径问题
2020/09/02 Python
Python3.8.2安装包及安装教程图文详解(附安装包)
2020/11/28 Python
html5拍照功能实现代码(htm5上传文件)
2013/12/11 HTML / CSS
丝芙兰法国官网:SEPHORA法国
2016/09/01 全球购物
SheIn俄罗斯:时尚女装网上商店
2017/02/28 全球购物
JDO的含义
2012/11/17 面试题
女儿十岁生日答谢词
2014/01/27 职场文书
学生周末长期请假条
2014/02/15 职场文书
餐饮投资计划书
2014/04/25 职场文书
2015年度考核个人工作总结
2015/10/24 职场文书
python实战之一步一步教你绘制小猪佩奇
2021/04/22 Python
Docker官方工具docker-registry案例演示
2022/04/13 Servers
阿里面试Nacos配置中心交互模型是push还是pull原理解析
2022/07/23 Java/Android