先來看效果圖:
TIM截圖20180716011847.png
使用方法:
到網(wǎng)易云音樂,選擇你想要下載的歌手或者歌單的音樂
復(fù)制頁(yè)面url地址,粘貼到程序的輸入框中据某,點(diǎn)擊下載按鈕或者敲回車。
等待下載完成寄纵,或者中途停止鳖敷。
TIM截圖20180716011908.png
代碼如下:
import os
import tkinter
import requests
from loguru import logger
from lxml import etree
# 標(biāo)記是否停止下載,初始值為False程拭,當(dāng)值為True時(shí)定踱,停止下載
from tqdm import tqdm
flag = False
class MusicSpider(object):
def __init__(self):
pass
@logger.catch()
def download_songs(self, text, entry):
self.text = text
self.entry = entry
url = self.entry.get() # 獲取輸入框中的字符串
url = url.replace('/#', '').replace('https', 'http') # 對(duì)字符串進(jìn)行去空格和轉(zhuǎn)協(xié)議處理
# 當(dāng)沒有輸入url就點(diǎn)擊下載或者回車的時(shí)候,在文本框中顯示提示
if url == '':
self.text.insert(tkinter.END, '請(qǐng)輸入您要下載的歌單的url地址恃鞋!')
return
# 網(wǎng)易云音樂外鏈url接口:http://music.163.com/song/media/outer/url?id=xxxx
out_link = 'http://music.163.com/song/media/outer/url?id='
# 請(qǐng)求頭
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36',
'Referer': 'https://music.163.com/',
'Host': 'music.163.com'
}
# 請(qǐng)求頁(yè)面的源碼
res = requests.get(url=url, headers=headers).text
tree = etree.HTML(res)
# 音樂列表
song_list = tree.xpath('//ul[@class="f-hide"]/li/a')
# 如果是歌手頁(yè)面
artist_name_tree = tree.xpath('//h2[@id="artist-name"]/text()')
artist_name = str(artist_name_tree[0]) if artist_name_tree else None
# 如果是歌單頁(yè)面:
song_list_name_tree = tree.xpath('//h2[contains(@class,"f-ff2")]/text()')
song_list_name = str(song_list_name_tree[0]) if song_list_name_tree else None
# 設(shè)置音樂下載的文件夾為歌手名字或歌單名
folder = './' + artist_name if artist_name else './' + song_list_name
if not os.path.exists(folder):
os.mkdir(folder)
def get_real_address(src):
res = requests.get(src, headers=headers, allow_redirects=False)
return res.headers['Location'] if res.status_code == 302 else None
for i, s in enumerate(song_list):
href = str(s.xpath('./@href')[0])
id = href.split('=')[-1]
src = out_link + id # 拼接獲取音樂真實(shí)的src資源值
title = str(s.xpath('./text()')[0]) # 音樂的名字
filename = title + '.mp3'
filepath = folder + '/' + filename
location = get_real_address(src)
r = requests.get(location, stream=True) # 音樂的二進(jìn)制數(shù)據(jù)
mp3_size = int(r.headers['Content-Length'])
info = '開始下載第%d首音樂:%s\n' % (i + 1, filename)
if flag: # 當(dāng)停止下載時(shí)崖媚,顯示信息,跳出循環(huán)恤浪,代碼不再向下執(zhí)行
self.text.insert(tkinter.END, '停止下載')
self.text.see(tkinter.END)
self.text.update()
break
self.text.insert(tkinter.END, info) # 在文本框中顯示下載信息
self.text.see(tkinter.END)
self.text.update()
try: # 下載音樂
# with open(filepath, 'wb') as f:
# f.write(data)
with open(filepath, 'wb') as f:
for data in tqdm(
iterable=r.iter_content(),
total=mp3_size,
unit='b',
desc=filepath,
unit_scale=True
):
f.write(data)
except Exception as e:
logger.error(e)
self.text.insert(tkinter.END, e) # 將錯(cuò)誤信息顯示在前端文本框中
self.text.see(tkinter.END)
self.text.update()
if not flag: # 中間沒有點(diǎn)擊停止下載畅哑,程序會(huì)走到這里
self.text.insert(tkinter.END, '下載完成') # 在前端文本框中顯示下載完畢
self.text.see(tkinter.END)
self.text.update()
class Application(object):
def __init__(self):
# 創(chuàng)建主窗口Tk()
self.window = tkinter.Tk()
# 設(shè)置一個(gè)標(biāo)題,參數(shù)類型:string
self.window.title('網(wǎng)易云音樂下載器——Powered by 王濤哥哥')
# 設(shè)置主窗口大小和位置
# self.window.geometry('800x500+240+120')
self.center_window(self.window, 800, 500) # 窗口居中水由,寬800荠呐,高500
# 使用frame增加上中下4個(gè)框架
self.fm1 = tkinter.Frame(self.window) # fm1存放label標(biāo)簽
self.fm2 = tkinter.Frame(self.window) # fm2存放url輸入框,下載按鈕
self.fm3 = tkinter.Frame(self.window) # fm3存放下載信息顯示的文本框
self.fm4 = tkinter.Frame(self.window) # fm4用來存放底下停止和退出按鈕
self.fm1.pack()
self.fm2.pack()
self.fm3.pack()
self.fm4.pack()
# 創(chuàng)建一個(gè)標(biāo)簽
self.label = tkinter.Label(self.fm1, text='輸入你要下載的歌單的url,點(diǎn)擊下載或者回車绷杜!', font=('微軟雅黑', 15), width=35)
# 顯示,布局管理器----可以理解為一個(gè)彈性容器
self.label.pack(fill=tkinter.X)
# 創(chuàng)建一個(gè)輸入框濒募,用來接收用戶輸入的歌單的url
self.entry = tkinter.Entry(self.fm2, width=46, bg='pink', font=('微軟雅黑', 20))
self.entry.grid(row=0, column=0, rowspan=1, columnspan=10, padx=2)
self.entry.bind("<Key-Return>", self.press_enter) # 輸入歌單url之后直接按回車鍵鞭盟,觸發(fā)press_enter函數(shù)
# 創(chuàng)建下載按鈕
self.btn_download = tkinter.Button(self.fm2, text='下載', command=self.crawl, bg='red', font=('微軟雅黑', 12))
self.btn_download.grid(row=0, column=30, rowspan=1, columnspan=1, padx=5, pady=3)
# 創(chuàng)建一個(gè)文本控件,用于顯示多行文本瑰剃,顯示音樂下載信息
self.text = tkinter.Text(self.fm3, width=110, height=18, font=('微軟雅黑', 10))
self.text.pack(side=tkinter.LEFT, fill=tkinter.Y)
# 創(chuàng)建一個(gè)滾動(dòng)條
self.scroll = tkinter.Scrollbar(self.fm3)
self.scroll.pack(side=tkinter.RIGHT, fill=tkinter.Y)
# 關(guān)聯(lián)滾動(dòng)條和文本 config, 相互關(guān)聯(lián)
self.scroll.config(command=self.text.yview())
self.text.config(yscrollcomman=self.scroll.set)
# 創(chuàng)建停止和退出按鈕
btn_stop = tkinter.Button(self.fm4, text='停止', command=self.stop, bg='gray', font=('微軟雅黑', 16))
btn_quit = tkinter.Button(self.fm4, text='退出', command=self.window.quit, bg='gray', font=('微軟雅黑', 16))
btn_stop.pack(side='left', padx=100, pady=10)
btn_quit.pack(side='right', padx=100, pady=10)
def stop(self):
global flag # 將flag設(shè)為全局變量齿诉,以便下載過程中能隨時(shí)獲取
flag = True
return flag
def crawl(self):
text = self.text
entry = self.entry
MusicSpider.download_songs(self, text, entry)
def press_enter(self, even):
return self.crawl()
def center_window(self, root, width, height):
screenwidth = root.winfo_screenwidth()
screenheight = root.winfo_screenheight()
size = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
# logger.info(size)
root.geometry(size)
def run(self):
# 進(jìn)入消息循環(huán),顯示主窗口
self.window.mainloop()
if __name__ == '__main__':
app = Application()
app.run()
文件名為163M.py
---> 多進(jìn)程下載版本
導(dǎo)出exe文件:
進(jìn)入文件所在目錄:
pyinstaller -F -w -i favicon.ico 163M.py
-F
打包成單文件程序,
-w
是windows程序晌姚,不顯示命令行窗口粤剧。如果執(zhí)行中需要顯示命令行,不要加這個(gè)參數(shù)挥唠。
-i favicon.ico
相當(dāng)于 --icon=favicon.ico
打包程序的時(shí)候給給它指定一個(gè)圖標(biāo)抵恋。
導(dǎo)出exe文件需要用到
pyinstaller
模塊
安裝命令pip install pyinstaller
沒有python環(huán)境又不想安裝python的同學(xué)可以到我的碼云倉(cāng)庫(kù)下載 網(wǎng)易云音樂下載器.exe
即可
多進(jìn)程下載腳本版
import os
import time
from multiprocessing import freeze_support, Pool
from threading import RLock
import psutil as psutil
import requests
from loguru import logger
from lxml import etree
from tqdm import tqdm
class M163:
headers = {
'Connection': 'keep-alive',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
'Referer': 'https://music.163.com/',
'Host': 'music.163.com'
}
def download(self, url):
url = url.replace('/#', '').replace('https', 'http') # 對(duì)字符串進(jìn)行去空格和轉(zhuǎn)協(xié)議處理
# 當(dāng)沒有輸入url就點(diǎn)擊下載或者回車的時(shí)候,在文本框中顯示提示
if url == '':
return
# 網(wǎng)易云音樂外鏈url接口:http://music.163.com/song/media/outer/url?id=xxxx
out_link = 'http://music.163.com/song/media/outer/url?id='
session = requests.Session()
html = session.get(url=url, headers=self.headers).text
tree = etree.HTML(html)
# 音樂列表
song_list = tree.xpath('//ul[@class="f-hide"]/li/a')
# 如果是歌手頁(yè)面
artist_name_tree = tree.xpath('//h2[@id="artist-name"]/text()')
artist_name = str(artist_name_tree[0]) if artist_name_tree else None
# 如果是歌單頁(yè)面:
song_list_name_tree = tree.xpath('//h2[contains(@class,"f-ff2")]/text()')
song_list_name = str(song_list_name_tree[0]) if song_list_name_tree else None
# 設(shè)置音樂下載的文件夾為歌手名字或歌單名
folder = artist_name if artist_name else song_list_name
if not os.path.exists(folder):
os.makedirs(folder)
st_time = int(time.time())
cpu_count = psutil.cpu_count()
freeze_support() # for Windows support
pool = Pool(
processes=cpu_count,
initializer=tqdm.set_lock, initargs=(RLock(),)
)
for i, song in enumerate(song_list, 1):
href = str(song.xpath('./@href')[0])
id = href.split('=')[-1]
song_url = out_link + id # 拼接獲取音樂真實(shí)的src資源值
title = str(song.xpath('./text()')[0]) # 音樂的名字
filename = title + '.mp3'
filepath = os.path.join(folder, filename)
logger.info(f'開始下載第{i}首音樂:{filename}')
pool.apply_async(func=self.download_song, args=(song_url, filepath, session,))
pool.close()
pool.join()
end_time = int(time.time())
time_take = end_time - st_time
logger.success(f'專輯《{song_list_name}》已下載完畢,耗時(shí){time_take}s')
def download_song(self, song_url, filepath, session):
location = self.get_real_address(song_url, session)
r = session.get(location, stream=True)
mp3_size = int(r.headers['Content-Length'])
try: # 下載音樂
# 調(diào)用iter_content宝磨,一塊一塊的遍歷要下載的內(nèi)容弧关,搭配stream=True,此時(shí)才開始真正的下載
# iterable:可迭代的進(jìn)度條 total:總的迭代次數(shù) desc:進(jìn)度條的前綴
with open(filepath, 'wb') as f:
for data in tqdm(
iterable=r.iter_content(),
total=mp3_size,
unit='b',
desc=filepath.split('/')[-1],
unit_scale=True,
# gui=True,
):
f.write(data)
logger.info(f'音樂:{filepath.split("/")[-1]} 下載完成')
except Exception as e:
logger.error(e)
def get_real_address(self, url, session):
res = session.get(url, headers=self.headers, allow_redirects=False)
return res.headers['Location'] if res.status_code == 302 else None
if __name__ == "__main__":
app = M163()
url = 'https://music.163.com/#/playlist?id=4956037794'
app.download(url)
如果這篇文章對(duì)你有幫助唤锉,不妨點(diǎn)個(gè)贊哦
(˙?˙)?--?