1. 程式人生 > 其它 >pyqt5+pygame+urllib+beautiful實現各大平臺(網易,酷狗,qq)音樂爬取和收聽,下載,最終版本

pyqt5+pygame+urllib+beautiful實現各大平臺(網易,酷狗,qq)音樂爬取和收聽,下載,最終版本

全部原始碼在https://github.com/hedy-bit/pyqt5-pygame-urllib-

先上效果圖

之前沒事幹,看windows10自帶的播放器有一(億)點點不順眼,然後想寫一個播放器,
正好有學了點pyqt5,然後就整了個離線播音樂放器,耗時4天,現在差不多也算是最終版本了吧,

如果接下來有時間的話也會繼續更新下去,連結:離線播放器連結

然後最近沒有事情要做,就寫了一個網路音樂播放器,結構跟離線版本差不多主要更新了邏輯,添加了爬蟲模組

快不多說,先上程式碼

首先是爬取搜尋的歌曲,歌手和url,由於要使用多執行緒,所以新開了一個類

class PAThread(QThread):
    # 自定義訊號物件。引數str就代表這個訊號可以傳一個字串
    trigger = pyqtSignal(str)

    def __int__(self):
        # 初始化函式
        super(PAThread, self).__init__()

    def get_info(self, url):
        global proxies
        global tryed
        print('start get info')
        print(tryed)
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/491.10.2623.122 Safari/537.36'
        }
        web_data = requests.get(url, headers=headers)
        soup = BeautifulSoup(web_data.text, 'lxml')
        ranks = soup.select('#list > table > tbody > tr:nth-child({}) > td:nth-child(1)'.format(str(tryed)))
        titles = soup.select('#list > table > tbody > tr:nth-child({}) > td:nth-child(2)'.format(str(tryed)))
        times = soup.select('#list > table > tbody > tr:nth-child({}) > td:nth-child(6)'.format(str(tryed)))
        for rank, title, time in zip(ranks, titles, times):
            data = {
                'IP': rank.get_text(),
                'duan': title.get_text(),
                'time': time.get_text()
            }
            q = str('http://' + str(rank.get_text()) + '/' + str(title.get_text()))
            proxies = {
                'http': q
            }
            print(proxies)

    def run(self):
        qmut.lock()
        try:
            global urls
            global songs
            global name
            global songid
            global proxies
            global pic
            print('type')
            print('begin looking')
            url = 'https://defcon.cn/dmusic/'
            name = name
            self.get_info('https://www.kuaidaili.com/free/inha')
            print(proxies)
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.110.430.128 Safari/537.36',
                'X-Requested-With': 'XMLHttpRequest'

            }
            urls = []
            songs = []
            pic = []
            if int(page) == '' or int(page) < 1:
                pages = 2
            else:
                pages = int(page)
            print(pages)
            for a in range(1, pages):

                params = {'input': name,
                          'filter': 'name',
                          'type': type,
                          'page': a
                          }

                res = requests.post(url, params, headers=headers, proxies=proxies)
                html = res.json()

                for i in range(0, 10):
                    try:
                        title = jsonpath(html, '$..title')[i]
                        author = jsonpath(html, '$..author')[i]
                        url1 = jsonpath(html, '$..url')[i]  # 取下載網址
                        pick = jsonpath(html, '$..pic')[i]  # 取歌詞
                        print(title, author)
                        urls.append(url1)
                        pic.append(pick)
                        songs.append(str(title) + ' - ' + str(author))
                        # self.textEdit.setText(lrc)  # 列印歌詞
                        # print(lrc)
                    except:
                        pass

                print(urls)
                print(songs)
                self.trigger.emit(str('finish'))
        except:
            print('pa error')
            self.trigger.emit(str('unfinish'))
        qmut.unlock()


然後就是從爬取的url下載歌曲,由於我設計了兩個歌單,所以我寫了兩個類

