【问题标题】:Why does my PyQt code not execute totally when multithreading?为什么我的 PyQt 代码在多线程时不能完全执行?
【发布时间】:2019-07-26 13:29:39
【问题描述】:

我正在尝试使用 PyQt5 和多线程编写一个网络抓取工具,以便我可以并行抓取多个 url(我知道这一点:Scrape multiple urls using QWebPage 但我真的想写一个并行版本,真的看不出为什么它不起作用) 我已经写了这段代码:

python
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

from PyQt5.QtWebEngineWidgets import QWebEnginePage

import time

urlb = "https://www.google.fr/"


class Worker(QRunnable, QWebEnginePage):
    '''
    Worker thread
    '''
    def __init__(self, url):
        super(Worker, self).__init__()
        self.url = url
    
    def _on_load_finished(self):
        print("tfouuu")
        self.html = self.toHtml(self.Callable)
        print('Load finished')

    def Callable(self, html_str):
        self.html = html_str
    
    @pyqtSlot()
    def run(self):
        print("a") 
        time.sleep(2)
        print(self.url)
        print("b")
        QWebEnginePage.__init__(self)
        print("c")
        self.html = ''
        self.loadFinished.connect(self._on_load_finished)
        self.load(QUrl(url))
        print("d")

class MainWindow(QMainWindow):


    def __init__(self, *args, **kwargs):
        
        self.threadpool = QThreadPool()
        print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount())
        
        super(MainWindow, self).__init__(*args, **kwargs)
        
        worker = Worker(urlb)
        worker2 = Worker(urlb)
        self.threadpool.start(worker)
        self.threadpool.start(worker2)


    
    
app = QApplication([])
window = MainWindow()
app.exec_()

但我有两个问题:

  • 第一个是我的代码一直在不停地运行(我猜这与缺少 app.quit() 行有关,但我真的不知道该放在哪里)

  • 第二个问题主要是我的代码只打印 'a'、'b'、'c' -> 它不运行连接和加载部分

【问题讨论】:

  • 好吧,首先 Python 不会进行多进程处理,除非您专门为其添加了多处理方面,因此您需要对此进行研究——并且没有线程不是多处理,它只是多处理-threading 完全不同的概念——以防万一你认为这是 threading 应该做的事情
  • 1) 为什么你需要多线程?如果您想要多线程,因为是的,那么不幸的是您将无法做到这一点,因为 QWebEnginePage 不能生活在另一个线程中,如果您的目标是执行多个请求而无需等待前一个请求结束,因此可以提出另一种解决方案。
  • 2) 多线程不等于并行,和多处理一起都是并发技术,这些技术有什么共同点?他们希望同时执行多项任务,但每项任务都从不同的角度进行,各有优缺点和要求。在 Qt 的情况下,它只支持多线程,但并非所有类都可以在不同的线程中运行,QWebEnginePage 就是这种情况。
  • 谢谢@eyllanesc 的回答,我明白了。您正在考虑同时执行多个请求的其他解决方案是什么?

标签: python multithreading web-scraping pyqt qwebengineview


【解决方案1】:

QWebEngineView 不能也不应该在另一个线程上运行。

如果你想异步获取 html,那么你应该使用 Qt 信号:

from functools import partial
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets


class WebManager(QtCore.QObject):
    def __init__(self, parent=None):
        super(WebManager, self).__init__(parent)
        self.pages = []
        self.results = []

    def load(self, url):
        page = QtWebEngineWidgets.QWebEnginePage(self)
        page.loadFinished.connect(self._on_load_finished)
        self.pages.append(page)
        page.load(QtCore.QUrl(url))

    @QtCore.pyqtSlot(bool)
    def _on_load_finished(self, ok):
        page = self.sender()
        if not isinstance(page, QtWebEngineWidgets.QWebEnginePage):
            return
        if ok:
            wrapper = partial(self.callable, page)
            page.toHtml(wrapper)
        else:
            self.pages.remove(page)
            page.deleteLater()

    def callable(self, page, html):
        self.pages.remove(page)
        url = page.requestedUrl().toString()
        page.deleteLater()
        self.results.append((url, html))
        if not self.pages:
            QtWidgets.QApplication.quit()


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)

    manager = WebManager()

    pages = []
    format_url = "http://pyqt.sourceforge.net/Docs/PyQt5/%s.html"
    for name in dir(QtWebEngineWidgets):
        if name.startswith("Q"):
            url = format_url % name.lower()
            manager.load(url)
    app.exec_()
    for url, html in manager.results:
        print(url)
        print(html)

【讨论】:

  • 非常感谢您的帮助,它工作正常!!我开始了解更多 PyQt,再次感谢 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多