• 목록
  • 아래로
  • 위로

안녕하세요?


개인적인 목적으로 사용하려고 파이썬과 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'를 입력하시면 파싱은 잘 되더군요.

작성자
이니스프리 119 Lv. (0%) 1876250/115200000EXP

당분간 일신상의 사정으로 쪽지나 댓글로 답변을 드리기 어렵습니다. 죄송합니다.

 

CSVpuymXAAAVVpd.jpg

댓글 3

title: 황금 서버 (30일)humit
profile image
+1

UI를 갱신하는 코드가 반복문 안에 있어서 발생하는 문제입니다.


저렇게 웹 요청을 보내는 네트워크 작업이나 파일을 쓰는 IO 작업의 경우에는 시간이 많이 소요되기 때문에, 멀티 프로세싱으로 하는게 좋긴 하지만 프로토 타입이라면 굳이 할 필요는 없겠네요.


해결법은 간단한데 해당 반복문 안에 QApplication.processEvents() 를 넣어줘서 pending 상태인 이벤트들을 처리해주면 됩니다. 즉 아래와 같이 한 줄만 추가해주시면 됩니다.


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): # 특정 글의 이미지 파일을 다운로드합니다
    print(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()
        print(gallery)
        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)
            QApplication.processEvents()
       
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainDialog()
    window.show()
    app.exec_()
    



프로그램이 단일 스레드로 돌아가고 있어서 창을 움직일 때 약간 뚝뚝 끊기는 느낌을 받으실 수 있습니다.


comment menu
2019.07.10. 00:20

신고

"humit님의 댓글"

이 댓글을 신고 하시겠습니까?

이니스프리 작성자 → humit
profile image

허걱 밤늦게 정말 감사합니다!

그러지 않아도 지금 막 페이스북의 파이썬 코리아에 질문글 올리려고 하던 참이었거든요 ㄷㄷ

저 혼자 구글링하면서 해결해보려고 해도 도저히 해결되지 않더군요 ㅠㅠ

 

humit 님께서 말씀해주신대로 반복문 안에 QApplication.processEvents()를 삽입한 후에 돌려보니

GUI가 정상적으로 작동하고 파일을 받아올 때마다 그 내역을 출력해주네요 ^^

 

그럼 날씨가 무더운데 humit 님께서도 바쁘시겠지만 항상 건강하시길 기원합니다!

바쁘신데 번번이 큰 도움을 주셔서 진심으로 감사드립니다 :)

comment menu
2019.07.10. 00:29

신고

"이니스프리님의 댓글"

이 댓글을 신고 하시겠습니까?

title: 황금 서버 (30일)humit → 이니스프리
profile image

네 열심히 코딩하세요 ㅎㅎㅎ

comment menu
2019.07.10. 00:41

신고

"humit님의 댓글"

이 댓글을 신고 하시겠습니까?

권한이 없습니다.
번호 제목 글쓴이 날짜 조회 수
공지 [작업 완료] 설 명절 맞이 서버 업데이트 안내 3 마스터 24.02.11.17:21 376
공지 [중요] 호스팅 만료와 관련하여 일부 수칙이 변경됩니다. 4 마스터 23.01.14.02:23 4060
공지 [필독] 질문하는 방법 17 마스터 18.02.23.03:09 4464
582 [그누보드] 테스트 메일 보내기가 안되요. 6 연우빠 19.07.23.01:31 2106
581 사이트 로고 디자인을 의뢰하기 전에 질문 드려요 ^^ 이니스프리 19.07.21.22:51 179
580 썸네일 추출관련 정규식 표현을 어떻게 해야하나요? 4 image 슬기 19.07.16.16:31 364
579 1일 간격으로 사이트가 비활성됩니다 1 260578 19.07.16.07:10 189
578 이베이에서 판매되는 Adobe master collection CS6 정품일까요? 8 이니스프리 19.07.12.17:12 1363
577 닷홈 인코딩 설정 관련해서 질문 드려요 ㅠㅠ 6 image 이니스프리 19.07.11.00:38 364
576 무료 아이콘 사이트 추천 부탁드려요 ^^ 4 이니스프리 19.07.09.20:15 228
PyQt 실행시 프리징 현상 관련하여 질문 드립니다! ㅠㅠ 3 이니스프리 19.07.06.03:37 6685
574 구글 에드센스 등록하려다보니.. 6 image 막시모 19.06.27.21:22 234
573 구글 광고를 등록하려고하는데.. 8 image 막시모 19.06.26.09:02 244
572 도메인 기관이전을 하려고 합니다 5 260578 19.06.13.22:18 213
571 나무위키 포크 위키 만들기 2 아엠이언맨 19.06.07.11:37 271
570 Freenom 무료 도메인 3 blueone 19.05.27.10:28 503
569 게시판 푸시 알림을 구현할 수 있을까요? 9 260578 19.05.22.21:38 591
568 이 코드 뭔가 이상해요... 18 image 국내산라이츄 19.05.20.16:38 614
567 [Summernote] 에디터 아이콘 문제 image 260578 19.05.19.21:58 350
566 부트스트랩 모달을 사용하면 모바일 환경에서 문제의 소지가 있을까요?? 2 이니스프리 19.05.15.22:26 255
565 윈도우 서버에 모니위키 설치 가능한가요? 6 제르엘 19.05.13.23:21 285
564 일본어 한 단어만 읽어주시면 감사하겠습니다!! 8 image 이니스프리 19.05.07.17:30 339
563 아이콘 라이센스의 저작자 표기 방법과 Dot icon 관련 사이트 추천 부탁드립니다. 이니스프리 19.05.06.12:46 267