Mein erstes "sinnvolles Programm" - IP Geo Daten

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
addi
User
Beiträge: 28
Registriert: Donnerstag 29. März 2018, 22:54

Hey Leute, ich beschäftige mich seit dieser Woche mit Python und ist sozusagen meine erste Sprache. Nun habe ich ein kleines Tool entwickelt, mit den man Geo Daten einer IP auslesen kann für die Anzeige (um die Anzahl der Leerzeichen zu ermitteln, habe ich ein seperates Programm geschrieben). Neben Python beschäftige ich mich noch mit C. Langfristig interessiert mich der Bereich der KI's. Ich werde wohl/hoffentlich länger hier sein.

Das Programm:

Code: Alles auswählen

from urllib.request import urlopen #importieren
from urllib.parse import urlparse
import time
import sys
import random

def speziell():
    print("\n")
    for k in range(101):
        time.sleep(random.uniform(0.001, 0.003))
        sys.stdout.write("\r%d%%" % k + " geladen")
        sys.stdout.flush()

def frage(): 
    frage = str(input("Deine Ip oder eine andere? [M/A] und Q zum verlassen oder h für Hilfe!: "))
    if frage == "M" or frage == "m":
        return True
    if frage == "A" or frage == "a":
        return False
    if frage == "Q" or frage == "q":
        return "Ciao"
    if frage == "h" or frage == "H":
        return "Help"
    if frage != "A" or frage != "a" or frage != "M" or frage !="m" or frage != "Q" or frage != "q" or frage != "h" or frage != "H":
        return "Fehler"


def main(): 
    global l 
    l = []
    data = str(check.read()) 
    if data.find("fail") != -1:
        print("IP ADRESSE: ", eingabe)
        print("Verbindung: fehlgeschlagen")
        quit()
    a = (data.count(",")) 
    data = data[2:len(data)] 
        n = data.find(",")
        l.insert(i, data[:n]) 
        data = data[n+1:len(data)]
    l.insert(i+1, data[:(len(data)-1)]) 
    l[0] = "erfolgreich" 
  

def liste(): #fkt fuer Liste
    l1 = ["Verbindung:     ", "Land:           ", "Landescode:     ", "Bundesland-Abk.:", "Bundesland:     ", "Stadt:          ", "PLZ:            ", "Breitengrad:    ", "Laengengrad:    ", "Zeitzone:       ",  "ISP:            ", "Organisation    ", "AS Nummer:      ", "IP ADRESSE:     "] #Tabelle
    print("\n")    
    print(l1[13], l[13])
    for j in range(0, 13):
        print(l1[j], l[j])
    print("\n")

def hilfe():
    print("\n        ================ HILFSEITE ================")
    print("\nMit diesem Programm kannst du Geodaten von IP's bestimmen.\n[M/m] = Damit bestimmst du deine eigene IP.\n[A/a] = Hiermit kannst du eine andere IP bestimmen oder die Ip von Websiten")
    print("	Dazu gibst du die Seite im folgenden Format ein: 'xyz.tld'!\n")
    print("\nSollte 'Verbindung: fehlgeschlagen' erscheinen, so gibt es deine IP nicht oder dem Programm ist es nicht möglich die Daten auszulesen.")
    print("\n\nDie Daten werden von der Api der Seite http://ip-api.com ausgelesen.")
    print("\n\nMade by addi\n\n") 	
    
meine = frage()

if meine == "Help":
	hilfe()
			
if meine == True:
    speziell()
    eingabe = 0
    check = urlopen("http://ip-api.com/csv?lang=de")
    main()
    liste()
if meine == False:
    s = ""
    eingabe = str(input("Deine gesuchte Ip Adresse?: "))
    check = "http://ip-api.com/csv/"
    lang = "?lang=de"
    check = s.join([check, eingabe, lang])
    check = urlopen(check)
    main()
    liste()

if meine == "Fehler":
    print("Bitte waehle zwichen den beiden optionen")
Zum Leerzeich ermitteln, damit es ordentlich aussieht:

Code: Alles auswählen

