Frage an die Python-Experten … kann man das besser / einfacher lösen?

8 Kommentare Autor: Jürgen (jdo)

Ich habe angefangen, mich etwas intensiver mit Python zu beschäftigen. Derzeit versuche ich zu Übungszwecken ein GUI für den CLI-Client von NordVPN zu schreiben.

Ich kann ja via

nordvpn countries

die Länder abfragen, zu denen ich mich verbinden kann. Diese Länder hätte ich später gerne in einem Dropdown-Menü, aber das ist gerade noch nicht der Punkt. Klar, könnte ich auch die wichtigsten Länder statisch in das Auswahlmenü schreiben, aber das ist auch Quatsch, wenn ich eine Liste aus der Anwendung bekomme.

Der Befehl von oben via Python ausgeführt

import subprocess

result = subprocess.run(['nordvpn', 'countries'], stdout=subprocess.PIPE)
printme = str(result.stdout)

print(printme)

liefert mir aber so etwas zurück.

b'\r-\r  \r\r-\r  \rAlbania\t\t\tGreece\t\t\tPoland\nArgentina\t\tHong_Kong\t\tPortugal\nAustralia\t\tHungary\t\t\tRomania\nAustria\t\t\tIceland\t\t\tSerbia\nBelgium\t\t\tIndia\t\t\tSingapore\nBosnia_And_Herzegovina\tIndonesia\t\tSlovakia\nBrazil\t\t\tIreland\t\t\tSlovenia\nBulgaria\t\tIsrael\t\t\tSouth_Africa\nCanada\t\t\tItaly\t\t\tSouth_Korea\nChile\t\t\tJapan\t\t\tSpain\nCroatia\t\t\tLatvia\t\t\tSweden\nCyprus\t\t\tLuxembourg\t\tSwitzerland\nCzech_Republic\t\tMalaysia\t\tTaiwan\nDenmark\t\t\tMexico\t\t\tThailand\nEstonia\t\t\tMoldova\t\t\tTurkey\nFinland\t\t\tNetherlands\t\tUkraine\nFrance\t\t\tNew_Zealand\t\tUnited_Kingdom\nGeorgia\t\t\tNorth_Macedonia\t\tUnited_States\nGermany\t\t\tNorway\t\t\tVietnam\n'

Mir ist schon klar, dass ich mit sed und so weiter die Tabs, Bindestriche und so weiter wegzaubern kann. Aber so ganz löst das mein Problem nicht. Eine Lösung habe ich gefunden und beschreibe sie auch anschließend. Was ich wissen möchte: kann man das eleganter lösen oder hilft in diesem Fall nur so ein kleiner Holzhammer?

Update: Lösung gefunden. Vielen Dank an Yannick, der folgendes kommentiert hat:

import subprocess

output = subprocess.run(['nordvpn', 'countries'], stdout=subprocess.PIPE).stdout.decode('utf-8')
output_list = output.split()
countries = [entry for entry in output_list if entry != '-']

print(countries)

Ich zitiere hier noch den Kommentar, weil das etwas prominenter ist:

Der Rückgabewert (stdout) ist ein Byte-Objekt, weshalb dieses dekodiert werden muss, um einen UTF-8-kodierten String zu erhalten. Mit split() wird der String bei Newlines, (mehreren) Leerzeichen und Tabs sowie anderen Arten von Whitespace aufgeteilt. Letztendlich werden noch die beiden Bindestriche aus der Liste entfernt.

Das funktioniert und ich habe etwas dazu gelernt. Vielen Dank auch an die anderen Kommentatoren, weil ich auch aus deren Antworten etwas gelernt habe.

Was ich derzeit mit Python mache

Ich lasse mir alle \t, \r, \n und so weiter in Kommas (,) umwandeln. Dabei ist es wohl egal, ob ich das mit sed oder in Python via replace mache. Ich habe mich für Python entschieden, da ich damit gerade experimentiere. Da ich nun ein eindeutiges Trennzeichen habe, kann ich mir alle Werte in eine Liste eintragen lassen.

import subprocess

result = subprocess.run(['nordvpn', 'countries'], stdout=subprocess.PIPE)
printme = str(result.stdout)
printme = printme.replace("\\t", ",").replace("\\r", ",").replace("\\n", ",")
mylist = printme.split(",")
print(mylist)

Die Ausgabe ist nun wie folgt:

Mit Python erst in Kommas gewandelt, dann eine Liste erstellt

Mit Python erst in Kommas gewandelt, dann eine Liste erstellt

