Anwendungen Teil I
- Passwörter kodiert speichern
- Auswahlmenü erstellen
- Texte lesen und analysieren
- HTML-Seite analysieren
- Ausnahmebehandlung (Wiederholung)
Passwörter kodiert speichern
Die Kryptografie ist beim Verschlüsseln auf exklusive Verfahren angewiesen, die möglichst zeitintensive "Brute-Force-Methoden"
benötigen, um verschlüsselte Texte zu entschlüsseln. Hier sollen Passwörter nach einem einfachen Verfahren entschlüsselt werden, wobei das Passwort selbst
der Schlüssel ist, sodass ein Entschlüsseln nicht möglich ist. Nehmen wir als Beispiel das gewählte Passwort 15ghM;OP
, was relativ sicher ist, allerdings etwas kurz. Eine Brute-Force-Methode
käme sehr schnell zum Ziel. Die Verschlüsselung soll jetzt wie folgt erfolgen:
PW = "15ghM;OP" for i in PW: print(ord(i))Das Passwort ist acht Zeichen lang und jedes Zeichen soll um diese Zahl einschließlich des laufenden Indexes verschoben werden. Das erste Zeichen, hier die 1, soll durch
chr(ord(i)+8+1)
(49+9) nach rechts verschoben werden. Wobei hier vorerst auf ein Rotieren verzichtet werden soll,
falls die Zahl größer als $2^{16}$ wird. Die 1 wird somit zum Doppelpunkt . Die anderen Zeichen werden entsprechend kodiert. Für das
zweite Zeichen ist es chr(ord(i)+8+2)
(50+10), was dem L entspricht.
PW = "12ghM;OP" print() print(PW + " -> ",end="") l_PW = len(PW) f = 24 // l_PW PW *= f j = 0 for i in PW: j += 1 print(chr(ord(i)+len(PW)+j),end="") print()
Vergleicht man das erzeugte verschlüsselte Passwort mit einem ähnlichen, beispielsweise PW = "22ghM;OP"
, so fällt auf,
dass das verschlüsselte Passwort ähnlich ist. Somit wäre es ein einfaches, das Prinzip zu entschlüsseln und ein "Rückwärtsverfahren"
zu entwickeln.
PW = ["12ghM;OP", "22ghM;OP", "22ghM;OPP" ] print() for pw_ in PW: print(pw_ + " -> ",end="") l_PW = len(pw_) f = 24 // l_PW + 1 pw_ *= f j = 0 for i in pw_[:24]: j += 1 print(chr(ord(i)+len(pw_)+j),end="") print()
Verwendet man dagegen eine sogenannte Hashfunktion, die normalerweise eine große Datenmenge auf eine kleinere abbildet, aber dennoch hier benutzt werden kann, denn sie ergibt eine vollständig andere Ausgabe, sobald auch nur ein Zeichen verändert wird:
from hashlib import blake2b PW = ["12ghM;OP", "22ghM;OP", "22ghM;OPP", "ä", "ö"] print() for pw_ in PW: pw_decode = blake2b(bytes(pw_,'utf-8'),digest_size=24).hexdigest() print("{:<10} -> {}".format(pw_, pw_decode)) print()
Voraussetzung ist, dass die Zeichenkette in Byteform vorliegt, was hier mit der Funktion bytes()
einfach erfolgen
kann.
Auswahlmenü
Folgendes Python-Programm soll so erweitert werden, dass der Anwender aus den vorgegebenen fünf Varianten durch Eingabe von 1,2,...,5 wählt, oder alternativ durch Einagbe der Parameter für die komplexe Konstante c=a+i b oder durch Eingabe von q für quit alles beendet.
import numpy as np import matplotlib.pyplot as plt def julia_set(c=-0.4 + 0.6j, height=800, width=1000, x=0, y=0, zoom=1, max_iterations=100): # To make navigation easier we calculate these values x_width = 1.5 y_height = 1.5*height/width x_from = x - x_width/zoom x_to = x + x_width/zoom y_from = y - y_height/zoom y_to = y + y_height/zoom # Here the actual algorithm starts x = np.linspace(x_from, x_to, width).reshape((1, width)) y = np.linspace(y_from, y_to, height).reshape((height, 1)) z = x + 1j * y # Initialize z to all zero c = np.full(z.shape, c) # To keep track in which iteration the point diverged div_time = np.zeros(z.shape, dtype=int) # To keep track on which points did not converge so far m = np.full(c.shape, True, dtype=bool) for i in range(max_iterations): z[m] = z[m]**2 + c[m] m[np.abs(z) > 2] = False div_time[m] = i return div_time plt.imshow(julia_set(), cmap='magma') #plt.imshow(julia_set(x=0.125, y=0.125, zoom=10), cmap='magma') # plt.imshow(julia_set(c=-0.8j), cmap='magma') #plt.imshow(julia_set(c=-0.8+0.156j, max_iterations=512), cmap='magma') #plt.imshow(julia_set(c=-0.7269 + 0.1889j, max_iterations=256), cmap='magma') plt.show()
Eine Liste aller colormaps erhält man mit folgendem Program:
import matplotlib as mpl import matplotlib.pyplot as plt def plot_all_cmaps(): N_ROWS, N_COLS = 8, 7 # 13, 13 <-- for all in one figure HEIGHT, WIDTH = 7, 14 cmap_ids = plt.colormaps() n_cmaps = len(cmap_ids) print(f'mpl version: {mpl.__version__},\nnumber of cmaps: {n_cmaps}') index = 0 while index < n_cmaps: fig, axes = plt.subplots(N_ROWS, N_COLS, figsize=(WIDTH, HEIGHT)) for row in range(N_ROWS): for col in range(N_COLS): ax = axes[row, col] cmap_id = cmap_ids[index] cmap = plt.get_cmap(cmap_id) mpl.colorbar.ColorbarBase(ax, cmap=cmap, orientation='horizontal') ax.set_title(f"'{cmap_id}', {index}", fontsize=8) ax.tick_params(left=False, right=False, labelleft=False, labelbottom=False, bottom=False) last_iteration = index == n_cmaps-1 if (row==N_ROWS-1 and col==N_COLS-1) or last_iteration: plt.tight_layout() #plt.savefig('colormaps'+str(index)+'.png') plt.show() if last_iteration: return index += 1 plot_all_cmaps()
Texte lesen und analysieren
Textanalyse
Das automatisierte Trennen von Wörtern ist immer noch eine Herausforderung. Da es keine Datenbanken gibt, die alle Wörter mit richtigen Trennungen enthält, muss mit wahrscheinlichkeitsbasierten Trennmustern (Pattern) gearbeitet werden. Dazu benötigt man erst einmal Wortlisten und zwar möglichst große, beispielsweise mindestens 1 GByte. Die werden entsprechend der Häufigkeit der Wörter sortiert und dann die häufigsten 500.000 Wörter per Hand getrennt. Das muss sein, damit man sicher ist, dass die Trennungen korrekt sind. Aus diesen Wörter ermitteln man dann Muster, die angeben, wie groß die Wahrscheinlichkeit ist, dass beispielsweise zwischen den Zeichen "ac" und "he" getrennt werden kann. In diesem Fall wäre es für die deutsche Sprache sehr unwahrscheinlich. Dieses Verfahren wurde bereits 1980 für das Satzprogramm $\TeX$ entwickelt und die nach dem Prinzip bestimmten entsprechenden Trennmuster noch heute benutzt.
Das folgende Programm ermittelt die Worthäufigkeit der Datei maturin.txt (maturin.zip), einem englischsprachigen Text. Es werden dabei einige lokale Dateien erzeugt:
Das Program lautet:
from collections import Counter # pip3 install collections # Zählt Worthäufigkeit def count_words(text, output=False): skips = [".", ",", ":", ";", "'", '"',")","(","#", "*", "“", "”", "_", "!", "--", "[", "]", "/", "&", "}", "´", "`", "'", "’", "‘", "£", "?" ] for ch in skips: text = text.replace(ch, " ") # alles durch Leerzeichen ersetzen word_counts = {} alpha_counts = {} max_alpha = 1 max_alpha_view = 100 # nur prozentuale Ausgabe for word in text.split(" "): # alles zwischen zwei Leerzeichen ist ein Wort if word in word_counts: word_counts[word]+= 1 # Worthäufigkeit else: word_counts[word]= 1 for c in word.upper(): # keine Groß-/Kleinschreibung if c in alpha_counts: # Zeichenhäufigkeit alpha_counts[c]+= 1 if alpha_counts[c] > max_alpha: max_alpha += 1 else: alpha_counts[c]= 1 if output: out = open("Wortliste_alph.txt","w") sortedList = dict(sorted(word_counts.items()))# nur für Python Version > 3.6 for key, value in sortedList.items(): out.write("{:14} {:.0f}".format(key, value)) out.write("\n") out.close() out = open("Wortliste_anzahl.txt","w") sorted_values = sorted(word_counts, key=word_counts.get, reverse=True) for r in sorted_values: out.write("{:14} {:.0f}".format(r, word_counts[r])) out.write("\n") out.close() out = open("Alphaliste_alpha.txt","w") sortedList = dict(sorted(alpha_counts.items()))# nur für Python Version > 3.6 for key, value in sortedList.items(): out.write("{:14} {:.0f}".format(key, value)) out.write("\n") out.close() rel_max_alpha = max_alpha_view / max_alpha # relative value for key, value in sortedList.items(): for i in range(0,int(value*rel_max_alpha)+1): print(key,end="") print() out = open("Alphaliste_anzahl.txt","w") sorted_values = sorted(alpha_counts, key=alpha_counts.get, reverse=True)# nur für Python Version > 3.6 for r in sorted_values: out.write("{:3} {:.0f}".format(r, alpha_counts[r])) out.write("\n") out.close() for r in sorted_values: for i in range(0,int(alpha_counts[r]*rel_max_alpha)+1): print(r,end="") print() return word_counts # Worthäufigkeit mit dem Zähler des Moduls collections bestimmen def count_words_fast(text, output=False): text = text.lower() skips = [".", ", ", ":", ";", "'", '"'] for ch in skips: text = text.replace(ch, "") word_counts = Counter(text.split(" ")) return word_counts #read a book and return it as a string def read_book(title_path): with open(title_path, "r", encoding ="utf8") as current_file: text = current_file.read() text = text.replace("\n", " ").replace("\r", " ") return text # word_counts = count_words_fast(text) def word_stats(word_counts): num_unique = len(word_counts) counts = word_counts.values() return (num_unique, counts) text = read_book("maturin.txt") #print(text) #word_counts = count_words_fast(text,output=False) word_counts = count_words(text,output=True) (num_unique, counts) = word_stats(word_counts) print("{} verschiedene Wörter bei insgesamt {} Wörtern.".format(num_unique, sum(counts)))
Daten aus HTML-Seiten extrahieren
Das Institut für Meteorologie der Freien Universität Berlin liefert alle 10 Minuten aktuelle Wetterdaten: https://www.geo.fu-berlin.de/met/. Diese erscheinen in einem sogenannten Frame; sie werden erst im Moment des Aufrufens der Webseite geladen. Die ensprechende URL ist BerlinWetter; das PHP-Programm generiert die HTML-Seite "on-the-fly!.
Von dieser Webseite lassen sich die Daten extrahieren, um sie für eigene Bedürfnisse weiter verwenden zu können:
import os from datetime import date from urllib.request import urlopen from bs4 import BeautifulSoup import ssl ssl._create_default_https_context = ssl._create_unverified_context def holeHTMLdaten(url,ausgabe=False): htmlfile = urlopen(url) htmltext = htmlfile.read().decode('utf-8') if ausgabe: print("Der HTML-code der Seite: \n", htmltext) htmlObject = BeautifulSoup(htmltext, 'html.parser') text = htmlObject.get_text()# html nach text wandeln heute = date.today().strftime("%Y-%m-%d") # print(text,heute) datenzeile = text[text.find(heute):] # bis zum Rest kopieren return datenzeile.replace("\n"," ") # Beispieldatensatz #2022-03-06 18:10:00 MEZ Temperatur1.3 °C Luftfeuchte61 % #WindrichtungNNW ... 342 ° Geschwindigkeit2.5 m/s Windspitze4.1 m/s #Luftdruck NN1025.5 hPa def holeWerte(daten, ausgabe=False): print(daten)# nur zur Kontrolle! Werte = {} Werte["Datum"] = daten[0:daten.find("Temperatur")]# von vorne bis Wort "Temperatur" erscheint Werte["Temperatur"] = daten[daten.find("Temperatur"):daten.find("Luftfeuchte")].split() Werte["Temperatur"][0] = Werte["Temperatur"][0].replace("Temperatur","") Werte["Luftfeuchte"] = daten[daten.find("Luftfeuchte"):daten.find("Windrichtung")].split() Werte["Luftfeuchte"][0] = Werte["Luftfeuchte"][0].replace("Luftfeuchte","") Werte["Windrichtung"] = daten[daten.find("Windrichtung"):daten.find("Geschwindigkeit")].split() Werte["Windrichtung"][0] = Werte["Windrichtung"][0].replace("Windrichtung","") Werte["Geschwindigkeit"] = daten[daten.find("Geschwindigkeit"):daten.find("Windspitze")].split() Werte["Geschwindigkeit"][0] = Werte["Geschwindigkeit"][0].replace("Geschwindigkeit","") Werte["Windspitze"] = daten[daten.find("Windspitze"):daten.find("Luftdruck")].split() Werte["Windspitze"][0] = Werte["Windspitze"][0].replace("Windspitze","") Werte["Luftdruck"] = daten[daten.find("Luftdruck"):].split() Werte["Luftdruck"][0] = Werte["Luftdruck"][0].replace("Luftdruck","") if ausgabe: print() print("Ermittelte Daten aus der Webseite:") for w in Werte: print(w,Werte[w]) return Werte datenZeile = holeHTMLdaten('https://page.met.fu-berlin.de/BerlinWetter',ausgabe=False) Werte = holeWerte(datenZeile,ausgabe=True)
Benötigt werden die Python-Module:
pip3 install ...
installiert werden oder direkt aus dem Python-Code heraus
mit:
import subprocess import sys def install(package): subprocess.check_call([sys.executable, "-m", "pip", "install", package]) # Beispiel: install("bs4")
Alternative Lösung mit html2text
Das Modul vereinfacht die Umwandlung einer html-Seite in reinen Text:
import html2text as ht import requests def holeDaten(url, Ausgabe=False): html = requests.get(url) text = ht.html2text(html.text) # html nach text wandeln if Ausgabe: print(text) # text ist _eine_ Zeichenkette (string) z_text = text.split("\n") # in Zeilen aufteilen (list) z_text.pop(1)# jetzt nur noch Daten, Zeile mit --- ist "entsorgt" worden if Ausgabe: for z in z_text: print(z) # nur für Kontrolle return z_text data = holeDaten('https://page.met.fu-berlin.de/BerlinWetter',True)
Weitere Informationen zu "komma-separierten" Datensätzen findet man hier.
Nächste Einheit: 07 Grafische Ausgaben