• 목록
  • 아래로
  • 위로

안녕하세요?


개인적인 목적으로 사용하려고 파이썬과 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%) 1905850/115200000EXP

Make StudyForUs Great Again!

 

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 711
공지 [중요] 호스팅 만료와 관련하여 일부 수칙이 변경됩니다. 4 마스터 23.01.14.02:23 4384
공지 [필독] 질문하는 방법 17 마스터 18.02.23.03:09 4475
825 500 에러 4 wikiowner 20.12.12.10:05 134
824 서버 초심자가 쓰기에 어떤 OS가 나을까요? 12 제르엘 20.12.11.23:00 278
823 사이트 들어가려는데 오류 2 image wikiowner 20.12.09.19:09 103
822 Set up the wiki 18 Whitetiger0423 20.12.07.10:37 437
821 홈페이지 디자인을 바꾼 후부터 게시판 스킨 적용이 이상하게 됩니다 image 손가락개 20.12.04.02:56 66
820 혹시 FTP 접속 잘 되시나요?? 2 원미 20.12.01.21:51 101
819 카페대문 만들기 힘드네요 .. 공구테라스 20.11.26.16:24 103
818 php 관련해서 질문 좀 여쭙겠습니다 image title: 대한민국 국기gimmepoint 20.11.23.20:50 109
817 [CSS] 애니메이션 관련 4 260578 20.11.21.23:24 116
816 요즘 코로나 백신 소식이 자주나오는데 내년에는 괜찮을까요? 3 필우 20.11.13.10:41 74
815 ftp web파일 1 wikiowner 20.11.11.22:16 57
814 중고 모니터 액정이 나갔는데 수리하는게 나을까요? 새로 사는게 나을까요? 10 필우 20.11.11.10:47 137
813 폴더 권한 관련 질문 드립니다. 5 image 이니스프리 20.11.08.14:47 94
812 맵네임을 누르면 다른 사이트의 게시판을 나오게 하고싶은데 어떻게 해야하나요? 1 손가락개 20.11.08.01:01 51
811 위키 설치 하실줄 아시는분,,,, 2 wikiowner 20.11.06.20:30 133
810 FTP 연결 안됌.... 2 image wikiowner 20.11.06.19:18 78
809 도메인 싸거나 공짜로 파는 곳 어디 있을까요 6 출사로 20.11.06.07:22 261
808 위키 호스팅에 대해 궁금점 9 wikiowner 20.11.04.15:05 131
807 사이트 정보 등록 3 citen 20.10.29.17:36 84
806 키보드 트레이 추천을 부탁드려요 ^^ 3 이니스프리 20.10.29.00:05 121