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_())