MP3下載(視窗程式)

      在〈MP3下載(視窗程式)〉中尚無留言

1.開新專案 , 專案名:mp3

2.python pip selenium pyqt5

3.安裝QT designer,並開啟

4.去除菜單與狀態列

5.frame拉出五個,在頁面空白處右鍵 lay out Vertically

6.在第一個frame裡拉一個frame,兩個label,並在此frame裡右鍵 lay out horizontally

7.外觀美化一下

8.儲存在專案位置下(ui/ui_mainwindow.ui)

9.專案設定qt編譯器

Program : 選取專案目錄下的 \venv\Scripts\pyuic5.exe

Arguments更改為如下
$FileName$ -o $FileNameWithoutExtension$.py

Working directory : 更改為如下
$ProjectFileDir$\ui

完成後就可以從左方清單中的ui_mainwindow.ui右鍵>external tools>PyUIC,轉換xml成py

10.編寫程式main.py

import sys

from PyQt5.QtWidgets import QApplication, QMainWindow

from ui.ui_mainwindow import Ui_MainWindow

class MainWindow(QMainWindow,Ui_MainWindow):
def __init__(self,parent=None):
super().__init__(parent)
self.setupUi(self)

if __name__=='__main__':
app=QApplication(sys.argv)
mainWindow=MainWindow()
mainWindow.show()
app.exec()

11.執行測試,是否成功叫出視窗

12.在第二個frame裡加入(由左至右):frame、label、line edit、push button、frame
並在此frame右鍵lay out horizontally,並調整美化

13.在第三個frame中拉入 list widget 並lay out horizontally

14.第四個與第五個frame 調整 max min 的higth為50

15.在第四個frame加入frame、push button、frame 再lay out horizontally
在此frame的第二個frame中加入 label 、push button lay out horizontally
進行微調

16.在最後一個frame加入label,lay out horizontally,調整置中,去除文字

17.修改物件名稱:
第一個文字框:txtSong
查詢:btnQuery
下載:btnDownload
路徑顯示:lblPath
目錄:btnPath
狀態欄:lblStatus

18.建立新的py

BrowserThread.py

from PyQt5.QtCore import QThread
class BrowserThread(QThread):#開啟瀏覽器執行緒
    pass

DownloadThread.py

from PyQt5.QtCore import QThread
class DownloadThread(QThread):#下載執行緒
pass

QueryThread.py

from PyQt5.QtCore import QThread
class QueryThread(QThread):#搜尋執行緒
pass

19.調整class MainWindow,加入

    def btnQueery_click(self):
        pass
    def btnDownload_click(self):
        pass
    def btnPath_click(self):
        pass
    def queryThreadCallback(self):
        pass
    def downloadThreadCallback(self):
        pass
    def downloadFinished(self):
        pass
    def browserThreadCallback(self):
        pass

20.在 setupUi下加上

self.btnDownload.clicked.connect(self.btnDownload_click)
self.btnQuery.clicked.connect(self.btnQueery_click)
self.btnPath.clicked.connect(self.btnPath_click)

21.測試是否能接通

    def btnQueery_click(self):
        #print("Queery")
        msg=QMessageBox()
        msg.setText("Queery")
        msg.exec()
        pass
    def btnDownload_click(self):
        #print("download")
        msg=QMessageBox()
        msg.setText("download")
        msg.exec()
        pass
    def btnPath_click(self):
        #print("路徑")
        msg=QMessageBox()
        msg.setText("路徑")
        msg.exec()
        pass

22.加上mp3資料夾的生成與判斷,再關掉按鍵(self.btnPath.clicked.connect)之下

        self.path='c:\\mp3'#一定要用
        self.lblPath.setText(self.path)
        if not os.path.isdir(self.path):
            os.mkdir(self.path)
        self.btnDownload.setEnabled(False)
        self.btnQuery.setEnabled(False)
        self.btnPath.setEnabled(False)

23.加上browser執行緒

self.BrowserThread=BrowserThread(self.path)
self.BrowserThread.callback.connect(self.browserThreadCallback)
self.BrowserThread.start()

24.更改 class BrowserThread(QThread)

