Python Webseiten Prüfung Erweitern (Teil 2)
Im zweiten Teil unserer “Python Webseiten Prüfung” geht es um die Erweiterung des Basis-Scripts mit einigen nützlichen Funktionen. Weiterhin gilt natürlich alles aus dem ersten Teil. Also die Abfrage, ob eine Seite erreichbar ist oder einen Fehler zurückgibt (40x/50x).
DarkWolfCave.de
Bisher konnten wir das Script direkt mit einem Parameter (einer URL) aufrufen und uns das Ergebnis anzeigen lassen.
Die Basis
Falls du dir erst nochmal die Grundversion anschauen und nachbauen möchtest, findest du in diesem Artikel alles weitere dazu. Dort erkläre ich dann auch genau was das Script bzw. die einzelnen Schritte machen.
Weiterhin wird hier auf eine eigene Python-Umgebung verwiesen und erklärt wie man diese einrichtet und startet.
Du wirst hier einen groben Überblick finden.
Allerdings biete ich dir auch noch etwas mehr Support an:
- Du benötigst persönlichen Support
- Du möchtest von Beginn an Unterstützung bei deinem Projekt
- Du möchtest ein hier vorgestelltes Plugin durch mich installieren und einrichten lassen
- Du würdest gerne ein von mir erstelltes Script etwas mehr an deine Bedürfnisse anpassen
Für diese Punkte und noch einiges mehr habe ich einen limitierten
VIP-Patreon Tarif
eingerichtet. Falls er dir dort zurzeit nicht angeboten wird,
kontaktiere mich bitte über Discord und wir finden eine Lösung!
Ziel / Beschreibung des Python Webseiten Prüfung Scripts
Wir werden unser Basis-Script der Webseiten Prüfung etwas umbauen und erweitern.
Danach kannst du das Script starten und es prüft in einem Rutsch unterschiedliche Webseiten, die in einer Datei hinterlegt sind.
Das Ergebnis wird dann auch in eine Textdatei gespeichert. So kannst du jederzeit prüfen und nachvollziehen, wann eine Seite mal nicht erreichbar war.
Was kann ich hier lernen?
- Wie man eine Liste erstellt und Einträge hinzufügt
- Was eine For-Schleife ist
- Wie man eine Text-Datei einließt und diese Zeile für Zeile verarbeitet
- Wie man ein Ergebnis in eine Datei speichern kann
- Wie man bei “try: except:” den komplette Abbruch des Python Scripts umgehen kann
Das fertige Python Script
#!/usr/bin/python3 # -*- coding: utf-8 -*- #***************************** #*****Name: check_site.py***** #*******DarkWolfCave.de******* #*****TEIL 2 - Erweiterung**** #***************************** import requests import pprint def check_site(): headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} #!! da wir eine Liste haben wollen - sagen wird das hier rstat = [] #!! #!! Hier starten wir eine for-Schleife mit den Ergebnissen aus unserer URL-Text Datei for zeile in url_laden(): try: rstat.append("-"*80) #einfach um einen optischen Trenner sehen zu können r = requests.get(zeile,headers=headers,timeout=3) if r.status_code != 200: rstat.append(zeile) #es wurde .append genutzt um unsere Liste zu erweitern rstat.append(f"Seite nicht erreichbar, Zugriff verweigert oder LogIn notwendig. StatusCode: {r.status_code}") #mit dem f können wir Variablen mit übergeben: {INHALT VBARIABLE} else: rstat.append(zeile) rstat.append(f"Seite erreichbar. StatusCode: {r.status_code}") except requests.exceptions.RequestException as e: rstat.append(zeile) rstat.append(f"Timeout? Fehlermeldung: {SystemExit(e)}") continue #wir wollen bei einem Fehler nicht komplett abbrechen (was bei einem return passieren würde), sondern lediglich den Fehler mit ausgeben und dann die nächste URL prüfen write_results("X:\Tutorials\Python\Ergebnis.txt", rstat, flag="a") #hier rufen wir unsere Funktion auf und schreiben die Ergebnisse in Ergebnis.txt return rstat #das ist unser neues return (gehört zu der FOR Schleife. Immer auf das richtige Einrücken achten.) #!! #!!! Neue Funktion zum einlesen der URLs def url_laden(): #Einlesen URL aus einer Datei URL_datei = r"X:\Tutorials\Python\urls.txt" with open(URL_datei, encoding="utf-8") as file: results = file.read().splitlines() return results #!!! def write_results(target, data, flag="w", encoding="utf-8"): with open(target, flag, encoding=encoding) as file: file.write(pprint.pformat(data)) file.write("\n") #file.flush() file.close() print(check_site())
Wir fangen an – und starten unser Environment (env)
Wie auch in dem ersten Teil beziehe ich mich hier auf ein Linux System und einer eigenen Python Umgebung. Du kannst alles in deiner eigenen env starten oder einfach in einem Ordner neu anlegen. Falls du nachlesen möchtest was genau ein env ist bzw. wie du so etwas erstellen kannst, schau einfach nochmal in diesem Artikel nach. Ansonsten läuft das Script überall dort wo Python3 installiert ist. Auch unter Windows und sogar im VisualStudioCode.
Aber wie gesagt, die Schritte hier beziehen sich auf eine Linux-Umgebung.
Um dein env zu aktivieren, geh in deinen Ordner, wo du sie eingerichtet hast und starte diese mit folgendem Befehl:source ./DEINE_ENV/bin/activate
Jetzt werden alle pip-Installationen nur noch in dieser env und nicht im gesamten System installiert.
Achte also immer darauf ob du dich in deiner aktivierten env befindest. Dies erkennst du unter anderem an dem prompt, welcher den Namen deiner Umgebung enthält.
Kurze Einführung in das Python Webseiten Prüfung Script
Wenn du dir das Script aus dem ersten Teil nochmal anschaust, wirst du feststellen, dass wir die URL bei dem Aufrufen des Scripts mit übergeben hatten.
Und so wie es bisher vorlag, konnte man genau eine(1) Webseite prüfen.
Dies macht in der Praxis ja nicht so wahnsinnig viel Sinn, und ich hatte mal gesagt dass ich gerne an nützlichen und praktischen Beispielen etwas zeige.
Klar ist das immer persönliches Empfinden… aber bauen wir das Script doch einfach mal ein wenig um und schauen es uns dann an.
Vielleicht findest du es danach auch etwas nützlicher oder konntest zumindest etwas daraus mitnehmen.
Wie fängt man an, ein Python Script umzubauen?
Das ist eine berechtigte Frage. Es gibt ganz bestimmt irgendwo ein Regelwerk / ein Buch, was dies ausführlich behandelt und aufzeigt, wie man Regelkonform vorgeht. Da man sich aber nicht immer und unbedingt überall einlesen muss, ignorieren wir das einfach mal…
Also überlegen wir uns, was unser neues Script denn so machen soll.
– es soll eine Datei einlesen und die URLs prüfen
– es soll die Ergebnisse in eine Datei / Textfile schreiben
Wie sooft gibt es natürlich mehrere Wege die zum Ziel führen. Und ich persönlich finde, wenn das Ergebnis stimmt ist es nicht falsch 😉
Ich nehme dich jetzt einfach mal mit, und zeige dir Schritt für Schritt, wie ich das vorhandene Script aus dem ersten Teil umbauen werde.
Der erste Schritt zum Ziel
Die einzelnen Änderungen sind im Quelltext mit #!!! und #!!! umschlossen. So kannst du diese direkt erkennen.
Im ersten Schritt werde ich noch gar nicht das gesamte Script umbauen, sondern mich lediglich mit einer neuen Funktion beschäftigen. Zur Erinnerung: unser Script soll die URLs aus einer Datei einlesen, verarbeiten und das Ergebnis dann speichern. Also bauen wir uns so etwas und fangen mit der Ladefunktion an:
#!/usr/bin/python3 # -*- coding: utf-8 -*- #***************************** #*****Name: check_site.py***** #*******DarkWolfCave.de******* #***************************** import requests def check_site(url): headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} try: r = requests.get(url,headers=headers,timeout=3) if r.status_code != 200: rstat="Seite nicht erreichbar, Zugriff verweigert oder LogIn notwendig. StatusCode:" return rstat, r.status_code else: rstat='Seite erreichbar. StatusCode:' return rstat, r.status_code except requests.exceptions.RequestException as e: return 'Timeout? Fehlermeldung: ',SystemExit(e) #!!! Neue Funktion zum einlesen der URLs def url_laden(): #Einlesen URL aus einer Datei URL_datei = r"X:\Tutorials\Python\urls.txt" with open(URL_datei, encoding="utf-8") as file: results = file.read().splitlines() return results #!!! text,code = check_site(sys.argv[1]) print (text,code)
Was haben wir gemacht?
Wie du sicher richtig erkannt hast, eine neue Funktion mit dem Namen url_laden erzeugt.
In dieser greifen wir auf eine urls.txt Datei zu, öffnen diese und lesen Zeile für Zeile den Inhalt ein ( file.read().splitlines() )
Natürlich musst du bei dir diese urls.txt Datei erst noch erstellen und evtl. den Pfad dorthin anpassen.
Meine urls.txt – Datei hat folgenden Inhalt:
Was passiert jetzt?
Wenn du das Script so startest wird nichts neues passieren. Da wir ja lediglich eine Funktion erstellt, aber noch nirgends aufgerufen haben.
Damit wir das ein wenig testen können, kommentieren wir den alten Aufruf erst einmal mit einer Raute ( # ) aus und rufen dafür unsere neue Funktion auf.:
#text,code = check_site(sys.argv[1]) #print (text,code) print(url_laden())
Starte das Script erneut und es sollte eine Ausgabe der eingelesenen URLs erscheinen. Bei mir z.B. so:['https://darkwolfcave.de', 'https://www.darkwolfcave.de', 'https://www.google.de', 'https://gibtesnicht234.de'
, 'https://testing.darkwolfcave.de'
]
Durch den Aufruf der Funktion mit url_laden() hat Python die Schritte dort verarbeitet und das print davor gibt uns den Inhalt auf den Bildschirm aus.
Natürlich erfolgt hier noch keine Prüfung der URLs – wir haben diese lediglich aus einer Datei eingelesen und können sie jetzt weiter verarbeiten.
Die geladenen URLs sollen auch geprüft werden
Damit unsere URLs aus der Datei auch geprüft werden können, müssen wir diese an unsere check_site() Funktion übergeben.
Dazu sind allerdings einige Änderungen gegenüber der ursprünglichen Version notwendig. Zuerst der gesamte umgebaute Code:
#!/usr/bin/python3 # -*- coding: utf-8 -*- #***************************** #*****Name: check_site.py***** #*******DarkWolfCave.de******* #***************************** import requests #!! Keine Parameterangabe mehr in der Funktion def check_site(): headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} #!! da wir eine Liste haben wollen - sagen wird das hier rstat = [] #!! #!! Hier starten wir eine for-Schleife mit den Ergebnissen aus unserer URL-Text Datei for zeile in url_laden(): try: rstat.append("-"*80) #einfach um einen optischen Trenner sehen zu können r = requests.get(zeile,headers=headers,timeout=3) if r.status_code != 200: rstat.append(zeile) #es wurde .append genutzt um unsere Liste zu erweitern rstat.append(f"Seite nicht erreichbar, Zugriff verweigert oder LogIn notwendig. StatusCode: {r.status_code}") #mit dem f können wir Variablen mit übergeben: {INHALT VBARIABLE} # return rstat, r.status_code !! Hier darf kein return mehr stehen, da jedes return den Abbruch bedeutet und somit die FOR-Schleife unterbrochen werden würde else: rstat.append(zeile) rstat.append(f"Seite erreichbar. StatusCode: {r.status_code}") # return rstat, r.status_code !! Hier darf kein return mehr stehen, da jedes return den Abbruch bedeutet und somit die FOR-Schleife unterbrochen werden würde except requests.exceptions.RequestException as e: rstat.append(zeile) rstat.append(f"Timeout? Fehlermeldung: {SystemExit(e)}") continue #wir wollen bei einem Fehler nicht komplett abbrechen (was bei einem return passieren würde), sondern lediglich den Fehler mit ausgeben und dann die nächste URL prüfen return rstat #das ist unser neues return (gehört zu der FOR Schleife. Immer auf das richtige Einrücken achten.) #!! def url_laden(): #TEST EINGABE UND VERABEITUNG AUS DATEI URL_datei = r"X:\Tutorials\Python\urls.txt" with open(URL_datei, encoding="utf-8") as file: results = file.read().splitlines() return results #text,code = check_site(sys.argv[1]) #text,code = check_site("https://www.darkwolfcave.de") #print (text,code) print(check_site())
Was haben wir gemacht?
Unsere Funktion hatte einen erwarteten Parameter ( check_site(url)
) den wir jetzt nicht mehr benötigen. Natürlich hätte man dies auch anders lösen können, bekanntlich führen mehrere Wege nach Rom 😉
Ich habe mich dazu entschieden, die Funktion check_site()
ohne Daten aufzurufen, und dann in dieser erst die Daten aus der Textdatei einzulesen. Und dies geschieht mit dem Aufruf der neuen Funktion url_laden()
die wir ja etwas weiter oben bereits erstellt hatten.
Damit wir die URLs Zeile für Zeile prüfen können, umschließen wir den Aufruf mit einer for-Schleife ( for zeile in url_laden()
).
Dies bewirkt dass der folgende eingerückte Code solange ausgeführt wird, wie sich ein Ergebnis in unserer Textdatei befindet. In der Variable “zeile” befindet sich immer eine URL mit der wir dann weiter arbeiten können.
Weitere Änderungen werden dir auch bei try: except auffallen. Hier wenden wir kein return mehr an. Denn dies würde direkt das Python Script beenden und alle weiteren Einträge in unserer URL-Datei werden ignoriert. Damit wir so etwas vermeiden können, speichern wir die Ergebnisse, sowie evtl. Fehler, in einer Liste und geben diese mit return am Ende der Schleife zurück.
Was ist: eine for – Schleife
Schauen wir uns die genutzte for-Schleife etwas genauer an. Der Aufbau ist recht simple:
for zeile in url_laden(): try: . . except: . . return rstat #das ist unser neues return (gehört zu der FOR Schleife. Immer auf das richige Einrücken achten.)
Man könnte auch sagen: “mit jedem einzelnen Wert (zeile) aus dem Ergebnis der Funktion url_laden() mach irgendwas bis nichts mehr da ist.”
Unsere for-Schleife ruft die Funktion url_laden() auf. Diese wiederum öffnet unsere urls.txt Datei und ließt den Inhalt ein. Jetzt wird dieser an die for-Schleife komplett übergeben und in ihr Zeile für Zeile abgearbeitet. Somit wird für jede einzelne URL eine Abfrage generiert und das Ergebnis ausgegeben. Die Variable “zeile” enthält demnach immer unsere aktuellste URL aus der Text-Datei.
Generell ist eine Schleife sowas wie eine Kontrollstruktur – mit Anweisungen – die sich für eine bestimmte Anzahl an Durchläufen wiederholen.
Daher nutzt man diese immer dann, wenn man dieselben Abläufe öfters durchgehen will. In unserem Fall also das Prüfen einer URL.
Was ist: eine Liste
Wir nutzen für unsere Ergebnisse innerhalb der FOR-Schleife eine Liste. Diese haben wir bereits zu Anfang als selbige deklariert (rstat = [] ):
#!! da wir eine Liste haben wollen - sagen wird das hier rstat = [] #!!
In diese Liste fügen wir im weiteren Verlauf mit append Werte hinzu. Zum Beispiel mit rstat.append(“-“*80) oder mit rstat.append(zeile).
Die Werte werden immer an das Ende der Liste hinzugefügt und durch ein Komma getrennt.
Ein Beispiel sehe etwa so aus: ['--------------------------------------------------------------------------------', 'https://darkwolfcave.de', 'Seite erreichbar. StatusCode: 200', '---------------------------------']
Das Ergebnis in eine Datei schreiben
Wir haben jetzt bereits ein recht gut funktionierendes Python-Script, welches unterschiedliche URLs aus einer Text-Datei einließt und diese dann auf ihren Status abfragt. Allerdings “sehen” wir diesen bisher nur auf unserem Bildschirm. Schön wäre es doch auch, wenn das Script dieses zusätzlich in eine Datei speichern würde.
Somit hätten wir jederzeit Zugriff darauf und könnten sogar über einen längeren Zeitraum unsere Webseiten auf Erreichbarkeit prüfen.
Also nochmal schnell einen Kaffee holen und auf zum letzten Schritt 🙂
Damit wir es beim speichern und formatieren der Ergebnisse etwas einfacher haben, importieren wir uns eine weitere Bibliothek mit dem Namen: pprint.
Wie gewohnt einfach am Anfang unseres Sourcecodes mit import pprint hinzufügen :
#!/usr/bin/python3 # -*- coding: utf-8 -*- #***************************** #*****Name: check_site.py***** #*******DarkWolfCave.de******* #***************************** import requests import pprint #neu für das spätere speichern in eine Datei
Als nächstes wollen wir ja nicht nur etwas auf unserem Bildschirm ausgeben, sondern etwas in eine Datei schreiben. Die Werte sind aber dieselben. Na?! Schon eine Idee mit welcher Variable(Objekt) wir hier arbeiten könnten?
Schauen wir uns nochmal die for – Schleife an:
for zeile in url_laden(): try: rstat.append("-"*80) #einfach um einen optischen Trenner sehen zu können r = requests.get(zeile,headers=headers,timeout=3) if r.status_code != 200: rstat.append(zeile) #es wurde .append genutzt um unsere Liste zu erweitern rstat.append(f"Seite nicht erreichbar, Zugriff verweigert oder LogIn notwendig. StatusCode: {r.status_code}") #mit dem f können wir Variablen mit übergeben: {INHALT VBARIABLE} # return rstat, r.status_code !! Hier darf kein return mehr stehen, da jedes return den Abbruch bedeutet und somit die FOR-Schleife unterbrochen werden würde else: rstat.append(zeile) rstat.append(f"Seite erreichbar. StatusCode: {r.status_code}") # return rstat, r.status_code !! Hier darf kein return mehr stehen, da jedes return den Abbruch bedeutet und somit die FOR-Schleife unterbrochen werden würde except requests.exceptions.RequestException as e: rstat.append(zeile) rstat.append(f"Timeout? Fehlermeldung: {SystemExit(e)}") continue #wir wollen bei einem Fehler nicht komplett abbrechen (was bei einem return passieren würde), sondern lediglich den Fehler mit ausgeben und dann die nächste URL prüfen return rstat #das ist unser neues return (gehört zu der FOR Schleife. Immer auf das richtige Einrücken achten.) #!!
Die gesamten Ergebnisse werden ja mit dem return rstat zurück und dann auf dem Bildschirm ausgegeben.
Also steht in dieser alles drin, was wir auch in unserer Datei haben wollen.
Somit solltest du, vor dem return, diese Werte an eine neue Funktion übergeben. Ja, diese müssen wir danach natürlich noch erstellen.
Aber jetzt trage erst einmal folgendes vor die Zeile return rstat #das ist unser neues return (gehört zu der FOR Schleife. Immer auf das richtige Einrücken achten.)
Und auch hier: das write_results muss auf derselben Höhe wie das return stehen, also außerhalb der FOR-Schleife.
write_results("X:\Tutorials\Python\Ergebnis.txt", rstat, flag="a") return rstat #das ist unser neues return (gehört zu der FOR Schleife. Immer auf das richtige Einrücken achten.)
Die Pfadangabe zu der Ergebnis.txt musst du natürlich entsprechend anpassen. Und ja… ich weiß… hardvercoden, also fest irgendwo im Quelltext vergeben, macht man nicht unbedingt. Aber hey, wir sind hier noch bei den Grundlagen! 😛
Weiterhin übergeben wir hier noch zwei Parameter mehr. Einmal rstat und flag=”a”.
In rstat haben wir unsere Abfragen zu den URLs und das flag=”a” teilt unserer Funktion mit, dass wir die Datei als append also zum anhängen/erweitern öffnen möchten. Es gäbe ansonsten noch w(rite) und r(ead). Erstes würde die Datei immer überschreiben und letzteres macht bei einer Funktion die etwas speichern soll keinen Sinn.
Kommen wir jetzt zu der neuen Funktion mit dem Namen write_results().
Hier müssen wir definieren was mit den Werten aus rstat passieren soll.
Am besten du fügst diese nach der def url_laden(): Funktion ein:
def write_results(target, data, flag="w", encoding="utf-8"): with open(target, flag, encoding=encoding) as file: file.write(pprint.pformat(data)) file.write("\n") file.close()
Was passiert hier?
def write_results(target, data, flag=”w”, encoding=”utf-8″): | wir übergeben 3 Parameter in diese Funktion. Zuerst unseren Pfad der Datei, diese wird hier in target gespeichert. Dann folgt der Inhalt aus der rstat die in data abgelegt werden. Das flag ist hier gesetzt und bedeutet: Wenn die Funktion ohne flag aufgerufen wird, dann nimm default “w” (write) ansonsten was der Funktion übermittelt wird. In unserem Fall das “a” (append). Zuletzt setzen wir default das encoding auf “utf-8”. Auch hier könnten wir beim Aufrufen des Scripts einen anderen Wert mitteilen. Wenn du mehr über utf-8 und andere Kodierungen lesen möchtest klickst du einfach hier. |
with open(target, flag, encoding=encoding) as file: | jetzt benutzen wir open um eine Datei zu öffnen/erstellen und teilen hier erneut das target (unser Pfad zur Datei), das flag (“a”) und das encoding (utf-8) mit. |
file.write(pprint.pformat(data)) | mit write schreiben wir den Inhalt (data=rstat) in die geöffnete Datei. Für pprint und pformat benötigen wir den import pprint vom Anfang unseres Scripts. |
file.write(“\n”) und file.close() | Damit bei einem erneuten Durchlauf das erste Ergebnis nicht NEBEN dem letzten steht, bauen wir noch einen neuen Absatz ein (“\n” = newLine). Was wir aufmachen, schließen wir auch wieder. Also file.close() |
Wir sind fertig – Fragen?!
Herzlichen Glückwunsch! Du solltest jetzt stolzer Besitzer eines funktionierenden Python-Scripts sein, welches URLs aus einer Datei einließt, diese dann verarbeitet und danach auf dem Bildschirm UND in eine Datei ausgibt!
Ganz oben sieht du nochmal den gesamten Sourcecode an einem Stück. Sollte etwas nicht funktionieren, schaue in Ruhe alles erneut an. Meistens findet man dann auch einen Fehler. Falls du gar nicht weiterkommst, kannst du natürlich auch gerne hier Fragen stellen oder auf Discord vorbeikommen.
Kleine Aufgabe / Anregung
Hast du noch Lust, etwas weiter zu “basteln”?
Dann hätte ich hier eine kleine Aufgabe bzw. Anregung für dich.
Das Python-Script speichert ja bei jedem Aufruf die Ergebnisse in die Textdatei. Also hängt sie dort an.
Jetzt stelle dir einmal vor, du würdest dieses einen Monat lang per Task-Scheduler / crontab laufen lassen.
Meinst du, du hättest dann noch einen Überblick in der Datei? Wäre es nicht viel schöner, wenn beim Aufruf das Datum (und vielleicht die Uhrzeit) mit in die Textdatei geschrieben werden würde?
Vielleicht hast du ja eine Idee, wie du das Datum beim Script-Aufruf mit in die Datei schreiben kannst.
Die Lösung – oder evtl. benötigte Hilfestellungen – kannst du gerne hier oder auf Discord posten – oder dich natürlich einfach nur selbst daran erfreuen 🙂