Python Browser Automatisierung mit Selenium
26.12.2020
Netzwerk Offline
Dein Netzwerk ist offline, deshalb laden wir die gesamte Seite aus dem Cache. Beachte: die Inhalte k├Ânnten veraltet sein!
F├╝r aktuelle Inhalte aktiviere bitte dein Internet!
Alter Browser
Hallo liebe Nutzer,
da wir als Coding-Plattform und Webentwickler ausschlie├člich auf aktuelle Technologien setzen, unterst├╝tzen wir keine veralteten Internetbrowser. Bitte schau dir doch ein paar der tollen Alternativen an, bestimmt findest du hier einen f├╝r dich passenden modernen Browser!

Selenium WebDriver in Python - die Webbrowser API

Inhaltsverzeichnis
[[TABLE OF CONTENTS]]
Teilen:
access_timeGesch├Ątzte Lesezeit ca. Minuten

Manchmal ist es notwendig Prozesse im Browser zu automatisieren oder die Funktionalitäten von Webseiten in regelmäßigen Abständen automatisiert zu testen und beim Fehlschlagen eine Information darüber zu erhalten. So kannst du Fehler schnellstmöglich beheben, damit deine Benutzer diese nicht erst melden müssen.

Selenium kann man auch zur Automatisierung verwenden. Vielleicht hast du eine kleine Webseite oder firmeninterne Webseite, auf der du immer zum Zeitpunkt X Daten eintragen musst, erhältst die Daten aber eigentlich auch automatisiert und könntest diese per API senden. Aber da es nur eine kleine firmeninterne Webseite ist und kein großes Budget da ist, um eine API zu ergänzen, ist das automatisierte Steuern vom Browser vielleicht eine Übergangslösung.

Ein letzter Anwendungsfall ist das Auslesen von Daten aus Webseiten mit JavaScript, denn mit einem Browser kann dieses ausgeführt werden. Bei einer einfachen Abfrage mit request kann das nicht erfolgen, da kein JavaScript Interpreter gestartet wird.

Github LogoSeleniumHQ/selenium
21.242 186 6.423

A browser automation framework and ecosystem.

In diesem Fall ist die Selenium WebDriver API genau das richtige Tool für dich. Damit kannst du einen Browser deiner Wahl mit verschiedenen Befehlen steuern und damit Interaktion eines Benutzers nachbauen. In unserem Beitrag zeige ich dir die Einrichtung von WebDrivern für Chrome, Firefox und Safari. Die API kannst du nicht nur mit Python verwenden, sondern zum Beispiel auch mit C#, Java, Kotlin, JavaScript, PHP, Ruby oder Rust. Da es eine API ist, kann man auch für jede andere beliebige Programmiersprache einen eigenen Wrapper dafür schreiben.

Alle Code-Beispiele die ich im nachfolgenden Artikel zeigen werde um Selenium zu Erklären sind, in einem Git-Repository abgelegt, sodass man diese selber herunterladen und testen kann. Du brauchst nur die richtigen Treiber für dein Betriebssystem im Ordner driver abzulegen.

Github Logofschuermeyer/hellocoding-selenium-examples
0 0 0

Selenium Beispiel für HelloCoding Artikel

Installation von Selenium in Python

Die Installation von Selenium selbst geht über den Paketmanger von Python: pip. Mit diesem kannst du das Paket "Selenium" und viele weitere ganz einfach installieren.

Mit dem Befehl kannst du Selenium Installieren über den Paketmanager PIP. Nach der Installation kannst du zur Einrichtung der WebDriver selbst übergehen.

pip install selenium

Einrichten vom Selenium WebDriver

Die WebDriver sind das Bindeglied zwischen der Programmiersprache (in unserem Fall Python) und dem Browser selbst. Ich habe dir eine Tabelle mit den Treibern für die bekanntesten Browser erstellt.

Wichtig: Du brauchst nicht nur den WebDriver, sondern das Gerät muss auch immer den Browser installiert haben, den du verwendest.

Thema der Tabelle "Selenium WebDriver Downloads"
´╗┐Browser Betriebsystem Download
Chromium Windows, Linux, macOS Chrome Driver
Firefox Windows, Linux, macOS Firefox Driver
Edge Windows 10, Linux, macOS Edge Driver
Edge Legacy Windows 10 Edge Driver Legacy
Internet Explorer Windows Internet Explorer Driver
Safari macOS Bereits Integriert