class BrowserThread(QThread):#開啟瀏覽器執行緒
    callback=pyqtSignal(object)#類別變數
    def __init__(self,path):
        super().__init__(None)#物件變數 會產生self.callback,覆蓋掉類別變數
        self.browser=None
        self.path=path
    def __del__(self):
        self.wait()
    def run(self):#當Thread的物件執行start,,會來執行run
        print(123)
        pass
    pass

25.修改 def run(self)

    def run(self):#當Thread的物件執行start,,會來執行run
        options=Options()
        #options.add_argument('--headless')
        options.add_argument('--disable-gpu')
    def run(self):#當Thread的物件執行start,,會來執行run
        options=Options()#from selenium.webdriver.chrome.options import Options
        #options.add_argument('--headless')
        options.add_argument('--disable-gpu')
        options.add_experimental_option('excludeSwitches',['enable-logging'])#允許下載
        prefs={'profile.default_content_settings.popups':0,'download.default_directony':self.path}#改下載路徑
        options.add_experimental_option('prefs',prefs)
        browser=webdriver.Chrome('chromedriver.exe',options=options)#from selenium import webdriver
        self.callback.emit(browser)
        pass

26.修改 browserThreadCallback,打開所有按鈕

def browserThreadCallback(self,browser):#browser回應
self.browser=browser
self.btnDownload.setEnabled(True)
self.btnQuery.setEnabled(True)
self.btnPath.setEnabled(True)
pass

27.修改btnQueery_click

def btnQueery_click(self):
self.listWidget.clear()
self.song=self.txtSong.text()
if (self.song==''):
msg=QMessageBox()
msg.setText('請輸入歌手/歌曲名')
msg.setWindowTitle('錯誤')
msg.exec()
return
self.lblStatus.setText('搜尋中…')
self.btn_close()
self.queryThread=QueryThread(self.browser,self.song)
self.queryThread.callback.connect(self.queryThreadCallback)
self.queryThread.start()
pass

28.修改類別 class QueryThread

class QueryThread(QThread):#搜尋執行緒
    clllback=pyqtSignal(object)
    def __init__(self,browser,song):
        super().__init__(None)
        self.browser=browser
        self.song=song
    def __del__(self):
        self.wait()
    def run(self):
        pass
    pass

29.修改 QueryThread 的run

    def run(self):
        url=f'https://www.youtube.com/results?search_query={self.song}'
        self.browser.get(url)
        #tags=self.browser.find_elements_by_tag('a')
        tags = self.browser.find_elements_by_tag_name('a')
        links={}
        for tag in tags:
            href=tag.get_attribute('href')
            if 'watch' in str(href):
                title=tag.get_attribute('title')
                if title=='':
                    try:
                        title=tag.find_element_by_id('video-title').get_attribute('title')
                    except:
                        pass
                if title !='':#有資料,進入下載清單
                    links[href]=f'{title} url={href}'


        self.callback.emit(links)

30.修改 def queryThreadCallback

def queryThreadCallback(self,links):#找尋後問應
self.lblStatus.setText('')
self.btn_open()
for key in links.keys():
box=QCheckBox(links[key])#核取方塊
item=QListWidgetItem()
self.listWidget.addItem(item)#加項目
self.listWidget.setItemWidget(item,box)#box listWidget父類別指定配對

31.修改btnDownload_click

def btnDownload_click(self):
count=self.listWidget.count()
boxes=[self.listWidget.itemWidget(self.listWidget.item(i))for i in range(count)]
chks=[]
for box in boxes:
if box.isChecked():
chks.append(box.text())
print(chks)
self.downloadThread=DownloadThread(self.browser,chks)
self.downloadThread.callback.connect(self.downloadThreadCallback)
self.downloadThread.finished.connect(self.downloadFinished)
self.downloadThread.start()

32.DownloadThread.py


