python編寫GUI版網(wǎng)易云音樂爬蟲

先來看效果圖:

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è)贊哦 (˙?˙)?--?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末世囊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子窿祥,更是在濱河造成了極大的恐慌株憾,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晒衩,死亡現(xiàn)場(chǎng)離奇詭異嗤瞎,居然都是意外死亡墙歪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門猫胁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箱亿,“玉大人,你說我怎么就攤上這事弃秆〗焱铮” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵菠赚,是天一觀的道長(zhǎng)脑豹。 經(jīng)常有香客問我,道長(zhǎng)衡查,這世上最難降的妖魔是什么瘩欺? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮拌牲,結(jié)果婚禮上俱饿,老公的妹妹穿的比我還像新娘。我一直安慰自己塌忽,他們只是感情好拍埠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著土居,像睡著了一般枣购。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擦耀,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天棉圈,我揣著相機(jī)與錄音,去河邊找鬼眷蜓。 笑死分瘾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吁系。 我是一名探鬼主播芹敌,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼垮抗!你這毒婦竟也來了氏捞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤冒版,失蹤者是張志新(化名)和其女友劉穎液茎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捆等,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年滞造,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栋烤。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谒养,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出明郭,到底是詐尸還是另有隱情买窟,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布薯定,位于F島的核電站始绍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏话侄。R本人自食惡果不足惜亏推,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望年堆。 院中可真熱鬧吞杭,春花似錦、人聲如沸变丧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锄贷。三九已至译蒂,卻和暖如春曼月,著一層夾襖步出監(jiān)牢的瞬間谊却,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工哑芹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炎辨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓聪姿,卻偏偏與公主長(zhǎng)得像碴萧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子末购,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容