Проблемы с вызовом PyQt5 рендеринга из функции - PullRequest
0 голосов
/ 17 апреля 2020

У меня есть скребок PyQt5, который должен визуализировать веб-страницу перед ее очисткой, так как веб-страница содержит динамические данные c. Это самая простая версия скрипта, которая, к сожалению, все еще состоит из нескольких частей.

Единственная причина, по которой рендеринг необходимо вызывать из функции, заключается в том, что иногда он зависает бесконечно, поэтому он имеет многопоточность тайм-аут на это. Это все хорошо, за исключением того, что Render не будет работать должным образом внутри функции, потому что QApplication по какой-то причине не передается должным образом. Я могу определить App = QApplication (sys.argv) и поместить класс Render в функцию ScrapeClockwise, но для этого необходимо также определить App внутри этой функции (по какой-то причине его нельзя передать). И затем, если функция по истечении этого времени он вылетит без закрытия QApplication, поэтому при следующем запуске функции программа просто вызовет sh. ЭТОТ ДАЖЕ ПРОИСХОДИТ, ЕСЛИ ЭТО ОПРЕДЕЛЕНО В ОТНОШЕНИИ ИСКЛЮЧИТЕЛЬНОЙ ЗАЯВКИ, что очень странно.

Как вы можете видеть, здесь много странных взаимодействий, и если бы кто-нибудь мог пролить свет на любую из них, я бы будь невероятно благодарным, я уже некоторое время бьюсь головой об этом.

import sys
from PyQt5.QtCore import *
from PyQt5.QtWebKitWidgets import *
from PyQt5.QtWidgets import *
from bs4 import BeautifulSoup
import threading
import functools
from threading import Thread

def timeout(timeout):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            res = [Exception('function [%s] timeout [%s seconds] exceeded!' % (func.__name__, timeout))]

            def newFunc():
                try:
                    res[0] = func(*args, **kwargs)
                except Exception as e:
                    res[0] = e

            t = Thread(target=newFunc)
            t.daemon = True
            try:
                t.start()
                t.join(timeout)
            except Exception as je:
                print('error starting thread')
                raise je
            ret = res[0]
            if isinstance(ret, BaseException):
                raise ret
            return ret

        return wrapper

    return deco

APP = QApplication(sys.argv)

class SomeClass(QWidget):
    def some_method(self):
        APP.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers | 
QEventLoop.WaitForMoreEvents)

class Render(QWebPage):
    def __init__(self, url):
        QWebPage.__init__(self)
        self.loadFinished.connect(self._loadFinished)
        self.mainFrame().load(QUrl(url))
        APP.exec_()

    def _loadFinished(self, result):
        self.frame = self.mainFrame()
        APP.quit()

def ScrapeClockwise(l):
    url = "https://www.clockwisemd.com/hospitals/" + str(l).zfill(4) + "/appointments/new"
    print(url)
    r = Render(url)
    result = r.frame.toHtml()
    soup = BeautifulSoup(result, 'html.parser')
    info = soup.find_all('h4')
    for i in info:
        print(i.get_text())

l = 0
while True:
    func = timeout(5)(ScrapeClockwise)
    try:
        func(str(l))
    except Exception as e:
        print(e)
        pass  # handle errors here
    l += 1

1 Ответ

0 голосов
/ 17 апреля 2020

Каждая технология имеет свои ограничения, и в случае Qt вы не можете использовать QWebPage во вторичном потоке. Вы также должны понимать, как работает технология, многие элементы Qt нуждаются и используют событие l oop, и это может помочь решить. В этом случае QTimer может использоваться для измерения истекшего времени, и если тайм-аут был запущен, загрузите новую страницу.

Используя этот вопрос Я изменил, чтобы получить это решение, учитывая вышеизложенное :

from PyQt5 import QtCore, QtWidgets, QtWebKitWidgets

from bs4 import BeautifulSoup


def create_urls():
    l = 0
    while True:
        yield "https://www.clockwisemd.com/hospitals/{:04d}/appointments/new".format(l)
        l += 1


class WebPage(QtWebKitWidgets.QWebPage):
    def __init__(self):
        super(WebPage, self).__init__()
        self.mainFrame().loadFinished.connect(self.handleLoadFinished)
        self.mainFrame().urlChanged.connect(print)

        self.timer = QtCore.QTimer(
            singleShot=True, interval=10 * 1000, timeout=self.on_timeout
        )

    def start(self, generator):
        self.generator = generator
        self.fetchNext()

    def fetchNext(self):
        url = next(self.generator)
        self.mainFrame().load(QtCore.QUrl(url))
        self.timer.start()

    def processCurrentPage(self):
        html = self.mainFrame().toHtml()
        print("[url]: {}".format(self.mainFrame().url().toString()))

        soup = BeautifulSoup(html, "html.parser")
        info = soup.find_all("h4")
        for i in info:
            print(i.get_text())

    def on_timeout(self):
        print("[Timeout]")
        self.fetchNext()

    def handleLoadFinished(self):
        if self.timer.isActive():
            self.timer.blockSignals(True)
            self.timer.stop()
            self.timer.blockSignals(False)
        self.processCurrentPage()
        self.fetchNext()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    webpage = WebPage()
    webpage.start(create_urls())
    sys.exit(app.exec_())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...