from PyQt5.QtCore import QThread, pyqtSignal
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class DownloadThread(QThread):#下載執行緒
    callback=pyqtSignal(object)
    finished=pyqtSignal()
    def __init__(self,browser,chks):
        super().__init__(None)
        self.runFlag=True#中斷下載用,python中途停止執行緒,非常不嚴僅(停不太下來)
        self.browser=browser
        self.chks=chks
    def __del__(self):
        self.runFlag=False
        self.wait()
    def run(self):
        for chk in self.chks:
            title=chk.split('url=')[0]
            url=chk.split('url=')[1].replace('youtube','youtubeto')
            self.callback.emit(f'正在下載{title}....')
            self.browser.get(url)
            try:
                self.browser.switch_to_frame('IframeChooseDefault')
                #切入內嵌的網頁
                try:#開始下載
                    WebDriverWait(self.browser,20,0.1).until(EC.presence_of_element_located((By.ID,'MP3Format')))
                    #等待出現Iframe
                    pass
                except:#下載失敗處理
                    pass
                btn=self.browser.find_element_by_id('MP3Format')#找到目標
                btn.click()#點擊
            except:#防切入失敗
                pass

最後程式結果 main.py

import os
import sys
#from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QCheckBox, QListWidgetItem, QFileDialog
#from selenium import webdriver
#from selenium.webdriver.chrome.options import Options

from ui.ui_mainwindow import Ui_MainWindow
from QueryThread import QueryThread
from DownloadThread import DownloadThread
from BrowserThread import BrowserThread

class MainWindow(QMainWindow,Ui_MainWindow):
    def __init__(self,parent=None):
        super().__init__(parent)
        self.setupUi(self)
        self.btnDownload.clicked.connect(self.btnDownload_click)
        self.btnQuery.clicked.connect(self.btnQueery_click)
        self.btnPath.clicked.connect(self.btnPath_click)
        self.path='c:\\mp3'#一定要用
        self.lblPath.setText(self.path)
        if not os.path.isdir(self.path):
            os.mkdir(self.path)
        #因brower未啟送,先禁用按鈕
        self.btn_close()
        self.BrowserThread=BrowserThread(self.path)
        self.BrowserThread.callback.connect(self.browserThreadCallback)
        self.BrowserThread.start()
    def btn_open(self):
        self.btnDownload.setEnabled(True)
        self.btnQuery.setEnabled(True)
        self.btnPath.setEnabled(True)
    def btn_close(self):
        self.btnDownload.setEnabled(False)
        self.btnQuery.setEnabled(False)
        self.btnPath.setEnabled(False)
    def btnQueery_click(self):
        self.listWidget.clear()
        self.song=self.txtSong.text()
        if (self.song==''):
            msg=QMessageBox()
            msg.setText('請輸入歌手/歌曲名')
            msg.setWindowTitle('錯誤')
            msg.exec()
            return
        self.lblStatus.setText('搜尋中…')
        self.btn_close()
        self.queryThread=QueryThread(self.browser,self.song)
        self.queryThread.callback.connect(self.queryThreadCallback)
        self.queryThread.start()
        pass
    def btnDownload_click(self):
        self.btn_close()
        count=self.listWidget.count()
        boxes=[self.listWidget.itemWidget(self.listWidget.item(i))for i in range(count)]
        chks=[]
        for box in boxes:
            if box.isChecked():
                chks.append(box.text())
        #print(chks)
        self.downloadThread=DownloadThread(self.browser,chks)
        self.downloadThread.callback.connect(self.downloadThreadCallback)
        self.downloadThread.finished.connect(self.downloadFinished)
        self.downloadThread.start()
        pass
    def btnPath_click(self):
        self.path=QFileDialog.getExistingDirectory()
        if self.path!='':
            self.path=self.path.replace('/','\\')
            if not os.path.isdir(self.path):
                os.mkdir(self.path)
            self.btn_close()
            self.lblPath.setText(self.path)
            self.browser.close()
            self.browserThread=BrowserThread(self.path)
            self.browserThread.callback.connect(self.browserThreadCallback)
            self.browserThread.start();
        pass
    def queryThreadCallback(self,links):#找尋後問應
        self.lblStatus.setText('')
        self.btn_open()
        for key in links.keys():
            box=QCheckBox(links[key])#核取方塊
            item=QListWidgetItem()
            self.listWidget.addItem(item)#加項目
            self.listWidget.setItemWidget(item,box)#把box由 listWidget父類別指定配對
    def downloadThreadCallback(self,msg):#下載開始後回應
        self.lblStatus.setText(msg)
        pass
    def downloadFinished(self):#下載完成後回應
        self.lblStatus.setText('下載完成')
        self.btn_open()
        pass
    def browserThreadCallback(self,browser):#browser回應
        self.browser=browser
        self.btn_open()
        pass

