Search code examples
pythonweb-scrapingpyqtpyqt6

Saving a website screenshot with pyqt6 / QtWebEngineView is always empty


I'm trying to save a screenshot of a website with QtWebEngineView, but the resulting image always ends up being empty.


import sys
import time
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import QApplication, QWidget
from PyQt6.QtWebEngineWidgets import QWebEngineView

class Screenshot(QWebEngineView):
    def __init__(self):
        self.app = QApplication(sys.argv)
        QWebEngineView.__init__(self)
        self._loaded = False
        self.loadFinished.connect(self._loadFinished)

    def capture(self, url, output_file):
        self.resize(QSize(1024, 768))
        self.load(QUrl(url))
        self.wait_load()

        image = QImage(self.size(), QImage.Format.Format_ARGB32)
        painter = QPainter(image)
        self.render(painter)
        painter.end()
        image.save(output_file)

    def wait_load(self, delay=0):
        # process app events until page loaded
        while not self._loaded:
            self.app.processEvents()
            time.sleep(delay)
        self._loaded = False

    def _loadFinished(self, result):
        self._loaded = True

s = Screenshot()
s.capture("https://www.google.com", "screenshot.png")

I've tried variations of the code where the QTWebEngineView is rendered in an application window and that part works. The wait_load also ensures the website is loaded before proceeding to paint. Yet somehow the rendered image is always a full white png instead of the website.

Edit: Voting to reopen because the solution in the "duplicate" does not seem to resolve this case.


Solution

  • The problem is caused by the QWebengineView not being rendered because the show() method is not called. As indicated in the other posts you should also use Qt.WidgetAttribute.WA_DontShowOnScreen. Here is a version that avoids using time.sleep and QXApplication.processEvents.

    import sys
    from PyQt6.QtCore import Qt, QEventLoop, QSize, QUrl
    from PyQt6.QtGui import QImage, QPainter
    from PyQt6.QtWidgets import QApplication
    from PyQt6.QtWebEngineWidgets import QWebEngineView
    
    
    class Screenshot(QWebEngineView):
        def __init__(self):
            self.app = QApplication(sys.argv)
            QWebEngineView.__init__(self)
            self.setAttribute(Qt.WidgetAttribute.WA_DontShowOnScreen)
            self.show()
            self._output_file = ""
            self._loop = QEventLoop()
            self.loadFinished.connect(self._loop.quit)
    
        def capture(self, url, output_file):
            self._output_file = output_file
            self.resize(QSize(1024, 768))
            self.load(QUrl(url))
            self.wait_load()
    
        def wait_load(self):
            self._loop.exec()
            image = QImage(self.size(), QImage.Format.Format_ARGB32)
            painter = QPainter(image)
            self.render(painter)
            painter.end()
            image.save(self._output_file)
    
    
    s = Screenshot()
    for i in range(10):
        s.capture("https://www.google.com", f"screenshot{i}.png")