Nun kann ich mir die benötigten Werte aus dieser Liste ziehen: der Wert darf nicht leer sein und soll mit einem Großbuchstaben anfangen:

import subprocess

result = subprocess.run(['nordvpn', 'countries'], stdout=subprocess.PIPE)
printme = str(result.stdout)
printme = printme.replace("\\t", ",").replace("\\r", ",").replace("\\n", ",")
mylist = printme.split(",")

for country in mylist:
    if len(country)!=0 and country[0].isupper():
        print(country)

Funktionieren tut das und ich bekomme nun nur noch die Länder angezeigt, mit denen ich mich verbinden kann.

Hier noch mal die Frage: Würde man das so ähnlich machen oder gibt es hier eine magische Funktion / einen Einzeiler, die genau das Problem löst?

Der CLI Client ist an sich völlig in Ordnung

Ich habe nicht gegen den Kommandozeilen-Client von NordVPN (günstig und schnell!)*, da er wunderbar auf dem Raspberry Pi funktioniert. Dank der Autoconnect-Funktion muss ich mich um die Software kaum kümmern, außer ich will das Land ändern. Allerdings brauche ich ein Projekt, um mir Python etwas besser zu lernen und das bietet sich gerade an. Ob im Endeffekt etwas daraus wird, weiß ich nicht.

Schnäppchen!

Du suchst ein günstiges VPN? Dauerhaft günstige Angebote. Bei manchen Anbietern bekommst Du bis zu 83 % Rabatt plus 3 Monate kostenlos.

Auf der Schnäppchen-Seite findest Du auch Deals für Spiele, E-Books und so weiter.




 Alle Kommentare als Feed abonnieren

8 Kommentare zu “Frage an die Python-Experten … kann man das besser / einfacher lösen?”

  1. Immer gerne. Ich hab keinen NordVPN hier deshalb die ausgabe des Befehls

    ls /etc

    import subprocess

    proc = subprocess.Popen(['ls','/etc'],stdout=subprocess.PIPE)
    while True:
    line = proc.stdout.readline()
    if not line:
    break
    # byte to str + strip whitespaces
    line = line.decode("utf-8").strip()
    print( line)

  2. Nachtrag: Leider werden die Whitespaces gefressen. Nicht ganz gut bei Python. Habs Dir hier gepastet: https://pastebin.com/fra5zMBB

    • jdo says:

      Danke und interessant, aber das funktioniert in meinem Fall nicht. Bei ls /etc funktioniert es wunderbar, bei meinem Befehle bekomme ich aber Zeilen mit mehreren Ländern inklusive Abstände dazwischen. Das sehe ich, wenn ich die Ausgabe in test.txt umleite.

      Trotzdem habe ich aus Deinem Ansatz wieder etwas Neues gelernt – ich auch eine Menge wert. 🙂

  3. Immer schwer sowas aus der Ferne zu diagnostizieren.
    Hier mal eine List of lists:

    import os
    df_output_lines = [s.split() for s in os.popen("df -Ph").read().splitlines()]

    • jdo says:

      Danke … wie schon vermutet, packt mein Befehl je nach Breite des Terminals mehrere Länder und Tabs in eine Zeile … ich komme wohl nicht umher, die Ausgabe anhand von Kommas und Großbuchstaben zu sezieren.

  4. ula says:

    Alternativ zum string basierten strip() könnte man auch das RegEx Modul benutzen, um die Informationen zu beziehen die man möchte.
    Nur immer die Frage, sobald RegEx ins Spiel kommt, ob man dann noch von einer leichten und einfachen Methode sprechen kann 😀

  5. Yannick says:

    Folgender Code sollte dir das gewünschte Ergebnis liefern.


    import subprocess

    output = subprocess.run(['nordvpn', 'countries'], stdout=subprocess.PIPE).stdout.decode('utf-8')
    output_list = output.split()
    countries = [entry for entry in output_list if entry != '-']

    print(countries)

    Der Rückgabewert (stdout) ist ein Byte-Objekt, weshalb dieses dekodiert werden muss, um einen UTF-8-kodierten String zu erhalten. Mit split() wird der String bei Newlines, (mehreren) Leerzeichen und Tabs sowie anderen Arten von Whitespace aufgeteilt. Letztendlich werden noch die beiden Bindestriche aus der Liste entfernt.

    • jdo says:

      JUUUHUUUU! Das funktioniert! Vielen Dank – wieder was gelernt – genau danach habe ich gesucht, wobei meine Lösung ganz schön kreativ war, finde ich. 😉