Einlesen einer Text Datei in Python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Hannes.LNG
User
Beiträge: 5
Registriert: Sonntag 2. Juni 2019, 17:31

Hallo

ich habe die bekommen eine Textdatei Zeile für Zeile auszulesen. Die Datei ist soll eine Apache-Logdatei simulieren.
Ich soll die IP-Adressen aus der Datei filtern und muss auch angeben wie viele IPs es insgesamt gibt.
Die IPs und die anzahl sollen dann in eine Textdatei geschrieben werden.

ich bin ein Python Anfänger. Ich denke es kann funktionieren, wenn die ich die Datei öffne, die Daten mit einer schleife auslese und in einer liste speicher.
Die Liste kann ich dann in eine Textdatei schreiben. Das Problem: ich wüsste nicht wie ich die Schleifen für das einlesen aufbauen soll. Mir fehlt dazu einfach die Syntax. habt ihr Vorschläge für die Schleife??

vielen Dank schon mal

das ist ein kleiner teil der Apache-Logdatei:
die IP-Adressen stehen immer am Anfang und nach der letzten Ziffer kommt immer --

127.0.0.1 - - [20/Nov/2008:17:38:06 +0100] "GET /joomla/installation/index.php HTTP/1.1" 200 8478
127.0.0.1 - - [20/Nov/2008:17:38:15 +0100] "GET /joomla/installation/favicon.ico HTTP/1.1" 200 1150
127.0.0.1 - - [20/Nov/2008:17:38:15 +0100] "GET /joomla/installation/template/css/template.css HTTP/1.1" 200 7375
127.0.0.1 - - [20/Nov/2008:17:38:15 +0100] "GET /joomla/media/system/js/mootools.js HTTP/1.1" 200 74404
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/includes/js/installation.js HTTP/1.1" 200 598
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/template/js/validation.js HTTP/1.1" 200 7137
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/template/images/j_border.png HTTP/1.1" 200 213
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/template/images/j_header_middle.png HTTP/1.1" 200 385
Benutzeravatar
noisefloor
User
Beiträge: 4190
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

... und willkommen im Forum :-)

Dein Denkansatz ist schon mal richtig.

Zeig' doch mal deinen Code, den du probiert hast und beschreib', was nicht so funktioniert, wie du dir das vorstellst.

Wenn du das Grundlagentutorial auf docs.python.org durchgearbeitet hast, sollte die Aufgabe eigentlich problemlos lösbar sein.

Gruß, noisefloor
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Was hast Du schon gelernt? Was hast Du damit versucht? Zeig den Code, den Du ausprobiert hast, und beschreibe genau, was nicht so funktioniert, wie Du Dir das vorstellst.
Zum Zählen der IPs bietet sich `collections.Counter` an.
Hannes.LNG
User
Beiträge: 5
Registriert: Sonntag 2. Juni 2019, 17:31

Hallo

erstmal danke an euch, dass ihr mir ein bisschen Motivation gegeben habt.
Ich hab mich mit dem split befehl auseinander gesetzt. So kann ich jetzt die IP filtern.
Lieder fehlt mir immer noch die die Quantität. zudem kann ich die IP Adressen nur in einer Liste in die txt schreiben. Aber es gibt dafür schon gute Methoden.... hab ich gesehen.

hier der code

Code: Alles auswählen

Adressen = []                    #Liste für die IP adressen

daten = open('access.log','r')   #Apache-Logdatei wird im lesemodus geöffnet

line = daten.readline()          #einelsen einer Zeile

while line != "":                # Schleife lieste die Zeilen aus
    text = line.split()          # IP wird aus der Zeile gefiltert
    Adressen.append(text[0])     # gefilterte IP wird in die Liste eingetragen
    line = daten.readline()      # Schleife endet, wenn zeile leer ist

f = open('adressen.txt','w')     # ausgeben der liste in eie txt
f.write(str(Adressen) +'\n')
f.close()
Benutzeravatar
sparrow
User
Beiträge: 4538
Registriert: Freitag 17. April 2009, 10:28

Gewöhn es dir gleich richtig an:
Variablennamen schreibt man klein_mit_unterstrich, Klasennamen CamelCase, Konstanten KOMPLETT_GROSS.

Such mal in der Python-Dokumentation nach dem Schlüsselwort "with" und verwende open damit.um Dateien zu öffnen.