WebDriver für Chrome einrichten

Den Treiber für Chrome bekommst du auf der folgenden Webseite: https://chromedriver.storage.googleapis.com/index.html. Dort findest du diesen für alle möglichen Chrome Versionen und das für Linux, macOS und Windows.

Bei dem Chrome WebDriver ist es wichtig, dass du die richtige Version zu deinem aktuellen Browser herunterlädst, bei Firefox muss zum Beispiel nicht ein spezifischer Treiber für jede Version gewählt werden.

Nach dem Herunterladen des Treibers solltest du diesen so ablegen, dass du ihn vom Python Skript aus per Pfad-Angabe leicht erreichen kannst.

Nun zeig ich dir den eigentlichen Code. Die Einrichtung des Treibers in Python folge immer dem folgenden Schema: webdriver.<browsername> wird aufgerufen und der Parameter xcutable_path wird entsprechend gesetzt, abhängig davon, wo der Treiber liegt.

In unserem Fall hab ich eine kleine Funktion eingebaut, die das "CWD" (Current Working Directory) für diese Datei auf den Pfad des aktuellen Skripts legt, sodass ich relative Pfad-Angaben vom Skript aus machen kann, auch wenn ich es aus einem anderen Verzeichnis heraus ausführen würde.

from selenium import webdriver
import os

# Current Working Directionary auf den Pfad des aktuellen Skripts setzen
os.chdir(os.path.dirname(os.path.abspath(__file__))) 

driver = webdriver.Chrome(executable_path='../driver/chromedriver')

driver.maximize_window()

driver.get("https://www.hellocoding.de")

Dieses kleine Beispiel startet den Chrome Browser, öffnet ihn in einem maximierten Fenster und ruft dann die Startseite von HelloCoding.de ab.

Das Fenster könnte zum Beispiel so aussehen. In Chrome gibt es einen zusätzlichen grauen Balken mit einem Hinweis darauf, dass es sich bei dem Fenster um eine automatisierte Testumgebung handelt.

Chrome WebDriver Selenium
Chrome WebDriver Selenium

WebDriver für Firefox einrichten

Der Treiber für Firefox heißt "Gecko Driver", da die Rendering Engine von Firefox ebenfalls "Gecko" heißt. Den Treiber findest du auf der folgenden Github Seite. Gecko wird ebenfalls von Windows, macOS und Linux unterstützt.

Webdriver Download

Das Code-Beispiel macht das gleiche wie das Vorherige. Als Erstes setze ich wieder den Arbeitspfad auf den Pfad des Skripts selbst. Dann wird das Fenster geöffnet, maximiert und abschließend wird HelloCoding.de aufgerufen.

from selenium import webdriver
import os

os.chdir(os.path.dirname(os.path.abspath(__file__))) # Current Working Directionary auf den Pfad des aktuellen Skripts setzen

driver = webdriver.Firefox(executable_path='../driver/geckodriver')

driver.maximize_window()

driver.get("https://www.hellocoding.de")

Die automatisierte Testumgebung bei Firefox erkennst du am grau-gestreiften Design in der Suche/URL Leiste und an dem kleinen Roboter vor der URL.

Firefox Webdriver Selenium
Firefox Webdriver Selenium

WebDriver für Safari einrichten

Bei macOS muss der Befehl safaridriver --enable ausgeführt werden, um den WebDriver zu aktivieren. Im Vergleich zu den anderen Browsern ist kein vorheriges Herunterladen eines WebDrivers notwendig. Da Safari nur für macOS entwickelt wird, funktioniert natürlich auch Selenium mit Safari nur auf Geräte mit macOS als Betriebssystem.

Das Skript selbst ist wieder mit der gleichen Funktionalität ausgestattet, wie bei den Beispielen zuvor. Safari wird gestartet, anschließend wird das Fenster maximiert und zuletzt HelloCoding.de aufgerufen.

from selenium import webdriver

driver = webdriver.Safari()

driver.maximize_window()

driver.get("https://www.hellocoding.de")

In Safari erkennst du die automatisierte Steuerung des Browsers an dem orangen Balken im Suchfeld. Der nachfolgende Screenshot zeigt wie unser Fenster aussieht, das vom Python Skript erstellt wurde.

WebDriver Selenium Safari
WebDriver Selenium Safari

Elemente Selektieren