class WorkThread(QThread):
    # 自定義訊號物件。引數str就代表這個訊號可以傳一個字串
    trigger = pyqtSignal(str)

    def __int__(self):
        # 初始化函式
        super(WorkThread, self).__init__()

    def cbk(self, a, b, c):
        '''''回撥函式
        @a:已經下載的資料塊
        @b:資料塊的大小
        @c:遠端檔案的大小
        '''
        per = 100.0 * a * b / c
        if per > 100:
            per = 100
        # print   ('%.2f%%' % per)
        self.trigger.emit(str('%.2f%%' % per))

    def run(self):
        try:
            global number
            global path
            global downloading
            global pic
            proxies = {
                'http': 'http://124.72.109.183:8118',
                ' Shttp': 'http://49.85.1.79:31666'

            }
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
                'X-Requested-With': 'XMLHttpRequest'}
            try:
                try:
                    try:
                        aq = pic[num]
                        aqq = aq.split('/')

                    except:
                        pass

                    if type == 'kugou' and len(aqq) - 1 == 6:
                        aqqe = str(aqq[0]) + str('//') + str(aqq[2]) + str('/') + str(aqq[3]) + str('/') + str(
                            '400') + str('/') + str(aqq[5]) + str('/') + str(aqq[6])
                        print(aqqe)
                    elif type == 'netease' and len(aqq) - 1 == 4:
                        aqn = aq.split('?')
                        b = '?param=400x400'
                        aqqe = (str(aqn[0]) + str(b))
                        print(aqqe)
                    else:
                        aqqe = pic[num]
                    req = requests.get(aqqe)

                    checkfile = open('./music/ls1.png', 'w+b')
                    for i in req.iter_content(100000):
                        checkfile.write(i)

                    checkfile.close()
                    lsfile = './music/ls1.png'
                    safile = './music/back.png'
                    draw(lsfile, safile)
                except:
                    pass
                url1 = urls[num]
                print(url1)
                os.makedirs('music', exist_ok=True)
                number = number + 1
                path = 'music\{}.臨時檔案'.format(number)
                urlretrieve(url1, path, self.cbk)  # 下載函式的使用
                to = 'downloadmusic\{}.mp3'.format(songs[num])
                os.makedirs('downloadmusic', exist_ok=True)
            except:
                pass

            try:
                copyfile(path, to)
            except:
                pass
            downloading = False
            self.trigger.emit(str('finish'))

        except:
            self.trigger.emit(str('nofinish'))


class WorkThread2(QThread):
    # 自定義訊號物件。引數str就代表這個訊號可以傳一個字串
    trigger = pyqtSignal(str)

    def __int__(self):
        # 初始化函式
        super(WorkThread, self).__init__()

    def cbk(self, a, b, c):
        '''''回撥函式
        @a:已經下載的資料塊
        @b:資料塊的大小
        @c:遠端檔案的大小
        '''
        per = 100.0 * a * b / c
        if per > 100:
            per = 100
        # print   ('%.2f%%' % per)
        self.trigger.emit(str('%.2f%%' % per))

    def run(self):
        try:
            global number
            global path
            global downloading
            proxies = {
                'http': 'http://124.72.109.183:8118',
                'http': 'http://49.85.1.79:31666'

            }
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
                'X-Requested-With': 'XMLHttpRequest'}
            try:

                if type == 'kugou':
                    aq = picd[num]
                    aqq = aq.split('/')
                    aqqe = str(aqq[0]) + str('//') + str(aqq[2]) + str('/') + str(aqq[3]) + str('/') + str('400') + str(
                        '/') + str(aqq[5]) + str('/') + str(aqq[6])
                    print(aqqe)
                else:
                    aqqe = picd[num]
                req = requests.get(aqqe)

                checkfile = open('./music/ls1.png', 'w+b')
                for i in req.iter_content(100000):
                    checkfile.write(i)

                checkfile.close()
                lsfile = './music/ls1.png'
                safile = './music/back.png'
                draw(lsfile, safile)

                url1 = urled[num]
                print(url1)
                os.makedirs('music', exist_ok=True)
                number = number + 1
                path = 'music\{}.臨時檔案'.format(number)
                urlretrieve(url1, path, self.cbk)  # 下載函式的使用
                to = 'downloadmusic\{}.mp3'.format(songed[num])
                os.makedirs('downloadmusic', exist_ok=True)
            except:
                pass

            try:
                copyfile(path, to)
            except:
                pass
            downloading = False
            self.trigger.emit(str('finish'))

        except:
            self.trigger.emit(str('nofinish'))

下面就是QListwidge的點選和播放模組

    def bofang(self, num):
        print ('try bofang')
        try:
            import urllib
            global pause
            global songs
            global music
            global downloading
            downloading = True
            self.console_button_3.setIcon(qtawesome.icon('fa.pause', color='#F76677', font=18))
            pause = False
            # QMessageBox.information(self, "ListWidget", "你選擇了: "+item.text())# 顯示出訊息提示框
            try:
                pygame.mixer.stop()
            except:
                pass
            pygame.mixer.init()
            try:
                self.Timer = QTimer()
                self.Timer.start(500)
            except:
                pass


            try:
                self.label.setText('下載中')#呼叫開頭的多執行緒下載歌曲
                self.work = WorkThread()
                self.work.start()
                self.work.trigger.connect(self.display)
            except:
                print ('song download error')
                downloading = False
                pass




        except:
            time.sleep(0.1)
            print ('system error')
            #self.next()
            pass
	#用於接收返回的訊號
    def display(self,sd):
        if sd == 'finish':
            self.label.setText(songs[num])
            print ('music\{}.mp3'.format(number))
            pygame.mixer.music.load('music\{}.mp3'.format(number))  # 載入音樂
            pygame.mixer.music.play()
            # 播放音樂
        else:
            self.label.setText('下載錯誤')

