前段時間,Python老師讓做一個程序,想來想去還是代表Python的爬蟲較好
加上最近一直在電影天堂上下載電影,就只好做這個了
1.界面
界面設計不是自己手打的啊,界面使用PyQt5 Designer設計的
很明顯的,左邊一個列表放目錄,右邊是詳情,上方是菜單欄
最終生成.py文件,開始修改
下面代碼是自動生成的(我小改了下)
# 界面UI佈局
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1102, 832)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.listView = QtWidgets.QListView(self.centralwidget)
self.listView.setGeometry(QtCore.QRect(0, 0, 271, 731))
self.listView.setObjectName("listView")
self.label1 = QtWidgets.QLabel(self.centralwidget)
self.label1.setGeometry(QtCore.QRect(710, 10, 81, 51))
self.label1.setObjectName("label1")
self.label1_is = QtWidgets.QLabel(self.centralwidget)
self.label1_is.setGeometry(QtCore.QRect(810, 10, 241, 51))
self.label1_is.setText("")
self.label1_is.setWordWrap(True)
self.label1_is.setObjectName("label1_is")
self.images = QtWidgets.QLabel(self.centralwidget)
self.images.setGeometry(QtCore.QRect(280, 10, 391, 621))
self.images.setText("")
self.images.setPixmap(QtGui.QPixmap("1.png"))
self.images.setScaledContents(True)
self.images.setObjectName("images")
self.label2 = QtWidgets.QLabel(self.centralwidget)
self.label2.setGeometry(QtCore.QRect(710, 50, 81, 51))
self.label2.setObjectName("label2")
self.label2_is = QtWidgets.QLabel(self.centralwidget)
self.label2_is.setGeometry(QtCore.QRect(810, 50, 241, 51))
self.label2_is.setText("")
self.label2_is.setWordWrap(True)
self.label2_is.setObjectName("label2_is")
self.label3 = QtWidgets.QLabel(self.centralwidget)
self.label3.setGeometry(QtCore.QRect(710, 90, 81, 51))
self.label3.setObjectName("label3")
self.label4 = QtWidgets.QLabel(self.centralwidget)
self.label4.setGeometry(QtCore.QRect(710, 130, 81, 51))
self.label4.setObjectName("label4")
self.label5 = QtWidgets.QLabel(self.centralwidget)
self.label5.setGeometry(QtCore.QRect(710, 170, 81, 51))
self.label5.setObjectName("label5")
self.label6 = QtWidgets.QLabel(self.centralwidget)
self.label6.setGeometry(QtCore.QRect(710, 210, 81, 51))
self.label6.setObjectName("label6")
self.label7 = QtWidgets.QLabel(self.centralwidget)
self.label7.setGeometry(QtCore.QRect(710, 250, 81, 51))
self.label7.setObjectName("label7")
self.label8 = QtWidgets.QLabel(self.centralwidget)
self.label8.setGeometry(QtCore.QRect(710, 290, 81, 51))
self.label8.setObjectName("label8")
self.label9 = QtWidgets.QLabel(self.centralwidget)
self.label9.setGeometry(QtCore.QRect(280, 630, 81, 51))
self.label9.setObjectName("label9")
self.btn_download = QtWidgets.QPushButton(self.centralwidget)
self.btn_download.setGeometry(QtCore.QRect(480, 740, 131, 41))
self.btn_download.setObjectName("btn_download")
self.label3_is = QtWidgets.QLabel(self.centralwidget)
self.label3_is.setGeometry(QtCore.QRect(810, 90, 241, 51))
self.label3_is.setText("")
self.label3_is.setWordWrap(True)
self.label3_is.setObjectName("label3_is")
self.label4_is = QtWidgets.QLabel(self.centralwidget)
self.label4_is.setGeometry(QtCore.QRect(810, 130, 241, 51))
self.label4_is.setText("")
self.label4_is.setWordWrap(True)
self.label4_is.setObjectName("label4_is")
self.label5_is = QtWidgets.QLabel(self.centralwidget)
self.label5_is.setGeometry(QtCore.QRect(810, 170, 241, 51))
self.label5_is.setText("")
self.label5_is.setWordWrap(True)
self.label5_is.setObjectName("label5_is")
self.label6_is = QtWidgets.QLabel(self.centralwidget)
self.label6_is.setGeometry(QtCore.QRect(810, 210, 241, 51))
self.label6_is.setText("")
self.label6_is.setWordWrap(True)
self.label6_is.setObjectName("label6_is")
self.label7_is = QtWidgets.QLabel(self.centralwidget)
self.label7_is.setGeometry(QtCore.QRect(810, 250, 241, 51))
self.label7_is.setText("")
self.label7_is.setWordWrap(True)
self.label7_is.setObjectName("label7_is")
self.label8_is = QtWidgets.QLabel(self.centralwidget)
self.label8_is.setGeometry(QtCore.QRect(810, 310, 241, 311))
self.label8_is.setText("")
self.label8_is.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
self.label8_is.setWordWrap(True)
self.label8_is.setObjectName("label8_is")
self.btn_up = QtWidgets.QPushButton(self.centralwidget)
self.btn_up.setGeometry(QtCore.QRect(10, 740, 93, 28))
self.btn_up.setObjectName("btn_up")
self.btn_down = QtWidgets.QPushButton(self.centralwidget)
self.btn_down.setGeometry(QtCore.QRect(170, 740, 93, 28))
self.btn_down.setObjectName("btn_down")
self.label_page = QtWidgets.QLabel(self.centralwidget)
self.label_page.setGeometry(QtCore.QRect(110, 746, 51, 20))
self.label_page.setAlignment(QtCore.Qt.AlignCenter)
self.label_page.setObjectName("label_page")
self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser.setGeometry(QtCore.QRect(350, 640, 731, 81))
self.textBrowser.setObjectName("textBrowser")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1102, 26))
self.menubar.setObjectName("menubar")
self.menu1 = QtWidgets.QMenu(self.menubar)
self.menu1.setObjectName("menu1")
self.menu2 = QtWidgets.QMenu(self.menubar)
self.menu2.setObjectName("menu2")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actiongu = QtWidgets.QAction(MainWindow)
self.actiongu.setObjectName("actiongu")
self.action2 = QtWidgets.QAction(MainWindow)
self.action2.setObjectName("action2")
self.action3 = QtWidgets.QAction(MainWindow)
self.action3.setObjectName("action3")
self.guanyu = QtWidgets.QAction(MainWindow)
self.guanyu.setObjectName("guanyu")
self.xieyi = QtWidgets.QAction(MainWindow)
self.xieyi.setObjectName("xieyi")
self.quexian = QtWidgets.QAction(MainWindow)
self.quexian.setObjectName("quexian")
self.zuixin = QtWidgets.QAction(MainWindow)
self.zuixin.setObjectName("zuixin")
self.zonghe = QtWidgets.QAction(MainWindow)
self.zonghe.setObjectName("zonghe")
self.guonei = QtWidgets.QAction(MainWindow)
self.guonei.setObjectName("guonei")
self.oumei = QtWidgets.QAction(MainWindow)
self.oumei.setObjectName("oumei")
self.rihan = QtWidgets.QAction(MainWindow)
self.rihan.setObjectName("rihan")
self.shanchu = QtWidgets.QAction(MainWindow)
self.shanchu.setObjectName("shanchu")
self.dakai = QtWidgets.QAction(MainWindow)
self.dakai.setObjectName("dakai")
self.exit = QtWidgets.QAction(MainWindow)
self.exit.setObjectName("exit")
self.menu1.addAction(self.zuixin)
self.menu1.addAction(self.zonghe)
self.menu1.addAction(self.guonei)
self.menu1.addAction(self.oumei)
self.menu1.addAction(self.rihan)
self.menu1.addSeparator()
self.menu1.addAction(self.shanchu)
self.menu1.addAction(self.dakai)
self.menu1.addSeparator()
self.menu1.addAction(self.exit)
self.menu2.addAction(self.guanyu)
self.menu2.addAction(self.xieyi)
self.menu2.addAction(self.quexian)
self.menubar.addAction(self.menu1.menuAction())
self.menubar.addAction(self.menu2.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
# 界面UI文字
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "電影天堂微型客戶端Python"))
self.label1.setText(_translate("MainWindow", "片 名"))
self.label2.setText(_translate("MainWindow", "年 代"))
self.label3.setText(_translate("MainWindow", "產 地"))
self.label4.setText(_translate("MainWindow", "類 別"))
self.label5.setText(_translate("MainWindow", "豆瓣評分"))
self.label6.setText(_translate("MainWindow", "片 長"))
self.label7.setText(_translate("MainWindow", "導 演"))
self.label8.setText(_translate("MainWindow", "主 演"))
self.label9.setText(_translate("MainWindow", "簡 介"))
self.btn_download.setText(_translate("MainWindow", "下載"))
self.btn_up.setText(_translate("MainWindow", "上一頁"))
self.btn_down.setText(_translate("MainWindow", "下一頁"))
self.label_page.setText(_translate("MainWindow", "1"))
self.textBrowser.setHtml(_translate("MainWindow",
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"></p></body></html>"))
self.menu1.setTitle(_translate("MainWindow", "電影"))
self.menu2.setTitle(_translate("MainWindow", "關於"))
self.actiongu.setText(_translate("MainWindow", "1"))
self.action2.setText(_translate("MainWindow", "2"))
self.action3.setText(_translate("MainWindow", "3"))
self.guanyu.setText(_translate("MainWindow", "關於"))
self.xieyi.setText(_translate("MainWindow", "協議"))
self.quexian.setText(_translate("MainWindow", "軟件缺陷"))
self.zuixin.setText(_translate("MainWindow", "最新電影"))
self.zonghe.setText(_translate("MainWindow", "綜合電影"))
self.guonei.setText(_translate("MainWindow", "國內電影"))
self.oumei.setText(_translate("MainWindow", "歐美電影"))
self.rihan.setText(_translate("MainWindow", "日韓電影"))
self.shanchu.setText(_translate("MainWindow", "刪除緩存"))
self.dakai.setText(_translate("MainWindow", "打開緩存目錄"))
self.exit.setText(_translate("MainWindow", "退出軟件"))
# 設置界面控件綁定及初始化界面數據
self.setDataUpdate(MainWindow)
2.目錄爬蟲和列表+上下頁單擊事件
設計思路
PS:先上變量
URl5個常量分別是電影天堂(ygdy8.net)目錄列表格式,通過format加入page即可訪問page頁的數據
1.獲取目錄及鏈接放到兩個列表變量中,使其對應
2.列表單擊方法
3.按鈕單擊方法
4.獲取目錄後更新列表方法
5.獲取當前選擇的電影類型(返回URL)
# 最新電影
URL_NEW = "https://www.ygdy8.net/html/gndy/dyzz/list_23_{}.html"
# 綜合
URL_COMPREHENSIVE = "https://www.ygdy8.net/html/gndy/jddy/list_63_{}.html"
# 國內
URL_DOMESTIC = "https://www.ygdy8.net/html/gndy/china/list_4_{}.html"
# 歐美
URL_EUROPE_AND_AMERICA = "https://www.ygdy8.net/html/gndy/oumei/list_7_{}.html"
# 日韓
URL_JAPAN_AND_KOREA = "https://www.ygdy8.net/html/gndy/rihan/list_6_{}.html"
# 電影詳情頁元組
movie = {}
# 目錄頁列表
catalog = []
# 目錄鏈接列表,與目錄對應
link = []
# 電影:1=最新 2=綜合 3=國內 4=歐美 5=日韓
type = 1
# 頁數
page = 1
1.獲取目錄及目錄對應的鏈接
# 爬蟲獲取目錄及鏈接,將值保存到catalog和link列表
# 需要注意,電影天堂的最新電影和綜合電影的目錄沒有跳過
# 而國內,歐美日韓等目錄界面有個廢鏈接,需要跳過去,在下方代碼if
def getCatalog(self, url):
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'} # 模擬瀏覽器登入
r = requests.get(url, headers=headers, timeout=10) # 獲取網頁
except:
pass
# print(r.status_code)
else:
html = r.content.decode('gb2312', 'ignore') # 解碼gb2312,忽略其中有異常的編碼,僅顯示有效的編碼
# print(html)
# 解析網頁
soup = BeautifulSoup(html, 'lxml')
tiaoguo = False
self.catalog = []
self.link = []
for li in soup.select('.co_content8 b'): # 選擇所有co_content8 下的所有的 b 節點
for a in li.select('a'): # 選擇 b 節點下的 a 節點
# print(a.string)
link = 'https://www.ygdy8.net' + a['href'] # 構造每個電影的網頁鏈接
if (self.type == 1 or self.type == 2):
self.catalog.append(getTitle(a.string))
self.link.append(link)
else:
# 每次獲取一個結果後,存儲一次 每兩次存儲一次,因爲有一次不對,沒有電影名稱
if tiaoguo:
# print(a.string)
self.catalog.append(getTitle(a.string))
self.link.append(link)
tiaoguo = False
else:
tiaoguo = True
2.列表單擊方法
initMovie是詳情頁爬蟲,在下方有代碼
# 列表單擊方法
def listClick(self, qModelIndex):
# print(self.link[qModelIndex.row()]) #調試輸出選中的列表Index
self.movie = getMovie(self.link[qModelIndex.row()])
# print(self.movie)
self.initMovie()
3.按鈕單擊方法
裏面很多代碼在下方貼,最終可運行Py文件見下方鏈接
# 按鈕單擊方法
def btnClick(self, btn):
# print(btn.text())# 輸出被點擊的按鈕
if (btn.text() == "下一頁"):
self.page += 1
self.getCatalog(self.getMovieType(self.type).format(self.page))
self.label_page.setText(str(self.page))
# print(self.catalog)
self.updateListCatalog()
elif (btn.text() == "上一頁"):
self.page -= 1
self.getCatalog(self.getMovieType(self.type).format(self.page))
self.label_page.setText(str(self.page))
# print(self.catalog)
self.updateListCatalog()
elif (btn.text() == "下載"):
QMessageBox.information(MainWindow, "下載", "所有的下載鏈接沒爬出來")
4.更新列表目錄
重新獲取目錄變量並賦值給列表
# 更新列表目錄
def updateListCatalog(self):
slm = QStringListModel()
slm.setStringList(self.catalog)
self.listView.setModel(slm)
5.返回電影type鏈接格式
這裏是根據type變量的值,來控制你爬取的是哪個類型的電影目錄
# 返回電影type鏈接格式
def getMovieType(self, type):
if (type == 1):
return self.URL_NEW
elif (type == 2):
return self.URL_COMPREHENSIVE
elif (type == 3):
return self.URL_DOMESTIC
elif (type == 4):
return self.URL_EUROPE_AND_AMERICA
elif (type == 5):
return self.URL_JAPAN_AND_KOREA
3.詳情頁爬蟲
# 模擬瀏覽器訪問url並獲取頁面內容,返回元組
def getMovie(url):
# 獲取html
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'} # 模擬瀏覽器登入
r = requests.get(url, headers=headers, timeout=10) # 獲取網頁
except:
pass
# print(r.status_code)
else:
yuanma = r.content.decode('gb2312', 'ignore') # 解碼gb2312,忽略其中有異常的編碼,僅顯示有效的編碼
# print(yuanma)
movie = {}
html = etree.HTML(yuanma)
title = html.xpath("//div[@class='title_all']//font[@color='#07519a']/text()")[0]
# print(title)
movie['title'] = title
zoomE = html.xpath("//div[@id='Zoom']")[0]
imgs = zoomE.xpath(".//img/@src")
cover = imgs[0] # 電影海報
movie['cover'] = cover
infos = zoomE.xpath(".//text()")
# print(infos)
for index, info in enumerate(infos):
if info.startswith("◎年 代"):
# print(info)
info = info.replace("◎年 代", "").strip()
# print(info)
movie['year'] = info
elif info.startswith("◎產 地"):
info = info.replace("◎產 地", "").strip()
movie['country'] = info
elif info.startswith("◎類 別"):
info = info.replace("◎類 別", "").strip()
movie['category'] = info
elif info.startswith("◎豆瓣評分"):
info = info.replace("◎豆瓣評分", "").strip()
movie['douban_rating'] = info
elif info.startswith("◎片 長"):
info = info.replace("◎片 長", "").strip()
movie['duration'] = info
elif info.startswith("◎導 演"):
info = info.replace("◎導 演", "").strip()
movie['director'] = info
# 影片的主演有多個,所有要添加判斷
elif info.startswith("◎主 演"):
info = info.replace("◎主 演", "").strip()
# print(info)
actors = [info, ]
for x in range(index + 1, len(infos)):
actor = infos[x].strip()
# 此處對應修復缺陷5,因爲有的電影詳情頁中沒有標籤,修復失敗
if actor.startswith("◎標 籤") or actor.startswith("◎簡 介"):
break
actors.append(actor)
movie['actors'] = actors
elif info.startswith("◎簡 介"):
info = info.replace("◎簡 介", "").strip()
for x in range(index + 1, len(infos)):
profile = infos[x].strip()
if profile.startswith("【下載地址】"):
break
# 因爲在這裏下載地址下面有一行空格,不加if的話會替換掉profile,使profile爲空格
if (len(movie) == 9):
movie['profile'] = profile
# print(movie.get("profile"))
# 下載地址
download_url = html.xpath("//td[@bgcolor='#fdfddf']/a/@href")
# print(download_url)
movie['download_url'] = download_url
return movie
4.詳情頁圖片顯示
# 獲取圖片,下載圖片至緩存目錄;即二次加載不用下載圖片
def getImage(image_url, image_name):
root = 'D://img//'
image_name = root + image_name + ".jpg"
try:
if not os.path.exists(root): # 判斷文件夾是否存在,不存在則創建文件夾
os.mkdir(root)
if not os.path.exists(image_name): # 判斷圖片文件是否存在,存在則進行提示
s = requests.get(image_url) # 通過requests.get方式獲取文件
# 使用with語句可以不用自己手動關閉已經打開的文件流
with open(image_name, "wb") as f: # 開始寫文件,wb代表寫二進制文件
f.write(s.content)
# print("爬取完成")
else:
print("文件已存在")
except Exception as e:
print("爬取失敗:" + str(e))
5.菜單欄單擊事件
# 菜單單擊方法
def menuClick(self, q):
# print(q.text() + ':被單擊') # 輸出那個Qmenu對象被點擊
# QMessageBox.[information,question,warning,ctitical,about]
# QMessageBox.about(self, "消息框標題", "這是關於軟件的說明", QMessageBox.Yes | QMessageBox.No)
if (q.text() == "關於"):
QMessageBox.about(MainWindow, "關於本微型客戶端", ""
"本站嚴禁提供任何帶色 情,違法內容的影片!"
"歡迎大家監督,有問題可發郵件到ygkf88#gmail.com(請將#換成@),"
"本站所有資源來源於網友交流,"
"只供網絡測試、請在24小時內刪除所下內容,"
"開始清理無版權的內容,"
"請大家支持正版到影院觀看或購買正版CD!"
"")
elif (q.text() == "協議"):
QMessageBox.about(MainWindow, "協議", "本軟件數據來源於電影天堂(ygdy8.net)。用於學習,請勿用於商用")
elif (q.text() == "軟件缺陷"):
QMessageBox.about(MainWindow, "軟件缺陷", "本軟件僅用於Python實踐,軟件缺陷如下\n"
"1.代碼沒優化,如獲取目錄時多處代碼冗餘,又如本行代碼下的方法\n"
"2.加載緩慢,爬取下載鏈接爬不出來\n"
"3.主演那,本來設計的時候覺得應該能放得下,結果放不下,懶得改控件了\n"
"4.沒有跳轉頁面,只能一頁頁加載,不寫的裏有同上\n"
"5.詳情頁爬蟲有點問題,電影天堂詳情頁的格式不太統一,導致加載有些界面會卡死\n"
"6.詳情頁爬蟲本來發現問題後,想做兩個的,一個xpath加載詳情頁數據,一個"
"BeautifulSoup方式加載,懶得寫BeautifulSoup式了"
"")
elif (q.text() == "最新電影"):
self.type = 1
self.page = 1
self.getCatalog(self.getMovieType(self.type).format(self.page))
self.label_page.setText(str(self.page))
# print(self.catalog)
self.updateListCatalog()
elif (q.text() == "綜合電影"):
self.type = 2
self.page = 1
self.getCatalog(self.getMovieType(self.type).format(self.page))
self.label_page.setText(str(self.page))
# print(self.catalog)
self.updateListCatalog()
elif (q.text() == "國內電影"):
self.type = 3
self.page = 1
self.getCatalog(self.getMovieType(self.type).format(self.page))
self.label_page.setText(str(self.page))
# print(self.catalog)
self.updateListCatalog()
elif (q.text() == "歐美電影"):
self.type = 4
self.page = 1
self.getCatalog(self.getMovieType(self.type).format(self.page))
self.label_page.setText(str(self.page))
# print(self.catalog)
self.updateListCatalog()
elif (q.text() == "日韓電影"):
self.type = 5
self.page = 1
self.getCatalog(self.getMovieType(self.type).format(self.page))
self.label_page.setText(str(self.page))
# print(self.catalog)
self.updateListCatalog()
elif (q.text() == "刪除緩存"):
QMessageBox.about(MainWindow, "刪除緩存", "這裏功能沒有寫,大致是兩種方法,1,遍歷文件夾文件,循環刪除至"
"空文件夾,刪不刪文件夾視情況;2.直接刪除目錄,再次打開時直接新建"
"緩存目錄")
elif (q.text() == "打開緩存目錄"):
openResourceManager()
elif (q.text() == "退出軟件"):
sys.exit()
總結
基本重點代碼以上,還有初始化數據的代碼,及一部分工具方法沒放上去
最終結果:
下載鏈接:鏈接: https://pan.baidu.com/s/14omsJMpqo1Gl6MAx7_S7hg 提取碼: 821o