Es folgt eine Auflistung von verschiedenen Möglichkeiten zum Selektieren von Elementen. Dabei wird zwischen der Selektierung einzelner und mehrerer Elemente unterschieden. Je nachdem, ob du einzelne oder mehrere Elemente Selektieren möchtest, gibt es verschiedene Methoden.

Die Methoden zur Selektierung einzelner Elemente beginnen mit find_element_by_ und sind in der folgenden Tabelle dargestellt.

Thema der Tabelle "Einzelenden Elemente finden (Selenium)"
´╗┐Name Beschreibung
find_element_by_id Element anhand des Attributes "id" finden.
find_element_by_link_text Element anhand des Textes eines Links finden.
find_element_by_partial_link_text Element anhand eines Teiles des Textes von einem Link finden.
find_element_by_name Element anhand des Attributes "name" finden - das ist f├╝r Formularfelder n├╝tzlich.
find_element_by_tag_name Element anhand des Tag-Namens finden.
find_element_by_class_name Element anhand einer CSS Klasse finden.
find_element_by_xpath Element anhand eines xPaths finden.
find_element_by_css_selector Element anhand eines CSS-Selektors finden.

Die Methoden zur Selektierung mehrerer Elemente fangen mit find_elements_by_ an und sind in der folgenden Tabelle erklärt.

Thema der Tabelle "Mehrere Elemente finden (Selenium)"
´╗┐Name Beschreibung
find_elements_by_link_text Elemente anhand der Texte von Links finden.
find_elements_by_partial_link_text Elemente anhand eines Teils des Textes von einem Link finden.
find_elements_by_name Element anhand des Attributes "name" finden - das ist f├╝r Formularfelder n├╝tzlich.
find_elements_by_tag_name Elemente anhand der Tag-Namen finden.
find_elements_by_class_name Elemente anhand einer CSS Klasse finden.
find_elements_by_xpath Elemente anhand eines xPaths finden.
find_elements_by_css_selector Elemente anhand eines CSS-Selektors finden.

Jetzt schauen wir uns an, wie wir Elemente mit CSS-Selektoren und xPath selektieren können.

Ich persönlich bin mit CSS-Selektoren deutlich besser vertraut als mit xPath, da ich diese täglich in meinem beruflichen Alltag verwende. Trotzdem darf man xPath nicht unterschätzen, da man zum Beispiel bei xPath Eltern Elemente selektieren kann, was bei CSS ohne den zusätzlichen Einsatz von JavaScript unmöglich ist.

Selektieren mit xPath Selektoren

Die Variante mit xPath Selektoren ist deutlich umfangreicher als die CSS Variante und wie ich finde auch komplizierter zu verstehen.

Der CSS-Selektor würde in xPath deutlich länger und komplizierter zu schreiben sein.

Als Beispiel nehmen wir folgende Überschrift, die wir jeweils über CSS und xPath selektieren wollen.

<body>
    <div>
        <h1 class="test" id="test">Überschrift</h1>
    </div>
</body>

Ein Beispiel eines CSS-Selektors:

body>div h1#test.test

Der gleiche Ausdruck als xPath:

.//body/div//h1[@id="test"][contains(concat(" ",normalize-space(@class)," ")," test ")]

Wie du siehst, ist es deutlich einfacher einen CSS Selektor zu schreiben, als einen xPath Selektor.

Aber es gibt auch hier einen Vorteil. Du kannst Eltern Elemente selektieren, oder auch kleine if-Abfragen schreiben und hast Funktionen wie contains zur Verfügung. Außerdem kann xPath Elemente relativ zu anderen Elementen selektieren, was bei CSS nicht einfach möglich ist, wenn ein Element kein eindeutiges Attribut besitzt.

Ich habe dir hier ein Cheat Sheet herausgesucht, das dir die xPath Selektor-Schreibweise näher bringt: https://devhints.io/xpath

Abschließend ein Beispiel, wie du xPath Selektoren in Selenium verwenden kannst. Über den ersten Aufruf erhalten wir ein h1 Element von der Webseite, im Zweiten erhalten wir eine Liste von p Elementen.

driver.find_element_by_xpath('.//html//body//h1')

driver.find_elements_by_xpath('.//html//body//p')

Selektieren mit CSS-Selektoren

Die Selektierung über CSS-Selektoren ist die Standard-Variante in Browsern um Elemente zu selektieren. Falls du kein Webentwickler bist, kann ich dir das Cheatsheet von DEVHINTS empfehlen. Dort findest du einen kurzen Überblick über die Selektoren.

