我可以串行运行QApplication吗?

时间:2017-01-25 22:11:19

标签: python python-2.7 web-scraping pyside

我正在尝试创建一个可以获取网址列表或单个网址并渲染它们的类。

在列表的情况下,它将它们全部呈现并使包含所有htmls的字典可用。这很好。

在单个案例中,它需要一个url,呈现它并使html作为属性可用,然后退出。当我运行一次时,这可以正常工作,但当我尝试它2次或更多次时,它会在调用app.exec _()时锁定。

import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebPage, QWebFrame

class Renderer(QWebPage):
    def __init__(self):
        self.app = QCoreApplication.instance()
        if self.app == None:
            self.app = QApplication(sys.argv)
            self.app.Type(QApplication.Tty)
        QWebPage.__init__(self)
        self.pages = []

    def start(self, urls):
        #for lists
        try:
            self.loadFinished.disconnect()
        except Exception:
            pass
        self.loadFinished.connect(self.listFinished)
        self._urls = iter(urls)
        self.fetchNext()
        self.app.exec_()

    def fetchNext(self):
        #for lists
        try:
            url = next(self._urls)
        except StopIteration:
            return False
        else:
            self.mainFrame().load(QUrl(url))
        return True

    def listFinished(self):
        #for lists
        html = self.processCurrentPage()
        self.pages.append(html)
        if not self.fetchNext():
            self.app.quit()

    def processCurrentPage(self):
        url = self.mainFrame().url().toString()
        html = self.mainFrame().toHtml()
        return html

    def render(self, url):
        try:
            self.loadFinished.disconnect()
        except Exception:
            pass
        self.loadFinished.connect(self.singleFinished)
        self._url = url
        self.mainFrame().load(QUrl(url))
        self.app.exec_()

    def singleFinished(self):
        print "singleFinished"
        html = self.processCurrentPage()
        self._html = html
        self.app.quit()

我正在尝试做什么?如何修复此代码,以便多次调用render()?我应该只使用基于列表的版本吗?

当我尝试列表情况然后单个案例时,会出现同样的问题。我很确定它不喜欢我在quit()之后调用exec_(),但我还没有找到任何关于此的文档。

1 个答案:

答案 0 :(得分:0)

重新创建或重新使用QApplication对象有时会导致问题,具体取决于平台和/或正在使用的PyQt / PySide的特定版本。

因此我调整了示例代码,以便它使用本地事件循环,而不是不断地退出并重新启动应用程序事件循环。如果页面没有加载,则本地循环应该在30秒后超时。请注意,原始示例尝试创建控制台应用程序(但使用错误的语法)。但是,渲染Web页面需要一个完整的GUI应用程序(事实上,尝试在我的系统上简单地转储核心)。代码在ArchLinux上使用Python2和Python3与PySide-1.2.4和PyQt-4.12进行了测试(从普通控制台运行)。

import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import QWebPage, QWebFrame
# from PyQt4.QtCore import *
# from PyQt4.QtGui import *
# from PyQt4.QtWebKit import QWebPage, QWebFrame

class Renderer(QWebPage):
    def __init__(self):
        self.app = QApplication.instance()
        if self.app is None:
            self.app = QApplication(sys.argv)
        super(Renderer, self).__init__()
        self.mainFrame().loadFinished.connect(self.handleLoadFinished)
        self.loop = QEventLoop()

    def render(self, urls):
        self._urls = iter(urls)
        self.fetchNext()

    def fetchNext(self):
        self.loop.exit(0)
        try:
            url = next(self._urls)
        except StopIteration:
            return False
        else:
            self.mainFrame().load(QUrl(url))
            timer = QTimer()
            timer.setSingleShot(True)
            timer.timeout.connect(lambda: self.loop.exit(1))
            timer.start(30000)
            if self.loop.exec_() == 1:
                print('url load timed out: %s' % url)
        return True

    def processCurrentPage(self):
        url = self.mainFrame().url().toString()
        html = self.mainFrame().toHtml()
        print('loaded: [%d bytes] %s' % (self.bytesReceived(), url))

    def handleLoadFinished(self):
        self.processCurrentPage()
        self.fetchNext()

if __name__ == '__main__':

    r = Renderer()
    r.render(['http://en.wikipedia.org/'])
    r.render(['http://stackoverflow.com/'])

输出:

$ python2 test.py
loaded: [863822 bytes] http://en.wikipedia.org/wiki/Main_Page
loaded: [1718852 bytes] http://stackoverflow.com/