l1 = ["Verbindung:", "Land:", "Landescode:", "Bundesland-Abk.:", "Bundesland:", "Stadt:", "PLZ:", "Breitengrad:", "Laengengrad:", "Zeitzone:",  "ISP:", "Organisation", "AS Nummer:", "IP ADRESSE:"]
l = len(l1)
l2 = []

while l >= 1:
	for i in range(len(l1)-1):
		if len(l1[i]) > len(l1[i+1]): 
			zwichen = l1[i]
			l1[i] = l1[i+1]
			l1[i+1] = zwichen
	l = l - 1
print("Leerzeichen")

s=""
for i in range(len(l1)):
	a = len(l1[13]) - len(l1[i])
	b = a * " "
	l2.insert(i, s.join([l1[i], b]))
print(l2)
Die Funktion "speziell" ist eine kleine Spielerei, wollte das ausprobieren.
Was nicht gut ist:
- keine Kommentare
- Variablenbezeichnung ist katastrophal (werde ich bei Gelegenheit nach bessern)

Was noch kommt:
-ein kleiner Log als Txt
-IP Check vor dem senden
-check, ob Website online ist
(- GUI)

Jetzt probiere ich mich etwas an den Web Frameworks
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Funktionen sollten nach Tätigkeiten benannt werden »speziell« ist da zu speziell. Pausen im Millisekundenbereich kann kaum ein Betriebssystem so exakt wiedergeben, ein einfaches sleep(0.001) hat wahrscheinlich exakt den selben Effekt. Du benutzt schon String-Formatierung stückelst aber dann noch zwei Strings mit + zusammen, mach einen String draus.

»frage« liefert unterschiedliche Typen als Rückgabewert, was man am Besten vermeidet. Zusätzlich wird zwischen Fehler und richtigem Ergebnis nicht unterscheiden (für Fehler gibt es Exceptions) oder hier, einfach in einer Schleife so lange Fragen, bis eine gültige Antwort kam. »input« liefert schon einen String, das Umwandeln in einen String ist also überflüssig. Benutze if, elif und else, statt im letzten Schritt alle vorangegangenen Abfragen zu negieren. str.upper vereinfacht noch weiter.

In »main«: vergiss dass es global gibt. Das macht nur Probleme und löst keine. Alles was eine Funktion braucht, bekommt sie über ihre Argumente (check, eingabe) und gibt sie als Rückgabewerte zurück: »return l«. Statt »find« willst Du den in-Operator benutzen, obwohl die Fehlerabfrage wohl zu einfach ist, denn die 4 Buchstaben können leicht in irgendwelchen Städtenamen vorkommen. »quit« ist nirgends definiert und sollte auch in keiner Funktion vorkommen, denn so hat der Aufrufer keine Chance, auf Fehler zu reagieren. »a« wird nicht verwendet, »i« ist nicht definiert, die Einrückung ist falsch, und insert sollte man nicht verwenden. Man baut Listen von vorne nach hinten mit append auf. Du suchst vielleicht auch einfach nur nach »split«.

Die for-Schleife in »liste« ist ein Anti-Pattern, weil man direkt über die Elemente einer Liste (oder mit zip über zwei Listen) iteriert, und dafür keinen Index braucht, der nur alles komplizierter macht.

Wenn man weiß, dass man drei Teile zu einem String zusammenbauen will, benutzt man Stringformatierung und nicht »join«.


Zum zweiten Script: Es gibt bereits »sort« zum Sortieren:

Code: Alles auswählen

l1.sort(key=len)
Dabei willst Du gar nicht sortieren, weil Dir die Reihenfolge der Einträge ja wichtig ist, Du suchst also nur die Maximale Länge:

Code: Alles auswählen

max_len = max(max(len, l1))
Wie schon geschrieben, benutzt man kein »insert« sondern »append«, Variablen sollte man erst dann initalisieren, wenn man sie braucht und nicht, wie bei »l2« 10 Zeilen davor. Über den Index zu iterieren ist ein Anti-Pattern, join verwendet man nur, wenn die Anzahl der Elemente variabel ist (extra eine Liste mit zwei Elementen zu erzeugen um sie dann zusammenzupappen ist Verschwendung):