if __name__=='__main__':
    app=QApplication(sys.argv)
    frame=MainWindow()
    frame.show()
    app.exec()

BrowserThread.py

from PyQt5.QtCore import QThread, pyqtSignal
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
class BrowserThread(QThread):#開啟瀏覽器執行緒
callback=pyqtSignal(object)#類別變數
def __init__(self,path):
super().__init__(None)#物件變數 會產生self.callback,覆蓋掉類別變數
self.browser=None
self.path=path
def __del__(self):
self.wait()
def run(self):#Thread的物件執行start,,會來執行run
options=Options()#from selenium.webdriver.chrome.options import Options
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_experimental_option('excludeSwitches',['enable-logging'])#允許下載
prefs={'profile.default_content_settings.popups': 0, 'download.default_directory': self.path}#改下載路徑
options.add_experimental_option('prefs',prefs)
browser=webdriver.Chrome('chromedriver.exe',options=options)#from selenium import webdriver
self.callback.emit(browser)
pass
pass

DownloadThread.py


from PyQt5.QtCore import QThread, pyqtSignal
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class DownloadThread(QThread):#下載執行緒
callback=pyqtSignal(object)
finished=pyqtSignal()
def __init__(self,browser,chks):
super().__init__(None)
self.runFlag=True#中斷下載用,python中途停止執行緒,非常不嚴僅(停不太下來)
self.browser=browser
self.chks=chks
def __del__(self):
self.runFlag=False
self.wait()
def run(self):
for chk in self.chks:
title=chk.split('url=')[0]
url=chk.split('url=')[1].replace('youtube','youtubeto')
self.callback.emit(f'正在下載{title}....')
self.browser.get(url)
try:
self.browser.switch_to_frame('IframeChooseDefault')
#切入內嵌的網頁
try:#開始下載
WebDriverWait(self.browser,20,0.1).until(EC.presence_of_element_located((By.ID,'MP3Format')))
#等待出現Iframe
pass
except:#下載失敗處理
pass
btn=self.browser.find_element_by_id('MP3Format')#找到目標
btn.click()#點擊
except:#防切入失敗
pass
self.finished.emit()

QueryThread.py

from PyQt5.QtCore import QThread, pyqtSignal
class QueryThread(QThread):#搜尋執行緒
callback=pyqtSignal(object)
def __init__(self,browser,song):
super().__init__(None)
self.browser=browser
self.song=song
def __del__(self):
self.wait()
def run(self):
url=f'https://www.youtube.com/results?search_query={self.song}'
self.browser.get(url)
#tags=self.browser.find_elements_by_tag('a')
tags = self.browser.find_elements_by_tag_name('a')
links={}
for tag in tags:
href=tag.get_attribute('href')
if 'watch' in str(href):
title=tag.get_attribute('title')
if title=='':
try:
title=tag.find_element_by_id('video-title').get_attribute('title')
except:
pass
if title !='':#有資料,進入下載清單
links[href]=f'{title} url={href}'
self.callback.emit(links)

pass

打包exe

cmd>>
pip install selenium PyQt5 Pyinstaller
進入專案目錄
pyinstaller –hidden-import=queue -F main.py
執行測試
重新生成
pyinstaller –hidden-import=queue -w -F main.py

由於selenium套件會讓-w失效
編輯c:\user\xxx\AppData\Local\Prograns\python\Python37\Lib\site-package\selenium\webdriver\common\service.py


找到self.process = subprocess.Popen
改成

self.process = subprocess.Popen(cmd, env=self.env,
close_fds=platform.system() != ‘Windows’,
stdout=self.log_file,
stderr=self.log_file,
stdin=PIPE,creationflags=134217728)

再重新包裝一次,即完成

給他人需要把兩個exe檔放一起才能執行

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *