Capture d'écran complète d'une page web avec Python en cinq étapes

Cette méthode permet de générer une capture d'écran longue d'une page web en utilisant Python, PyQt5 pour l'affichage et le défilement, et PIL pour le traitement d'images. Elle s'articule autour de cinq phases distinctes.

Étape 1 : Configuration de l'interface et chargement de la page

On crée une fenêtre sans cadre avec PyQt5, on y intègre un navigateur web (QWebEngineView) qui charge l'URL cible. Une fois le chargement terminé, une fonction de vérification est déclenchée pour analysre les dimensions de la page.

class FenetreCapture(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle('Capture Web')
        self.hauteur_temp = 0
        self.setWindowFlag(Qt.WindowMinMaxButtonsHint, False)
        self.setWindowFlag(Qt.FramelessWindowHint, True)

    def demarrer_capture(self, url):
        self.navigateur = QWebEngineView()
        self.navigateur.load(QUrl(url))
        geometrie = self.choisir_ecran()
        self.setGeometry(geometrie)
        self.navigateur.loadFinished.connect(self.verifier_page)
        self.setCentralWidget(self.navigateur)

    def obtenir_taille_page(self):
        taille = self.navigateur.page().contentsSize()
        self.largeur_definie = taille.width()
        self.hauteur_definie = taille.height()
        return taille.width(), taille.height()

    def choisir_ecran(self):
        largeur_cible, hauteur_cible = 800, 1400
        bureau = QApplication.desktop()
        nombre_ecrans = bureau.screenCount()
        for indice in range(nombre_ecrans):
            rect = bureau.availableGeometry(indice)
            largeur_ecran, hauteur_ecran = rect.width(), rect.height()
            if largeur_ecran >= largeur_cible and hauteur_ecran >= hauteur_cible:
                return QRect(rect.left(), rect.top(), largeur_cible, hauteur_cible)
        return QRect(0, 0, largeur_cible, hauteur_cible)

if __name__ == '__main__':
    application = QApplication(sys.argv)
    fenetre = FenetreCapture()
    fenetre.show()
    application.exit(application.exec_())

Étape 2 : Calcul des segments de capture

Après le chargement de la page, on récupère sa hauteur totale. On divise cette hauteur par la hauteur visible de la fenêtre pour déterminer le nombre de captures complètes et la hauteur résiduelle. Un mintueur est instancié pour gérer les captures séquentielles.

    def verifier_page(self):
        largeur_page, hauteur_page = self.obtenir_taille_page()
        self.nb_segments, self.reste_hauteur = divmod(hauteur_page, self.height())
        if self.nb_segments == 0:
            self.nb_segments = 1
        self.outil_fusion = OutilFusionImages(self.nb_segments, self.reste_hauteur)
        self.minuteur = QTimer(self)
        self.minuteur.timeout.connect(self.executer_action)
        self.minuteur.setInterval(350)
        self.minuteur.start()

Étape 3 : Orchestration des captures et du défilement

La fonction exécutée par le minuteur contrôle la séquence : elle effectue une capture à chaque itération, défile la page vers le bas, et gère le reste une fois tous les segments principaux traités. L'image fusionnée est générée à la fin.

    def executer_action(self):
        if self.nb_segments > 0:
            self.capturer_ecran()
            self.defiler_page()
        elif self.nb_segments < 0:
            self.minuteur.stop()
            self.outil_fusion.fusionner_images()
            self.close()
        elif self.reste_hauteur > 0:
            self.capturer_ecran()
        self.nb_segments -= 1

    def defiler_page(self):
        code_js = """
            var faireDefiler = function(hauteurPas) {
                var positionActuelle = document.documentElement.scrollTop;
                var hauteurTotale = document.documentElement.scrollHeight;
                var nouvellePosition = positionActuelle + (hauteurPas || 0);
                if (nouvellePosition > hauteurTotale) {
                    window.scrollTo(0, document.documentElement.clientHeight);
                } else {
                    window.scrollTo(0, nouvellePosition);
                }
            };
        """
        commande = code_js + f'\n faireDefiler({self.height()});'
        self.navigateur.page().runJavaScript(commande)

Étape 4 : Sauvegarde des captures partielles

Chaque capture d'écran est réalisée via l'écran principal, convertie en image et sauvegardée dans un répertoire temporaire. L'objet image est ensuite ajouté à la liste de l'outil de fusion pour assemblage ultérieur.

    def capturer_ecran(self):
        ecran_principal = QApplication.primaryScreen()
        identifiant_fenetre = self.navigateur.winId()
        pixmap = ecran_principal.grabWindow(int(identifiant_fenetre))
        chemin_image = f'{self.outil_fusion.dossier_racine}/capture_temp.png'
        pixmap.save(chemin_image)
        self.outil_fusion.ajouter_image(chemin_image)

Étape 5 : Assemblage des images partielles

Cette classe gère la collection des images capturées. Elle ajuste la dernière image si nécessaire pour ne conserver que la partie visible résiduelle, puis assemble toutes les images verticalement en une seule image finale.

class OutilFusionImages:
    def __init__(self, nombre_segments, reste_hauteur):
        self.collection_images = []
        self.nombre_segments = nombre_segments
        self.reste_hauteur = reste_hauteur
        self.initialiser_chemins()

    def initialiser_chemins(self):
        self.dossier_racine = Path(__file__).parent / 'captures_temp'
        if not self.dossier_racine.exists():
            self.dossier_racine.mkdir(parents=True)
        self.chemin_sortie = self.dossier_racine / 'capture_finale.png'

    def ajouter_image(self, chemin_fichier):
        if len(self.collection_images) == self.nombre_segments:
            image_segment = self.recadrer_image(chemin_fichier)
        else:
            image_segment = Image.open(chemin_fichier)
        nom_fichier = f'{len(self.collection_images) + 1}.png'
        image_segment.save(self.dossier_racine / nom_fichier)
        self.collection_images.append(image_segment)

    def calculer_dimensions_finales(self):
        largeur_max = 0
        hauteur_totale = 0
        for img in self.collection_images:
            largeur, hauteur = img.size
            if largeur > largeur_max:
                largeur_max = largeur
            hauteur_totale += hauteur
        return largeur_max, hauteur_totale

    def fusionner_images(self):
        if len(self.collection_images) > 1:
            largeur_finale, hauteur_finale = self.calculer_dimensions_finales()
            image_finale = Image.new('RGB', (largeur_finale, hauteur_finale), 'white')
            position_y = 0
            for segment in self.collection_images:
                image_finale.paste(segment, (0, position_y))
                position_y += segment.height
            image_finale.save(self.chemin_sortie)
        else:
            image_unique = self.collection_images[0]
            image_finale = Image.new('RGB', image_unique.size, 'white')
            image_finale.paste(image_unique, (0, 0))
            image_finale.save(self.chemin_sortie)

    def recadrer_image(self, chemin_source):
        image_source = Image.open(chemin_source)
        largeur, hauteur = image_source.size
        boite_recadrage = (0, hauteur - self.reste_hauteur, largeur, hauteur)
        return image_source.crop(boite_recadrage)

# Exemple d'utilisation
if __name__ == '__main__':
    url_cible = 'https://example.com'
    app = QApplication(sys.argv)
    fenetre = FenetreCapture()
    fenetre.demarrer_capture(url_cible)
    fenetre.show()
    app.exit(app.exec_())

Étiquettes: Python PyQt5 Pillow Capture d'écran web traitement d'images

Publié le 20 juillet à 16h47