Code: Alles auswählen

l2 = []
for l in l1:
    l2.append(l + " " * (max_len - len(l))
Aber eigentlich berechnet man sowas nicht im voraus, sondern dann, wenn man es für die Ausgabe braucht:
[codebox=text file=Unbenannt.txt]
bezeichner = ["Land:", "Landescode:", "Bundesland:", "Stadt:"]
werte = ["Deutschland", "DE", "Bayern", "München"]
max_len = max(map(len, bezeichner))
for bez, wert in zip(bezeichner, werte):
print("{:{}s} {}".format(bez, max_len, wert))[/code]
addi
User
Beiträge: 28
Registriert: Donnerstag 29. März 2018, 22:54

Danke für die Ausführliche Antwort! Ich setze es gleich Mal um.
addi
User
Beiträge: 28
Registriert: Donnerstag 29. März 2018, 22:54

Funktionen sollten nach Tätigkeiten benannt werden »speziell« ist da zu speziell. Pausen im Millisekundenbereich kann kaum ein Betriebssystem so exakt wiedergeben, ein einfaches sleep(0.001) hat wahrscheinlich exakt den selben Effekt. Du benutzt schon String-Formatierung stückelst aber dann noch zwei Strings mit + zusammen, mach einen String draus.
Gut verstehe, habe es jetzt mal komplett rausgenommen, hatte mir Random gearbeitet, um jedes mal einen anderen Effekt zu erzeugen, wie gesagt, war eine Spielerei und damit nicht essentiell.
»frage« liefert unterschiedliche Typen als Rückgabewert, was man am Besten vermeidet. Zusätzlich wird zwischen Fehler und richtigem Ergebnis nicht unterscheiden (für Fehler gibt es Exceptions) oder hier, einfach in einer Schleife so lange Fragen, bis eine gültige Antwort kam. »input« liefert schon einen String, das Umwandeln in einen String ist also überflüssig. Benutze if, elif und else, statt im letzten Schritt alle vorangegangenen Abfragen zu negieren. str.upper vereinfacht noch weiter.
Bei den Rückgabewerten, fällt mir kein wirklicher Weg ein, wie man hätte es in diesem Fall mit nur einem Rückgabewert machen können. Die Schleife, da habe ich jetzt wie folgt gelöst:

Code: Alles auswählen

def frage():
	correct_input = False
	while correct_input == False:
		frage = str(input("Deine Ip oder eine andere? [M/A] und Q zum verlassen oder h für Hilfe!:"))
		if str.upper(frage) == "M":
			return True
			correct_input = True
		elif str.upper(frage) == "A":
			return False
			correct_input = True
		elif str.upper(frage) == "Q":
			return "Ciao"
			correct_input = True
		elif str.upper(frage) == "H":
			return "Help"
			correct_input = True
		else:
			correct_input = False
			print("Bitte gebe einen gültigen Befehl(M, A, H oder Q ein)")
Ich mach mich auch gleich über Exception schlau und bau das mit ein.

Code: Alles auswählen

In »main«: vergiss dass es global gibt. Das macht nur Probleme und löst keine. Alles was eine Funktion braucht, bekommt sie über ihre Argumente (check, eingabe) und gibt sie als Rückgabewerte zurück: »return l«
Macht Sinn, baue ich rein.

Tatsächlich habe ich sowas wie nach zip und split gesucht. Danke!

Ich werde mal wieder von vorne anfangen und deine Tipps mit einbauen.

Und zum zweiten Programm, im Prinzip wollte ich Bubblesort ausprobieren und hatte nach einen Verwendungszweck gesucht.


Könntest du bitte den letzten Code erklären, den du gepostet hast?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@addi: was ich das letzte mal noch übersehen hatte, eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 3.

Die neue Funktion `frage` benutzt `correct_input` gar nicht, weil es erst nach den `return` auf True gesetzt wird, ergo nie. Kann man einfach durch eine while-True-Schleife ersetzen. Ebenso kann man das Ergebnis bei "M", "A", "Q" oder "H" lassen und muß es nicht erst in irgendwas anders übersetzen, denn beides ist ja äquivalent.

Code: Alles auswählen

def frage():
    while True:
        frage = input("Deine Ip oder eine andere? [M/A] und Q zum verlassen oder h für Hilfe!:")
        if frage.upper() in ("M", "A", "Q", "H"):
            return frage.upper()
        print("Bitte gebe einen gültigen Befehl(M, A, H oder Q ein)")
Was verstehst Du an meinem zuletzt geposteten Code nicht?
addi
User
Beiträge: 28
Registriert: Donnerstag 29. März 2018, 22:54

Jo, danke jetzt habe ich denn Sinn von return wirklich verstanden :D.
Achja zum Einzug, habe es im editor jetzt eingestellt. Danke.

Also ich habe jetzt die Befehle bei max_len findest du den längsten Wert in "bezeichner".
In der Schleife sorgt zip dafür, das die zwei Listen zusammengefürt werden.
Dazu lädt die schleife in bez einen Wert von Bezeichner und in wert einen von Wert.
Soweit korrekt oder?

Aber bei der print ausgabe blicke ich nicht ganz durch.
addi
User
Beiträge: 28
Registriert: Donnerstag 29. März 2018, 22:54

Habe nun die Änderungen eingebaut und ein paar kosmetische Änderungen vorgenommen. Die exceptions wollte ich noch nicht reinbauen.

Code: Alles auswählen

from urllib.request import urlopen 
from urllib.parse import urlparse
from colorama import Fore, init

init()

def frage():
	
	while True:
		frage = input("Deine Ip oder eine andere? [M/A] und Q zum verlassen oder h für Hilfe!:")
		if frage.upper() in ("M", "A", "H", "Q"):
			return frage.upper()
		print("Bitte gebe einen gültigen Befehl(M, A, H oder Q ein)")

def main():
	bezeichner = ["Verbindung:", "Land:", "Landescode:", "Bundesland-Abk.:", "Bundesland:", "Stadt:", "PLZ:", "Breitengrad:", "Laengengrad:", "Zeitzone:",  "ISP:", "Organisation", "AS Nummer:", "IP ADRESSE:"]
	data = str(check.read())
	if "fail" in data:
		print("\nIP Adresse:", eingabe)
		print("Verbindung:" + Fore.RED + " fehlgeschlagen\n")
	else:
		liste = data.split(",")
		liste[0] = "erfolgreich"
		max_len = max(map(len, bezeichner))
		print("\n")
		
		#mir ist bewusst, das die nachfolgende Variante nicht die beste Lösung für das Problem darstellt
		
		print("{:{}s} {}".format(bezeichner[0], max_len, Fore.GREEN + liste[0]))
		liste.pop(0)
		bezeichner.pop(0)
		temp = liste[12]
		liste[12] = temp[:len(liste[12])-1]
		print(Fore.RESET + "{:{}s} {}".format(bezeichner[12], max_len, liste[12]))
		liste.pop(12)
		bezeichner.pop(12)
		for bez, wert in zip(bezeichner, liste):
			print(Fore.RESET + "{:{}s} {}".format(bez, max_len, wert))
		print("\n")

def hilfe():
    print(Fore.CYAN + "\n        ================ HILFSEITE ================")
    print("\nMit diesem Programm kannst du Geodaten von IP's bestimmen.\n[M/m] = Damit bestimmst du deine eigene IP.\n[A/a] = Hiermit kannst du eine andere IP bestimmen oder die Ip von Websiten")
    print("	Dazu gibst du die Seite im folgenden Format ein: 'xyz.tld'!\n")
    print("\nSollte 'Verbindung: fehlgeschlagen' erscheinen, so gibt es deine IP nicht oder dem Programm ist es nicht möglich die Daten auszulesen.")
    print("\n\nDie Daten werden von der Api der Seite http://ip-api.com ausgelesen.")
    print("\n\nMade by addi\n\n")

meine = frage()

if meine == "M":
    check = urlopen("http://ip-api.com/csv?lang=de")
    main()

if meine == "A":
	eingabe = str(input("Deine Ip-Adresse oder Website: "))
	check = "http://ip-api.com/csv/"
	lang = "?lang=de"
	check = check + eingabe + lang
	check = urlopen(check)
	main()

if meine == "H":
	hilfe()

Achja über die Erklärung würde ich mich dann sehr freuen :)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@addi: ich habe mir die Daten angeschaut, die Du da von der Webseite ziehst und es sind wirklich csv-Daten, die man am besten mit dem csv-Modul lädt. Dass das "fail" ein Fehler ist, hatte ich Dir ja schon geschrieben, in Wirklichkeit enthält nämlich nur das erste Feld den Wert fail/success. Die anderen Felder werden mit Anführungszeichen versehen, falls Sonderzeichen drin vorkommen. Das Lesen macht das CSV-Modul automatisch richtig. Daneben benutzt Du immer noch globale Variablen.