Ein Beispiel, wie du CSS-Selektoren in Selenium verwenden kannst. Über den ersten Aufruf erhalten wir ein h1 Element von der Webseite, über den Zweiten erhalten wir eine Liste von p Elementen.

driver.find_element_by_css_selector('html body h1')

driver.find_elements_by_css_selector('html body p')

Auf Elemente warten

Manchmal ist es notwendig auf Elemente zu warten, da man zum Beispiel weiß, dass sie auf der Seite erscheinen werden und, dass durch andere Elemente der Webseite die Ausgabe dieser Elemente verzögert wird.

Für das Warten auf Elemente müssen wir einige Pakete importieren.

from selenium.webdriver.common.by import By

By hält verschiedene Selektierungs-Möglichkeiten bereit. Unter anderem die Selektierung über CSS-Selektoren sowie xPath.

Das Paket expected_conditions hält verschiedene Möglichkeiten bereit, um auf Elemente zu warten. Zum Beispiel title_is, title_contains und element_to_be_clickable. Letzteres verwenden wir, um zu prüfen, ob ein Element vorhanden ist.

from selenium.webdriver.support import expected_conditions as EC

WebDriverWait kann auf eine "Expected Condition" warten. Wenn die angegebene Maximal-Zeit überschritten wird, kommt es zu einer TimeoutException. Diese kann bequem abgefangen werden, also der Fall, dass das Element in einer vorgegebenen Zeit nicht auf der Seite erscheint.

from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException

Es folgt ein Code-Beispiel welches prüft, ob ein Element auf der Seite vorhanden ist. In diesem Fall wird das Element angeklickt. Als Ziel wählen wir den "Cookie Einstellungen" Button.

Falls das Element nicht vorhanden ist, kommt es zu einer TimeoutException nach 10 Sekunden.

try:
    wait = WebDriverWait(driver, 10)

    element = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#CookieSettings > div > div > div > div > a')))

    element.click()
except TimeoutException as error:
    failed = True

Wie funktioniert das Warten auf Elemente?

Das Warten auf Elemente Funktioniert eigentlich ziemlich Einfach und kann Theoretisch auch schnell selbst Programmiert werden. Es wird alle 500ms eine Abfrage gemacht ob das Element Vorhanden ist falls nicht wird weitere 500ms gewartet anderen falls wird das Element zurück gegeben. Falls nach Ablauf der Angegeben Zeit immer noch kein Element gefunden wurde wird eine TimeoutException gefeuert.

Klicks und Tastatureingaben ausführen

Kommen wir jetzt zu einer der wichtigsten Funktionen von Selenium: dem Klicken und der Eingabe von Tasten. Ohne diese Funktionalität ergibt Selenium in der Verwendung wenig Sinn, da es ohne diese Optionen keine Möglichkeiten der Interaktion gibt. Als Beispiel zum Klicken und der Tastatureingaben, habe ich ein kleines zusammenhängendes Skript erstellt, mit dem du die Suche auf HelloCoding.de automatisiert ansteuern kannst:

driver.get("https://www.hellocoding.de")

driver.find_element_by_css_selector('#navigation-handler > ul > li.search.noChildren > a').click()

driver.find_element_by_css_selector('form.col-md-12 > input:nth-child(2)').send_keys('JavaScript')

driver.find_element_by_css_selector('button.btn.search-icon').click()

Auf ein Element klicken

Um ein Element zu klicken, benötigst du nur die Methode click. Diese ist für alle Elemente verfügbar. Die click Methode selbst hat keine weiteren Argumente.

driver.find_element_by_css_selector('#navigation-handler > ul > li.search.noChildren > a').click()

Eine Tastatureingabe tätigen

Tastatureingaben können, ähnlich wie beim Klicken, relativ einfach getätigt werden. Mit der Methode send_keys können wir Tastatureingaben tätigen.

driver.find_element_by_css_selector('form.col-md-12 > input:nth-child(2)').send_keys('JavaScript')

Special Keys

Für spezielle Schlüssel ("Special Keys") kann man auf vordefinierte Werte zugreifen. Diese sind unter common.keys abgelegt. Einen bestimmten Key können wir danach wie folgt abrufen.

from selenium.webdriver.common.keys import Keys

driver.find_element_by_css_selector('input').send_keys(Keys.NUMPAD9)