下面是雙擊播放和上一首還有下一首

	#QlistWidget的雙擊事件
    def change_func(self, listwidget):
        global num
        item = QListWidgetItem(self.listwidget.currentItem())
        print(item.text())
        
        num = int(listwidget.currentRow())
        
        self.label.setText(songs[num])
        print(listwidget.currentRow())
        self.bofang(num)
		
	#下一首按鈕
	def nextion(self):

            try:
                    if play == 'shun':
                        print('shuning')
                        self.next()
                    elif play == 'shui':
                        print('shuiing')
                        self.shui()
                    elif play == 'always':
                        print('alwaysing')
                        self.next()

            except:
                print('no')
                pass

	

下面自動播放,有迴圈,隨機和單曲迴圈,加上下一首,上一首,

	#隨機播放
    def shui(self):
        global num
        global songs
        q = int(len(songs) - 1)
        num = int(random.randint(1, q))
        try:
            print('shui')
            pygame.mixer.init()
            self.Timer = QTimer()
            self.Timer.start(500)
            # self.Timer.timeout.connect(self.timercontorl)#時間函式,與下面的進度條和時間顯示有關
            self.label.setText(songs[num])
            self.bofang(num) # 播放音樂

        except:
            pass
	#下一首
    def next(self):
        print ('nexting')
        global num
        global songs
        if num == len(songs) - 1:
            print('冇')
            num = 0
        else:
            num = num + 1
        try:
            self.label.setText(songs[num])
            self.bofang(num)
        except:
            print ('next error')
            pass


	#單曲迴圈
    def always(self):
        try:
            self.bofang(num)
            self.label.setText(songs[num])

        except:
            pass
	#上一首
    def last(self):
        global num
        global songs
        if num == 0:
            print('冇')
            num = len(songs) - 1
        else:
            num = num - 1
        try:
            self.bofang(num)
            self.label.setText(songs[num])

        except:
            pass

下面是播放模式選擇和迴圈判斷是否要自動下一首
由於每秒鐘判斷一次,所以要使用多執行緒

	'''
		在init裡面的迴圈判斷開啟方法
	    t1 = threading.Thread(target=self.action)
        t1.setDaemon(True)
        t1.start()
    '''
	#選擇播放模式
    def playmode(self):
        global play
        try:
            if play == 'shun':
                play = 'shui'
                print('隨機播放')
                self.label2.setText("當前為隨機播放")
                try:
                    self.console_button_6.setIcon(qtawesome.icon('fa.random', color='#3FC89C', font=18))
                    print('done')
                except:
                    print('none')
                    pass

                # self.left_shui.setText('切換為單曲迴圈')
            elif play == 'shui':
                play = 'always'
                print('單曲迴圈')
                self.label2.setText("當前為單曲迴圈")
                try:
                    self.console_button_6.setIcon(qtawesome.icon('fa.retweet', color='#3FC89C', font=18))
                    print('done')
                except:
                    print('none')


                # self.left_shui.setText('切換為順序播放')
            elif play == 'always':
                play = 'shun'
                print('順序播放')
                self.label2.setText("當前為順序播放")
                try:
                    self.console_button_6.setIcon(qtawesome.icon('fa.align-center', color='#3FC89C', font=18))
                    print('done')
                except:
                    print('none')

                # self.left_shui.setText('切換為隨機播放')
        except:
            print('error')
            pass
	#迴圈判斷
    def action(self):
        a = 1
        global num
        while a < 2:
            # print ('checking')
            try:
                time.sleep(1)
                if not pygame.mixer.music.get_busy() and pause == False and not downloading:
                    if play == 'shun':
                        print('shuning')
                        self.next()
                    elif play == 'shui':
                        print('shuiing')
                        self.shui()
                    elif play == 'always':
                        print('alwaysing')
                        self.always()

            except:
                print('no')
                pass
        else:
            pygame.mixer.music.stop()

程式碼介紹在這裡就結束了,如果有更好的修改方案請私信我

最後是全部程式碼,
https://github.com/hedy-bit/pyqt5-pygame-urllib-