Und dann schau dir die for-Schleife an.
Benutzeravatar
noisefloor
User
Beiträge: 4190
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

Ergänzend so dem von sparrow gesagten:

Python kann direkt zeilenweise über eine geöffnete (Text-) Datei iterieren, du brauchst nicht über readline() oder readlines() zu gehen.
In deinem Beispeil ist das Doppelminus -- der Trenner von IP und Rest - warum spilttest du dann an Whitespaces (was `split()` macht)?
Warum speicherst du die Liste einfach als String? Das macht relativ wenig Sinn und ist blöd beim weiterverarbeiten. Wenn du in der Datei `adressen.txt` die IP-Adresse zeilenweise speichern willst, dann musst du so wie so anders vorgehen. (Stichwort: über Liste iterieren). Aktuell stünden alle IP-Adressen in einer Zeile und die Zeile ist von eckigen Klammern eingeschlossen.

Gruß, noisefloor
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hannes.LNG: wenn die Bedingung, wann eine while-Schleife abbrechen soll, erst innerhalb der while-Schleife feststeht (also nachdem Du eine Zeile gelesen hast), nimmt man eine while-True-Schleife mit break:

Code: Alles auswählen

while True:
    line = daten.readline()
    if not line:
        break
    text = line.split()
    Adressen.append(text[0])
Hier aber brauchst Du gar keine while-Schleife, weil ein file-Objekt auch als Iterator fungieren kann, und daher eine for-Schleife besser ist:

Code: Alles auswählen

for line in daten:
    text = line.split()
    Adressen.append(text[0])
Dateien, die man öffnet, sollte man auch wieder schließen, das tust Du bei `daten` nicht. Um solche Fehler erst gar nicht aufkommen zu lassen, benutze das with-Statement, das automatisch schließt:

Code: Alles auswählen

with open("access.log") as daten:
    for line in daten:
        text = line.split()
        Adressen.append(text[0])
Alle Deine Kommentare beschreiben nur, was ohnehin schon im Code steht, Kommentare sollten beschreiben, warum etwas gemacht wird. Wenn Du bessere Variablennamen verwendest, werden die Kommentare noch überflüssiger.
Deutsch und Englisch zu mischen, ist ein Stolperstein beim Lesen, am besten nur Englische Begriffe verwenden:

Code: Alles auswählen

ip_addresses= []
with open("access.log") as lines:
    for line in lines:
        ip_address, _remains = line.split(None, 1)
        ip_addresses.append(ip_address)
Die String-Repräsentation einer Liste ist nur für Debuggingzwecke gedacht, nicht dafür dass man sie in eine Text-Datei schreibt. Zum Schreiben brauchst Du wieder eine Schleife:

Code: Alles auswählen

with open("adressen.txt", "w") as output:
    for ip_address in ip_addresses:
        output.write("{}\n".format(ip_address))
Zum Zählen der Vorkommnisse der IP-Adressen habe ich Dir schon `collections.Counter` vorgeschlagen.
Hannes.LNG
User
Beiträge: 5
Registriert: Sonntag 2. Juni 2019, 17:31

Danke für die Hilfe mit den for schleifen. Das hat mir schon sehr geholfen.
leider glaube ich das ich doch auf den Holzweg bin. Der counter ist für mich als Anfänger schwer zu verstehen.
Deshalb habe ich versucht die Durchläufe der Schleife zu zählen. Leider schaffe ich es nicht mal diesen wert in die txt zu schreiben.

Code: Alles auswählen

ip_addresses= []
counter = 0

with open("access.log") as lines:
    for line in lines:
        counter += 1
        ip_address, _remains = line.split(None, 1)
        ip_addresses.append(ip_address)

print(counter)

output = open('adressen.txt','w')
output.writelines(str(counter))
output.close()

with open("adressen.txt", "w") as output:
    for ip_address in ip_addresses:
        output.write("{}\n".format(ip_address))

Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Ist dir klar, was der Parameter "w" beim Aufruf von open macht? Und warum verwendest du für einen einzelnen String writelines statt write?
Hannes.LNG
User
Beiträge: 5
Registriert: Sonntag 2. Juni 2019, 17:31

