- 3
- 이니스프리
- 조회 수 6837
안녕하세요?
개인적인 목적으로 사용하려고 파이썬과 PyQt를 이용하여 디시인사이드의 이미지를 다운받는 스크립트를 작성했는데요.
80% 정도 완성한 단계인데요 PyQt의 GUI가 반응이 없는 현상이 계속되어서 질문을 드립니다 ㅠㅠ
우선 .py 파일입니다.
import requests, time, re, sys, os, dc_get_ui from bs4 import BeautifulSoup from PyQt5.QtCore import * from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5 import uic def parse_page(gall_name, page): # 특정 페이지의 글 번호를 파싱합니다 headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'} page_url = 'https://gall.dcinside.com/mgallery/board/lists?id=' + gall_name + '&page=' + str(page) req = requests.get(page_url, headers=headers) html = req.text soup = BeautifulSoup(html, 'html.parser') list_nums = soup.find_all('td',{'class':'gall_num'}) article_no = [] for list_num in list_nums: if list_num.text.isdigit() == True: article_no.append(list_num.text) return article_no def download(gall_name, article_no): # 특정 글의 이미지 파일을 다운로드합니다 article_url = 'https://gall.dcinside.com/mgallery/board/view/?id=' + gall_name + '&no=' + article_no headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'} req = requests.get(article_url, headers=headers) html = req.text soup = BeautifulSoup(html, 'html.parser') title = soup.select_one('h3 span.title_subject').text title = re.sub('[^가-힣a-zA-Z0-9\s]', '', title) write_time = soup.select_one('span.gall_date')['title'].split(' ')[0] ul_li_as = soup.select('ul.appending_file li a') count = 0 if len(ul_li_as) != 0: for ul_li_a in ul_li_as: base_url = ul_li_a['href'].replace('download', 'viewimage') file_name = ul_li_a.text image_req = requests.get(base_url, headers=headers) if image_req.status_code == 200: with open('./bona/' + write_time + ' ' + title + ' - ' + file_name, 'wb') as f: f.write(image_req.content) del image_req count += 1 message = title + '(' + str(count) + ')' print(message) return message class MainDialog(QDialog, dc_get_ui.Ui_Dialog): # GUI 관련 부분입니다 def __init__(self): QDialog.__init__(self, None) self.setupUi(self) self.init_UI() def init_UI(self): self.pushButton_1.clicked.connect(self.start) self.pushButton_2.clicked.connect(self.select_dir) def select_dir(self): directory = str(QFileDialog.getExistingDirectory(self, "Select Directory")) #os.startfile(directory) def start(self): gallery = self.lineEdit_1.text() if self.lineEdit_2.text() == '0': start = 1 else: start = int(self.lineEdit_2.text()) if self.lineEdit_3.text() < self.lineEdit_2.text(): end = int(self.lineEdit_2.text()) + 1 else: end = int(self.lineEdit_3.text()) + 1 all_article_no = [] for i in range(start, end): all_article_no = all_article_no + parse_page(gallery, i) for temp in all_article_no: result = download(gallery, temp) time.sleep(1) self.textBrowser.append(result) # 이 부분이 문제가 되고 있습니다 if __name__ == '__main__': app = QApplication(sys.argv) window = MainDialog() window.show() app.exec_()
PyQt5로 만들고 .py 파일로 변환한 파일입니다.
from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Dialog(object): def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(800, 579) self.textBrowser = QtWidgets.QTextBrowser(Dialog) self.textBrowser.setGeometry(QtCore.QRect(20, 120, 761, 421)) self.textBrowser.setObjectName("textBrowser") self.pushButton_1 = QtWidgets.QPushButton(Dialog) self.pushButton_1.setGeometry(QtCore.QRect(20, 70, 111, 31)) self.pushButton_1.setObjectName("pushButton_1") self.comboBox = QtWidgets.QComboBox(Dialog) self.comboBox.setGeometry(QtCore.QRect(20, 20, 91, 31)) self.comboBox.setObjectName("comboBox") self.comboBox.addItem("") self.comboBox.addItem("") self.lineEdit_1 = QtWidgets.QLineEdit(Dialog) self.lineEdit_1.setGeometry(QtCore.QRect(130, 20, 651, 31)) self.lineEdit_1.setObjectName("lineEdit_1") self.pushButton_2 = QtWidgets.QPushButton(Dialog) self.pushButton_2.setGeometry(QtCore.QRect(150, 70, 111, 31)) self.pushButton_2.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) self.pushButton_2.setAutoDefault(False) self.pushButton_2.setObjectName("pushButton_2") self.label = QtWidgets.QLabel(Dialog) self.label.setGeometry(QtCore.QRect(20, 551, 761, 21)) self.label.setObjectName("label") self.checkBox = QtWidgets.QCheckBox(Dialog) self.checkBox.setGeometry(QtCore.QRect(270, 80, 131, 16)) self.checkBox.setAutoFillBackground(False) self.checkBox.setChecked(True) self.checkBox.setObjectName("checkBox") self.label_2 = QtWidgets.QLabel(Dialog) self.label_2.setGeometry(QtCore.QRect(380, 80, 81, 16)) self.label_2.setObjectName("label_2") self.label_3 = QtWidgets.QLabel(Dialog) self.label_3.setGeometry(QtCore.QRect(580, 80, 81, 16)) self.label_3.setObjectName("label_3") self.lineEdit_2 = QtWidgets.QLineEdit(Dialog) self.lineEdit_2.setGeometry(QtCore.QRect(460, 70, 71, 31)) self.lineEdit_2.setMaxLength(2) self.lineEdit_2.setObjectName("lineEdit_2") self.lineEdit_3 = QtWidgets.QLineEdit(Dialog) self.lineEdit_3.setGeometry(QtCore.QRect(640, 70, 71, 31)) self.lineEdit_3.setObjectName("lineEdit_3") self.retranslateUi(Dialog) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): _translate = QtCore.QCoreApplication.translate Dialog.setWindowTitle(_translate("Dialog", "Dialog")) self.pushButton_1.setToolTip(_translate("Dialog", "Start download.")) self.pushButton_1.setText(_translate("Dialog", "Download")) self.comboBox.setToolTip(_translate("Dialog", "Choose between major & minor gallery.")) self.comboBox.setItemText(0, _translate("Dialog", "Gallery")) self.comboBox.setItemText(1, _translate("Dialog", "Minor gallery")) self.lineEdit_1.setToolTip(_translate("Dialog", "Input gallery name.")) self.pushButton_2.setToolTip(_translate("Dialog", "Select folder to download files.")) self.pushButton_2.setText(_translate("Dialog", "Select folder")) self.label.setText(_translate("Dialog", "Total files downloaded : ")) self.checkBox.setToolTip(_translate("Dialog", "Open folder after finishing downloading.")) self.checkBox.setText(_translate("Dialog", "Open folder")) self.label_2.setText(_translate("Dialog", "From page :")) self.label_3.setText(_translate("Dialog", "To page:")) self.lineEdit_2.setToolTip(_translate("Dialog", "Input the starting page number which you want to download.")) self.lineEdit_2.setInputMask(_translate("Dialog", "99")) self.lineEdit_2.setText(_translate("Dialog", "1")) self.lineEdit_3.setToolTip(_translate("Dialog", "Input the final page number which you want to download.")) self.lineEdit_3.setInputMask(_translate("Dialog", "99")) self.lineEdit_3.setText(_translate("Dialog", "1")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) Dialog = QtWidgets.QDialog() ui = Ui_Dialog() ui.setupUi(Dialog) Dialog.show() sys.exit(app.exec_())
스크립트가 장황한데 문제가 되는 부분을 간단히 말씀을 드리면요.
일단 게시판 파싱과 이미지 다운로드 부분은 잘 작동을 하더군요.
그런데 GUI 관련하여 문제가 있는데 도저히 해결이 안 되네요 ㅠㅠ
파일을 다운로드 받은 후에 그 제목을 GUI에 표시하려고 self.textBrowser.append(result)을 삽입했는데요.
(실행파일 아래에서부터 8번째 줄에 있습니다)
GUI가 프리징 되면서 전혀 작동하지 않더군요.
멀티프로세싱으로 해결해보려고 해도 제 실력으로는 잘 안 되구요 ㅜㅜ
제가 구글링을 해본바로는 stackoverflow의 다음글이 관련된 내용인 것 같은데 도저히 이해가 안 되네요 ㅠㅠ
https://stackoverflow.com/questions/41526832/pyqt5-qthread-signal-not-working-gui-freeze
아마도 유사한 문제가 PyQt5에서 종종 발생하는 것 같아요.
결론적으로 PyQt5 자체의 문제인지, 프리징을 제가 어떻게 해결할 수 있는지 여쭤봅니다.
그럼 즐거운 주말 되세요!
스포어 회원님들께 항상 감사드립니다 ^-^
+)
버튼을 클릭해도 실행이 안 된다고 생각하실 수 있는데요 ㅠㅠ
현재까지는 마이너 갤러리에만 작동하도록 되어있어요.
(갤러리 / 마이너 갤러리 선택 드롭다운 메뉴는 아직 작동을 하지 않아요)
'war'이나 'bona'를 입력하시면 파싱은 잘 되더군요.
작성자
댓글 3
허걱 밤늦게 정말 감사합니다!
그러지 않아도 지금 막 페이스북의 파이썬 코리아에 질문글 올리려고 하던 참이었거든요 ㄷㄷ
저 혼자 구글링하면서 해결해보려고 해도 도저히 해결되지 않더군요 ㅠㅠ
humit 님께서 말씀해주신대로 반복문 안에 QApplication.processEvents()를 삽입한 후에 돌려보니
GUI가 정상적으로 작동하고 파일을 받아올 때마다 그 내역을 출력해주네요 ^^
그럼 날씨가 무더운데 humit 님께서도 바쁘시겠지만 항상 건강하시길 기원합니다!
바쁘신데 번번이 큰 도움을 주셔서 진심으로 감사드립니다 :)
네 열심히 코딩하세요 ㅎㅎㅎ
UI를 갱신하는 코드가 반복문 안에 있어서 발생하는 문제입니다.
저렇게 웹 요청을 보내는 네트워크 작업이나 파일을 쓰는 IO 작업의 경우에는 시간이 많이 소요되기 때문에, 멀티 프로세싱으로 하는게 좋긴 하지만 프로토 타입이라면 굳이 할 필요는 없겠네요.
해결법은 간단한데 해당 반복문 안에 QApplication.processEvents() 를 넣어줘서 pending 상태인 이벤트들을 처리해주면 됩니다. 즉 아래와 같이 한 줄만 추가해주시면 됩니다.
프로그램이 단일 스레드로 돌아가고 있어서 창을 움직일 때 약간 뚝뚝 끊기는 느낌을 받으실 수 있습니다.