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檔放一起才能執行