Code: Alles auswählen

import csv
from urllib.request import urlopen 

URL = "http://ip-api.com/csv/{}?lang=de"
BEZEICHNER = ["Verbindung:", "Land:", "Landescode:", "Bundesland-Abk.:", "Bundesland:", "Stadt:", "PLZ:", "Breitengrad:", "Laengengrad:", "Zeitzone:",  "ISP:", "Organisation", "AS Nummer:", "IP ADRESSE:"]
def display_result(address=""):
    print("\n")
    with urlopen(URL.format(address)) as result:
        data = next(csv.reader(result))
    if data[0] == "fail":
        _, error_msg, address = data
        print("IP Adresse:", address)
        print("Verbindung: fehlgeschlagen ({})".format(error_msg))
    else:
        max_len = max(map(len, BEZEICHNER))
        for bez, wert in zip(BEZEICHNER, data):
            print("{:{}s} {}".format(bez, max_len, wert))
    print("\n")

def main():
    anwort = frage()
    if antwort == "M":
        display_result()
    elif antwort == "A":
        address = input("Deine Ip-Adresse oder Website: ")
        display_result(address)
    else:
        hilfe()

if __name__ == '__main__':
    main()
noch sicherer ist es, die Daten gleich in einem wohldefinierten Format liefern zu lassen:

Code: Alles auswählen

import json
from urllib.request import urlopen 

URL = "http://ip-api.com/json/{}?lang=de"
BEZEICHNER = [
    ("Verbindung:", "status"),
    ("Land:", "country"),
    ("Landescode:", "countryCode"),
    ("Bundesland-Abk.:", "region"),
    ("Bundesland:", "regionName"),
    ("Stadt:", "city"),
    ("PLZ:", "zip"),
    ("Breitengrad:", "lon"),
    ("Laengengrad:", "lat"),
    ("Zeitzone:", "timezone"),
    ("ISP:", "isp"),
    ("Organisation:", "org"),
    ("AS Nummer:", "as"),
    ("IP ADRESSE:", "query"),
]
def display_result(address=""):
    print("\n")
    with urlopen(URL.format(address)) as result:
        data = json.loads(result.read().decode('utf8'))
    if data['status'] == "fail":
        print("IP Adresse:", data["query"])
        print("Verbindung: fehlgeschlagen ({})".format(data["message"]))
    else:
        max_len = max(map(len, BEZEICHNER))
        for bez, key in BEZEICHNER:
            print("{:{}s} {}".format(bez, max_len, data[key]))
    print("\n")
addi
User
Beiträge: 28
Registriert: Donnerstag 29. März 2018, 22:54

Hey, danke für den Code, ich werde mich gleich mal mehr mit Json und CSV und deren Funtionen auseinander setzen!

Zu den global Variablen:
Am einfachsten löse ich es, in dem ich wenn ich Variablen als global einsetzen möchte, in Funktionen auslagere oder?

Du hast es immer noch nicht erklärt :(
addi
User
Beiträge: 28
Registriert: Donnerstag 29. März 2018, 22:54

Habs das mit dem .format ist ja echt Basic. Danke nochmals Sirus! Nun setze ich mit json und CSV auseinander :)
Antworten