malte70.blog()

Website-Screenshots mit Python und Selenium

Mit Hilfe von Selenium und PyVirtualDisplay lassen sich auch ohne laufende grafische Oberfläche Screenshots von Webseiten anfertigen.

PyVirtualDisplay nutzt dabei den virtuellen XServer Xvfb, und Selenium steuert einen so genannten WebDriver, eine programmierbare Instanz eines Webbrowsers (im Falle von Geckodriver ein Firefox, oder Chromium/Chrome mit Chromedriver).

Im folgenden zeige ich, wie sich ein einfaches Skript erstellen lässt, das in einer Funktion (make_screenshot()) einen Screenshot von einer Webseite erstellt, und sich über die Kommandozeile bedienen lässt.

Virtuellen Display und WebDriver einrichten

In der Funktion make_screenshot() wird zunächst der virtuelle Bildschirm erstellt. Dabei wird sicherheitshalber die Höhe und Breite des Browserfensters um 100 Pixel erweitert.

Danach wird versucht, eine Instanz des Firefox-Treibers GeckoDriver erstellt. Da dieser zum Beispiel unter Raspbian nicht verfügbar ist, wird ein Fallback auf ChromeDriver eingerichtet. Damit GeckoDriver keine Log-Datei erstellt, was das Standardverhalten ist, wird /dev/null als Logdatei-Pfad eingerichtet. (Bei ChromeDriver ist das nicht notwendig.) Danach wird die Größe des Browserfensters gesetzt.

import sys
import time
import shutil

import pyvirtualdisplay
import selenium


def make_screenshot(url, img, window_size=(1280, 1024), browser_timeout=15, sleep_time=5):
	window_width  = window_size[0]
	window_height = window_size[1]
	screen_size = (window_width + 100, window_height + 100)

	# Create virtual display
	display = pyvirtualdisplay.Display(visible=False, size=screen_size)
	display.start()

	# Select best webdriver
	if shutil.which("geckodriver"):
		browser = selenium.webdriver.Firefox(service_log_path="/dev/null")

	elif shutil.which("chromedriver"):
		browser = selenium.webdriver.Chrome()

	else:
		print("Errror: Neither geckodriver nor chromedriver found in $PATH!", file=sys.stderr)
		print("Please install at least one of them (geckodriver is preferred, but not", file=sys.stderr)
		print("available on Raspberry Pi OS for example.)", file=sys.stderr)
		sys.exit(1)

	# Resize browser
	browser.set_window_size(
		window_width,
		window_height
	)

URL laden und Scrollbars entfernen

Nun wird die URL geladen, und ein Timeout für das Laden der Webseite gesetzt. Damit auch aufwendigere Seiten Zeit haben z.B. via JavaScript externe Inhalte nachzuladen, wird mit time.sleep() fünf Sekunden gewartet.

Um am rechten Rand keinen weißen Bereich durch Scrollbars zu haben, wird die CSS-Eigenschaft overflow des <body>-Elements via JavaScript auf hidden gesetzt.

	# Load URL
	browser.get(url)

	# Set timeouts
	browser.set_script_timeout(browser_timeout)
	browser.set_page_load_timeout(browser_timeout)

	# Wait a few seconds so the page get's rendered...
	time.sleep(sleep_time)

	# Hide scrollbars (if not we might get a white border at the right)
	browser.execute_script('document.querySelector("body").style.overflow="hidden";')

Screenshot erstellen

Zu guter Letzt wird der Screenshot erstellt und Browser sowie virtueller Display beendet.

	# Save screenshot
	browser.save_screenshot(img)

	# Bye bye...
	browser.quit()
	display.stop()

main()-Funktion

Die main()-Funktion nimmt den ersten und zweiten Komandozeilen-Parameter für die URL und den Screenshot-Dateinamen; und erstellt, solange die Bilddatei nicht existiert den Screenshot mithilfe der Funktion make_screenshot().

def main():
	url = sys.argv[1]
	img = sys.argv[2]
	if os.path.exists(img):
		print("File exists: {}".format(img), file=sys.stderr)
		return 1

	make_screenshot(url, img)

	return 0


if __name__ == "__main__":
	sys.exit(main())

Screenshot erstellen

Nun kann das Skript genutzt werden, um einen Screenshot zu erstellen:

python3 website-screenshot.py \
	"https://malte70.de/blog/website-screenshots-python-selenium/" \
	blogpost.png
Eine erweiterte Version des hier vorgestellten Skripts findet sich in meinem scripts-Repo auf Github. Diese erlaubt auch, die Fenstergröße und Timeouts über Umgebungsvariablen anzupassen.