Die folgenden Keys sind in common.keys vorhanden und können verwenden werden:

  • ADD
  • ALT
  • ARROW_DOWN
  • ARROW_LEFT
  • ARROW_RIGHT
  • ARROW_UP
  • BACK_SPACE
  • CANCEL
  • CLEAR
  • COMMAND
  • DECIMAL
  • DELETE
  • DIVIDE
  • DOWN
  • END
  • ENTER
  • EQUALS
  • ESCAPE
  • F1
  • F2
  • F3
  • F4
  • F5
  • F6
  • F7
  • F8
  • F9
  • F10
  • F11
  • F12
  • HELP
  • HOME
  • INSERT
  • LEFT
  • LEFT_ALT
  • LEFT_CONTROL
  • LEFT_SHIFT
  • META
  • MULTIPLY
  • NULL
  • NUMPAD0
  • NUMPAD1
  • NUMPAD2
  • NUMPAD3
  • NUMPAD4
  • NUMPAD5
  • NUMPAD6
  • NUMPAD7
  • NUMPAD8
  • NUMPAD9
  • PAGE_DOWN
  • PAGE_UP
  • PAUSE
  • RETURN
  • RIGHT
  • SEMICOLON
  • SEPERATOR
  • SHIFT
  • SPACE
  • SUBTRACT
  • TAB
  • UP

Quelle: https://www.selenium.dev/selenium/docs/api/py/webdriver/selenium.webdriver.common.keys.html

Screenshots erstellen in Python mit Selenium

In Python ist es auch möglich Screenshots von der Webseite zu erstellen, auf der du dich befindest. Dafür wird die Methode save_screenshot verwendet. Falls du einen Pfad angibst, solltest du darauf achten, dass dieser bereits vorher existiert. Ansonsten wird der Screenshot nicht erstellt. Die Funktion gibt True oder False zurück, so kannst du eine eigene Prüfung einbauen. Das kann zum Beispiel so aussehen:

if driver.save_screenshot('screenshots/selenium-test.png'):
    print("Erfolgreich Erstellt")
else:
    print("Screenshot konnte nicht Erstellt werden")

JavaScript mit Selenium im Browser ausführen

Manchmal kommt es vor, dass man bestimmte Interaktionen vornehmen will, die sich nicht so einfach mit Selenium bewerkstelligen lassen. Zum Beispiel bestimmte JavaScript Variablen abfragen.

Als Beispiel fragen wir die Variable window.location ab und lassen uns die Werte in Python ausgeben. Du kannst auch Werte des Objekts window.location direkt abfragen, was je nach Anwendung sicherlich nützlich sein kann.

location = driver.execute_script("return window.location")

print(location)

print(location['host'])

Anwendungen mit einem Headless-Browser betreiben

Die Browser Chrome und Firefox unterstützen beide die Option headless zu laufen, das heißt ohne grafisches Benutzerinterface, sondern nur als Prozess im Hintergrund. Der Vorteil davon ist, dass du so automatische Tests auch im Hintergrund laufen lassen und parallel andere Sachen erledigen kannst. Bei einem Nicht-Headless-Browser würde sich das Fenster in den Vordergrund schieben, wenn du es startest. Das kann äußerst störend sein, wenn du mehrere Fenster öffnen willst.

Firefox über Selenium im Headless-Mode starten

Um Firefox im Headless-Mode zu starten, müssen wir das Argument --headless beim Start übergeben. Danach wird der Browser ohne eine GUI gestartet, also nur als Hintergrundprozess.

options = webdriver.firefox.options.Options()
options.add_argument('--headless')

driver = webdriver.Firefox(options=options,executable_path='../driver/geckodriver')

Chrome in Python Selenium im Headless-Mode starten

Der Aufbau, um Chrome im Headless-Mode in Python zu starten, ist sehr ähnlich zu dem Code vom Firefox. Wir tauschen nur das Wort "Chrome" durch "Firefox" aus. Das Argument zum Starten im Headless-Mode ist wieder --headless.

options = webdriver.chrome.options.Options()
options.add_argument('--headless')

driver = webdriver.Chrome(options=options,executable_path='../driver/chromedriver')

Eine Erste kleine Test Suite erstellen

Nun wollen wir uns anschauen, wie man eine Test Suite erstellen kann. Dafür benötigen wir weitere Module.

  • unittest - Das Standard Unit Testing Framework für Python.
  • HtmlTestRunner - Das ist ein Paket, mit dem die Resultate visuell dargestellt werden können. Ich habe dieses verwendet um euch in dem Beitrag eine elegantere Ausgabe liefern zu können.