"w" öffnet die Datei im Schreibmodus. Überschreibt "w" auch die Datei??
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja. https://docs.python.org/3/library/functions.html#open

""
w: open for writing, truncating the file first
"""
Benutzeravatar
kbr
User
Beiträge: 1508
Registriert: Mittwoch 15. Oktober 2008, 09:27

Hannes.LNG hat geschrieben: Sonntag 2. Juni 2019, 18:02 Ich soll die IP-Adressen aus der Datei filtern und muss auch angeben wie viele IPs es insgesamt gibt.
Die IPs und die anzahl sollen dann in eine Textdatei geschrieben werden.
Kann es sein, dass Deine Aufgabenstellung lautet, die Zahl der *unterschiedlichen* IPs herauszufinden und deren Anzahl als auch eine Auflistung dieser IPs zu speichern?

Code: Alles auswählen

ip_addresses = set()

with open("access.log") as lines:
    for line in lines:
        ip_address, _remains = line.split(None, 1)
        ip_addresses.add(ip_address)
        
# to do: save data here ...
# print number of different ips:
print(len(ip_addresses))

Die Zahl *aller* IPs herauszufinden ist trivial: dafür brauchst Du nur die Zeilen der Datei zu zählen, sofern nur gültige Einträge vorkommen.

Oder sollst Du herausfinden, wie viele *unterschiedliche* IPs vorkommen und wie oft diese *jeweils* vorkommen? Das wäre schon etwas anspruchsvoller, lässt sich aber mit dem bereits erwähnten collections.counter gut lösen.
Hannes.LNG
User
Beiträge: 5
Registriert: Sonntag 2. Juni 2019, 17:31

ja du hast recht. Ich soll tatsächlich die Häufigkeit der IPs zählen und dann mit ihrer Anzahl in einer Txt ausgeben.
Benutzeravatar
noisefloor
User
Beiträge: 4190
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

dann wäre eine mögliche Lösung, die auch "nur" mit Grundwissen von Python umsetzbar ist, als Datenstruktur ein Dictionary zu verwenden.
Der Schlüssel ist die IP-Adresse, der Wert die Anzahl der Vorkommen.
Womit du im Prinzip das nach programmierst, was `collections.Counter` so wie so macht. Also Übung für's Verständnis ist das aber nicht verkehrt.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mit `collections.Counter` und dem `ipaddress`-Modul um sicher zu stellen, das man wirklich gültige IPs zählt:

Code: Alles auswählen

#!/usr/bin/env python3
from collections import Counter
from ipaddress import ip_address

LOG_LINES = '''\
127.0.0.1 - - [20/Nov/2008:17:38:06 +0100] "GET /joomla/installation/index.php HTTP/1.1" 200 8478
127.0.0.1 - - [20/Nov/2008:17:38:15 +0100] "GET /joomla/installation/favicon.ico HTTP/1.1" 200 1150
127.0.0.1 - - [20/Nov/2008:17:38:15 +0100] "GET /joomla/installation/template/css/template.css HTTP/1.1" 200 7375
127.0.0.1 - - [20/Nov/2008:17:38:15 +0100] "GET /joomla/media/system/js/mootools.js HTTP/1.1" 200 74404
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/includes/js/installation.js HTTP/1.1" 200 598
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/template/js/validation.js HTTP/1.1" 200 7137
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/template/images/j_border.png HTTP/1.1" 200 213
127.0.0.1 - - [20/Nov/2008:17:38:16 +0100] "GET /joomla/installation/template/images/j_header_middle.png HTTP/1.1" 200 385
'''.splitlines(True)


def parse_line(line):
    ip_part, dash, _ = line.partition(' - ')
    if not dash:
        raise ValueError('dash expected')
    return ip_address(ip_part)
        

def parse_lines(log_lines):
    for line in log_lines:
        try:
            yield parse_line(line)
        except ValueError:
            pass  # No IP — intentionally ignored.


def create_histogram(log_lines):
    return Counter(parse_lines(log_lines))


def save_histogram(filename, histogram):
    with open(filename, 'w', encoding='ascii') as file:
        file.writelines(
            f'{address} {count}\n' for address, count in histogram.most_common()
        )


def main():
    histogram = create_histogram(LOG_LINES)
    print(histogram)
    save_histogram('test.txt', histogram)


if __name__ == '__main__':
    main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Antworten