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.

 1import sys
 2import time
 3import shutil
 4
 5import pyvirtualdisplay
 6import selenium
 7
 8
 9def make_screenshot(url, img, window_size=(1280, 1024), browser_timeout=15, sleep_time=5):
10	window_width  = window_size[0]
11	window_height = window_size[1]
12	screen_size = (window_width + 100, window_height + 100)
13
14	# Create virtual display
15	display = pyvirtualdisplay.Display(visible=False, size=screen_size)
16	display.start()
17
18	# Select best webdriver
19	if shutil.which("geckodriver"):
20		browser = selenium.webdriver.Firefox(service_log_path="/dev/null")
21
22	elif shutil.which("chromedriver"):
23		browser = selenium.webdriver.Chrome()
24
25	else:
26		print("Errror: Neither geckodriver nor chromedriver found in $PATH!", file=sys.stderr)
27		print("Please install at least one of them (geckodriver is preferred, but not", file=sys.stderr)
28		print("available on Raspberry Pi OS for example.)", file=sys.stderr)
29		sys.exit(1)
30
31	# Resize browser
32	browser.set_window_size(
33		window_width,
34		window_height
35	)
36	

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.

37	# Load URL
38	browser.get(url)
39
40	# Set timeouts
41	browser.set_script_timeout(browser_timeout)
42	browser.set_page_load_timeout(browser_timeout)
43
44	# Wait a few seconds so the page get's rendered...
45	time.sleep(sleep_time)
46	
47	# Hide scrollbars (if not we might get a white border at the right)
48	browser.execute_script('document.querySelector("body").style.overflow="hidden";')
49	

Screenshot erstellen

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

50	# Save screenshot
51	browser.save_screenshot(img)
52
53	# Bye bye...
54	browser.quit()
55	display.stop()
56	

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

57def main():
58	url = sys.argv[1]
59	img = sys.argv[2]
60	if os.path.exists(img):
61		print("File exists: {}".format(img), file=sys.stderr)
62		return 1
63
64	make_screenshot(url, img)
65
66	return 0
67
68
69if __name__ == "__main__":
70	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.