Für unsere Tests wenden wir das Wissen aus den ersten Beispielen an. Wir erstellen 2 Tests: der Erste prüft, ob die Webseite aufgerufen werden kann. Diesen Test nennen wir test_loading_website. Der zweite Test stellt sicher, dass keine der Webseiten den Fehlercode 500 zurückgibt. Das erreichen wir, indem wir uns alle Links aus der Sitemap holen und dann den Quellcode der Seiten mit dem Text vergleichen, den ich bei HelloCoding für 500er Fehler definiert habe.

Wir können mit Selenium nicht direkt auf HTTP Status Codes zugreifen, deshalb empfiehlt sich diese Variante zu nutzen. Alternativ kannst du das request Modul nutzen, um dir die Status Codes zu holen. Je nach Test ergibt die Kombination von Selenium und Request Sinn.

# Imports für die Tests
import unittest
import HtmlTestRunner

# Standard Import für korrekten Pfad
import os

# Selenium Imports
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

# Current Working Directory auf den Pfad des aktuellen Skripts setzen
os.chdir(os.path.dirname(os.path.abspath(__file__))) 

class HelloCodingTestSuit(unittest.TestCase):
    baseUrl="https://hellocoding.de"
    sitemapUrl="https://www.hellocoding.de/sitemap"

        # Wird vor den Tests aufgerufen
    def setUp(self):
        self.driver = webdriver.Chrome(executable_path='../driver/chromedriver')
        self.driver.maximize_window()

        self.driver.implicitly_wait(5)

    # Homepage laden
    def test_loading_website(self):        
        self.driver.get(self.baseUrl)        
        # Teste, ob die korrekte Webseite geladen wurde
        self.assertIn("Übersicht, Programmieren Lernen | HelloCoding",self.driver.title)

    # Teste, ob eine Seite einen 500er Fehler hat
    def test_no_error(self):
        self.driver.get(self.sitemapUrl) 
        failed = True

        wait = WebDriverWait(self.driver, 5)
        # Alle Links aus der Sitemap holen
        try:
            element = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#contentTOC')))
            elements = element.find_elements_by_css_selector('.sitemap-widget ul li a')
        except TimeoutException as error:
            pass

        if len(elements) != 0: 
            links = []
            for a in elements:
                links.append(a.get_attribute('href'))

            # Jeden Link auf den 500er Fehler prüfen
            for link in links:
                if link != "":
                    self.driver.get(link)

                    if "500er - Server Fehler" in self.driver.page_source:
                        self.fail(link + " - 500er Fehler")
        else:
            self.fail('Keine Elemente Gefunden')

    # Abschließende Funktion am Ende aller Test Cases
    def tearDown(self):
        # Browser schließen
        self.driver.quit()

if __name__ == '__main__':
    unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(output='./report'))

Wenn wir nun unsere Unittest ausführen, erhalten wir eine HTML Datei, die folgende Ausgabe enthalten kann. Dort sehen wir, dass eine Seite einen 500er Fehler zurückgegeben hat und auch direkt, welche Seite das war. Aber das Laden der Startseite war schon einmal erfolgreich.

Unittest Result
Unittest Result

So könnte man noch weitere Fälle abdecken, wie das Versenden von Kommentaren und Formularen, oder das Verwenden der Suche.

Du könntest zum Beispiel diesen Test über eine Cronjob alle 3 Tage auf einem Server ausführen lassen und dir das Ergebnis per Mail zusenden lassen. So hast du dann ein Monitoring das angibt, ob deine Webseite korrekt funktioniert, oder sich ein Fehler eingschlichen hat.

Praktische Anwendungsbeispiele

Zuletzt will ich dir 3 Praxisbeispiele an die Hand geben, die ich für dich geschrieben habe, mit denen du folgende Dinge tun kannst:

  • Die Ergebnisse einer Google Suche auslesen
  • Ein Formular ausfüllen und absenden
  • Screenshots einer Webseite erstellen

Falls du danach noch Fragen haben solltest, kannst du die Kommentarsektion am Ende des Artikels nutzen, oder auf unserem Discord Server vorbeischauen!

Google Suche auslesen

Als Erstes wollen wir die Google Suche steuern und dort die Titel der 10 obersten Suchergebnissen auslesen.

Das Skript habe ich auf den Safari-Browser ausgelegt, kann aber ebenso gut mit Chrome und Firefox verwendet werden.

Eine Besonderheit ist hier, dass wir die Methode switch_to.frame verwenden, um auch einen iFrame Steuern zu können, da dann der driver auf den iFrame zeigt. Diese ist notwendig da das Fenster mit den Datenschutzeinstellungen bei Google ein iFrame ist. Mit switch_to.parent_frame können wir wieder unser eigentliches Fenster steuern.

Am Ende erhalten wir mit getAttribute('innerHTML') die Titel der verschiedenen Suchergebnisse und erstellen daraus eine entsprechende Liste. Diese könnte man zum Beispiel für ein W-Fragen Tool verwenden.

Ein letzter Hinweis: die Google Webseite ändert sich regelmäßig, also kann es gut sein, dass dieses Beispiel schon nicht mehr funktioniert, wenn du es anwendest. Dieses Beispiel soll dir nur einen Eindruck davon geben, wie ein Skript zur Browser-Automatisierung für so einen Fall grundsätzlich aussehen könnte.

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from time import sleep

driver = webdriver.Safari()

# Wait Instanz erstellen
wait = WebDriverWait(driver, 5)

# Fenster maximieren
driver.maximize_window()

driver.get("https://www.google.de")

sleep(5)

# iFrame mit der Datenschutz Einstellung suchen
privacyiFrame = driver.find_elements_by_css_selector('iframe')

# Wenn der iFrame da ist, auf diesen wechseln und den "Zustimmen" Button drücken.
if privacyiFrame:
    driver.switch_to.frame(0)
    driver.find_element_by_id('introAgreeButton').click()

# Zurück zum Eltern Fenster wechseln
driver.switch_to.parent_frame()

# Das Suchfeld finden. Es hat immer den Namen q - Das ist bei einigen Seiten der Fall, zum Beispiel auch bei Wordpress Seiten. 
element = driver.find_element_by_name('q')

# Suchbegriff eingeben
element.send_keys('Google with Safari Automatisation')

# Suche absenden
element.send_keys(Keys.RETURN)

# Variable für den Fall, das keine Suchergebnisse gefunden werden
failed = False

# Prüfen, ob das Element mit den Suchergebnissen vorhanden ist. Falls ja, kann mit den einzelnen Ergebnissen fortgefahren werden.
try: 
   element = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#search')))
   search_results = element.find_elements_by_css_selector('.g')
except TimeoutException as error:
    failed = True

if failed != True:

    titles = []

        # Über die Suchergebnisse iterieren
    for search_item in search_results:
        try:
            item = WebDriverWait(search_item, 5) 

                        # Titel Element abrufen
            title_elm = item.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'h3 > span')))

                        # Text aus dem Titel nehmen
            titles.append(title_elm.get_attribute('innerHTML'))
        except TimeoutException as error:
            pass

driver.quit()

print(titles)

Formulare ausfüllen

Als zweites Beispiel habe ich mir ausgesucht, automatisiert Kontaktformulare auszufüllen. Als Beispiel habe ich dafür eine JSON Datei mit den Werten angelegt siehe nachfolgende JSON Datei. Falls du mehr dazu wissen willst, wie du mit JSON in Python arbeiten kannst, dann schau dir diesen Artikel an.

Die Beispiel JSON Datei ist wie folgt aufgebaut: Es gibt ein Array mit mehreren Objekten, in denen sich wiederum Zuordnungen zu unseren 4 verschiedenen Felder im Kontaktformular befinden, die wir ausfüllen können.

[
    {
        "name": "Hans",
        "thema": "Python CSV Erstellen",
        "discord": "hans#0000",
        "email": "hans@hellocoding.de"
    },
    {
        "name": "Max",
        "thema": "Python JSON Erstellen",
        "discord": "max#0000",
        "email": "max@hellocoding.de"
    }
]

Das Formular, das wir absenden wollen, ist das Folgende: https://hellocoding.de/artikel-idee. Dort kannst du mir Themenvorschläge für neue Artikel senden.

Das eigentliche Ausfüllen des Formulars ist über die send_keys Methode möglich. Abschließend prüfen wir, ob das Element mit der Klasse form-message-success sichtbar ist. Da dieses Element das CSS Attribut display: block erhält, wenn das Formular erfolgreich versendet wurde, ist es ein guter Indikator dafür, dass das Formular abgeschickt wurde. Mit einer Schleife können wir das Formular auch mehrmals abschicken.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

import os
import json
from time import sleep

# Current Working Directory auf den Pfad des aktuellen Skripts setzen
os.chdir(os.path.dirname(os.path.abspath(__file__))) 

driver = webdriver.Firefox(executable_path='../driver/geckodriver')

# Fenster maximieren
driver.maximize_window()

url = "https://hellocoding.de/artikel-idee"

# Daten aus dem JSON File laden
with open('fill.json','r') as file:
    obj = json.loads(file.read())

# WebDriverWait Instanz, um später auf das erfolgreiche Versenden warten zu können. 
wait = WebDriverWait(driver, 5)

# Das Formular mehrmals ausfüllen, mit allen vordefinierten Werten aus dem JSON
for data in obj:
        # URL abrufen
    driver.get(url)

        # Feld Name mit entsprechenden Werten aus dem JSON ausfüllen
    driver.find_element_by_name('Name').send_keys(data['name'])

    driver.find_element_by_name('Thema').send_keys(data['thema'])

    driver.find_element_by_name('Discord Name').send_keys(data['discord'])

    driver.find_element_by_name('sender-mail').send_keys(data['email'])

    driver.find_element_by_name('privacy').click()

    driver.find_element_by_name('anfrage-senden').click()

        # Prüfen, ob das Senden rrfolgreich war. Falls nicht tue nichts und versuche den nächsten zu versenden.
    try: 
        wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, '.form-message-success')))
    except TimeoutException as error:
        pass

driver.quit()

Screenshots erstellen

Für dieses Beispiel habe ich mir etwas Einfaches überlegt: Wir holen uns aus der Seite "Sitemap" von HelloCoding.de alle Links heraus, die dort definiert sind, rufen alle Seiten aus der Sitemap nacheinander ab und speichern jeweils einen Screenshot. Damit wir die Namen der Bilder leserlich speichern können, verwende ich das Modul slugify (https://pypi.org/project/python-slugify/). Das sorgt für saubere Dateinamen.

Mit der Methode get_attribute holen wir uns das href-Attribut, also den Link, den wir abrufen wollen. Über diese Liste an Links wird nun iteriert, für jeden Link ein Screenshot erstellt und wir erhalten einen Ordner mit etlichen Screenshots.

Wofür könnte das Interessant sein? Du kannst zum Beispiel prüfen, ob auf allen Seiten das Design stimmt. Anstatt jede Seite aufzurufen, musst du nur noch durch einen Ordner von Screenshots gehen.

from selenium import webdriver
import os
from time import sleep
from slugify import slugify

# Current Working Directory auf den Pfad des aktuellen Skripts setzen
os.chdir(os.path.dirname(os.path.abspath(__file__))) 
driver = webdriver.Chrome(executable_path='../driver/chromedriver')

# Fenster maximieren 
driver.maximize_window()

# Sitemap abrufen
driver.get("https://www.hellocoding.de/sitemap")

sleep(4)

# Alle Links auslesen
elements = driver.find_elements_by_css_selector('#contentTOC .sitemap-widget ul li a')

# Eine Liste mit allen Links erstellen
links = []
for a in elements:
    links.append(a.get_attribute('href'))

# Alle Webseiten abrufen und jeweils einen Screenshot erstellen.
for link in links:
    driver.get(link)
    sleep(2)
    driver.save_screenshot('../screenshot-example/' + slugify(link) + '.png')

driver.quit()

Abschließende Worte

Selenium ist eine mächtige API um den Browser fernzusteuern und in Kombination mit Python hast du eine syntaktisch einfache Sprache an der Hand, mit der du deine Browser-Tests schreiben kannst. Natürlich kannst du auch Selenium mit Python Unittest kombinieren!

Dir sind keine Grenzen in dem gesetzt, was du mit dem Paket Selenium umsetzen kannst. Bedenke nur immer, dich an die Vorgaben der Webseiten-Betreiber zu halten, falls du Selenium auf fremden Webseiten einsetzt!

Teilen:
Bildquelle - Vielen Dank an die Ersteller*innen f├╝r dieses Bild

Kommentare zum Artikel

Es sind noch keine Kommentare vorhanden? Sei der/die Erste und verfasse einen Kommentar zum Artikel "Python Browser Automatisierung mit Selenium"!

Kommentar Schreiben

Verwante Beitr├Ąge

keyboard_arrow_up
Home Men├╝ Suche
close