csv-Liste auslesen und ausgeben

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
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Hallo,

ich hab vor aus einer csv-Liste die Zeilen auszugeben in denen in der ersten Spalte ein bestimmtes Wort steht. Ursprünglich waren die Daten in einem HTML-Code..die hab ich dann mit bs4 in eine csv-Datei geworfen und kann sie auch problemslos aufrufen. Nur versteh ich nicht wie man das ohne RE machen kann. Meine Ansätze hab ich damit gemacht und es geschafft das Wort auszugeben, aber nicht die ganzen anderen Daten aus der Zeile

Hier ist meine csv Datei

Code: Alles auswählen

5a|Do|4.|xxxxx|/|BK|xxxxx|BK|KR2|Raum�nderung
5c|Do|2.|xxxxx|/|Geo|xxxxx|Vertr.|S308
5c|Do|5.|xxxxx|/|Med|xxxxx|Vertr.|050
5c|Do|6.|xxxxx|/|Med|entf�llt
5e|Do|1.|xxxxx|/|BK|entf�llt
5e|Do|2.|xxxxx|/|BK|xxxxx|Vertr.|S305|Raum�nderung
5e|Do|5.|xxxxx|/|M|xxxxx|M|S305|Zusatzstunde
5e|Do|6.|xxxxx|/|M|xxxxx|M|S305|Zusatzstunde
6b|Do|4.|xxxxx|/|Bio|xxxxx|KL|S306|statt|Fr|29.|Mrz|6.|Std.
6bF|Do|1.|xxxxx|/|F|entf�llt
6d|Do|2.|xxxxx|/|Bio|entf�llt|gehalten|am|Do|21.|Mrz|1.|Std.
6d|Do|2.|xxxxx|/|Bio|xxxxx|M|S107|statt|Do|21.|Mrz|1.|Std.
6d|Do|4.|xxxxx|/|E|xxxxx|E|S205|Raum�nderung
7b|Do|4.|xxxxx|/|L|xxxxx|BK|KR|Zusatzstunde
Und jetzt will ich die Zeile zB nur ausgeben, wenn in dem dazugehörigen 1. Feld 6d steht.

Code: Alles auswählen

for absatz in absaetze:
    liste = absatz.text.split()
    with open('csvfile.csv', 'a', newline='') as csvfile:
        writer = csv.writer(csvfile, delimiter='|')
        writer.writerow(liste)
        mylist = liste
        r = re.compile(".*8a")
        newlist = list(filter(r.match, mylist))
        print(newlist)
Das hab ich bisher..damit kommt:

Code: Alles auswählen

[]
[]
[]
[]
['6d']
['6d']
['6d']
[]
[]
[]
[]
[]
Tut mir leid..ich bin noch ein Anfänger und versuche mir das beizubringen, aber an dem Punkt komm ich einfach nicht weiter.
Danke schonmal im Voraus
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@vertretung: Also erst einmal ist `liste` kein guter Name weil der nichts darüber sagt was in der Liste enthalten ist. Dann ist die Zuweisung von `liste` an `mylist` unsinnig, weil damit der Name a) noch schlechter wird, und b) es hier keinen Sinn macht die *selbe* Liste nun auch noch unter einem zusätzlichen anderen Namen verfügbar zu haben. Warum machst Du das?

Dann ist ein regulärer Ausdruck für *feste* Zeichenketten wie '6d' unsinnig – das würde man einfach mit ``==`` machen. Das was dann tatsächlich im Quelltext steht, also '.*8a' könnte man wahrscheinlich auch ohne regulären Ausdruck mit `str.endswith()` erledigen.

`mylist` enthält ja den gesamten Datensatz, also verarbeitest Du mit `filter()` nicht nur das erste Feld, sondern *alle*. Wenn Du nur das erste Feld auf etwas prüfen möchtest, dann mach halt auch nur das. Und halt auch nur testen und nicht irgendwas aus dem Datensatz heraus filtern. Das dann auch *vor* dem Schreiben in die Datei, denn danach ist es ja bereits in der Datei. Du musst ja vorher entscheiden ob es überhaupt geschrieben werden soll.

Da ist dann auch das ständige öffnen und schliessen der Datei für jeden Datensatz ineffizient. Die Datei würde man vor der Schleife *einmal* öffnen. Beim Modus 'a' muss man sich auch im klaren sein, dass damit nicht nur Datensätze vom aktuellen Programmlauf in der Datei landen, sondern das auch eine bereits vorhandene Datei einfach erweitert wird.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Die csv-Datei sieht nicht richtig aus. Die Zeilen haben unterschiedliche Anzahl an Spalten. Vor allem das „M|S107|statt|Do|21.|Mrz|1.|Std.” sieht nicht richtig aus. Da wurde scheinbar jedes Leerzeichen durch einen Trennstrich ersetzt.
Das liegt wahrscheinlich an dem „absatz.text.split“ Wie es richtig geht kann man aber aus dem gezeigten Code nicht sagen. ›liste‹ ist dann auch ein sehr allgemeiner Variablenname, der ohne Grund in das noch schlechtere ›mylist‹ umbenannt wird.

Du suchst nach 8a bekommst aber als Ergebnis die 6d? Sehr seltsam.

In dem gezeigten Code öffnest Du ständig die Datei um eine Zeile zu schreiben. Das macht man einmal um die gesamte for-Schleife herum. Außerdem solltest Du nicht zu viel auf einmal machen.
Also nicht in die csv-Datei schreiben und nach der 8a/6d filtern.

Erstens, nur die Daten aufbereiten, wobei da was besseres als text.split benutzt werden sollte:

Code: Alles auswählen

stundenplan = []
for absatz in absaetze:
    eintrag = absatz.text.split() # TODO: besseres Aufbereiten der Einträge
    stundenplan.append(eintrag)
Dann kannst Du diese Datenstruktur auch einfach in eine csv-Datei schreiben:

Code: Alles auswählen

with open('csvfile.csv', 'a', newline='') as csvfile:
    writer = csv.writer(csvfile, delimiter='|')
    writer.writerows(stundenplan)
Oder nach dem Filtern, was Du in Deiner Beschreibung tatsächlich geschrieben hast, nach der ersten Spalte zu filtern:

Code: Alles auswählen

klasse = '8a'  # oder '6d'
for eintrag in stundenplan:
    if eintrag[0] == klasse:
        print(eintrag)
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Erstmal danke für die Antworten..haben mir echt weitergeholfen. Jetzt funktioniert auch alles
Gibt es eine Möglichkeit die Tabelle so zu begrenzen, dass nur alles bis "entfällt" angezeigt wird? Also alles was sonst dahinter kommt mich nicht interessiert wie zB gehalten am..oder alles nach "gehalten" gelöscht wird bzw nicht ausgegeben wird (inklusive "gehalten").

Code: Alles auswählen

6bF|Do|1.|xxxxx|/|F|entf�llt
6d|Do|2.|xxxxx|/|Bio|entf�llt|gehalten|am|Do|21.|Mrz|1.|Std.
6d|Do|2.|xxxxx|/|Bio|xxxxx|M|S107|statt|Do|21.|Mrz|1.|Std.
Meine vorstellung war eigentlich:

Code: Alles auswählen

J2|Mi|8.|xxxxx|/|Geo1|entf�llt
J2|Mi|8.|xxxxx|/|g4|entf�llt
J2|Mi|9.|xxxxx|/|g4|entf�llt
Oder wenn ein Raum geändert wurde:

Code: Alles auswählen

J2A|Mi|3.|xxxxx|/|D1|xxxxx|D1|312|Raum�nderung
J2A|Mi|4.|xxxxx|/|D1|xxxxx|D1|312|Raum�nderung
Mein Ziel ist es nämlich die Daten gleichmäßig in den Spalten zu haben, damit ich sicher sein kann, dass alles in Spalte 2 die Stunde ist, damit ich die Daten dann später auch zB in einem String ausgeben kann
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie sieht denn Deine ursprüngliche HTML-Datei aus? Du schriebest etwas von bs4 aber davon hast Du nichts gezeigt. Ich denke, man sollte da schon die richtigen Spalten auswählen, anstatt hinterher zu versuchen, aus dem Durcheinander wieder das richtige herauszuraten.
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@vertretung: Ich denke mal grundsätzlich ist ein einfaches `split()` an Leerzeichen keine Sinnvolle Verarbeitung von einem `absatz`. Die Spalte die nur '/'-Zeichen enthält ist ja sicher auch überflüssig. Und bei der dritten Spalte ist der Punkt hinter der Zahl wahrscheinlich auch nicht wirklich wichtig. Und falls bei den ausge-x-ten Spalten Namen stehen, dann könnte da vielleicht auch mal ein Leerzeichen vorkommen.

Ansonsten kann man das sowohl bei `split()` schon regeln oft maximal getrennt werden soll – schau Dir da einfach mal die möglichen Argumente an. Oder man nimmt von der gesplitteten Zeile die ersten 10 Elemente. Das wäre Grundlagen bei Listenverarbeitung.

Besser wäre es natürlich vorher schon anzusetzen.

Ach: Und bei welchem Schritt gehen Dir denn die Umlaute kaputt? Wenn man Textdateien öffnet, sollte man immer explizit eine Kodierung angeben.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Sirius3 hat geschrieben: Sonntag 11. August 2019, 13:05 Wie sieht denn Deine ursprüngliche HTML-Datei aus? Du schriebest etwas von bs4 aber davon hast Du nichts gezeigt. Ich denke, man sollte da schon die richtigen Spalten auswählen, anstatt hinterher zu versuchen, aus dem Durcheinander wieder das richtige herauszuraten.
Die sahen so aus..

Code: Alles auswählen

<tr class="normal Leselinie">
<td class="VBlock">5c</td>
<td class="VBlock">Mi 5.</td>
<td class="VBlock">xxxxx / D</td>
<td class="VBlock">xxxxx</td>
<td class="VBlock">Vertr.</td>
<td class="VBlock">210</td>
<td class="VBlock" style="white-space:normal">Raum�nderung</td>
</tr>
<tr class="normal">
<td class="VBlock">5c</td>
<td class="VBlock">Mi 6.</td>
<td class="VBlock">xxxxx / PSD</td>
<td class="VBlock">entf�llt</td>
<td class="VBlock"></td>
<td class="VBlock"></td>
<td class="VBlock" style="white-space:normal"></td>
</tr>
und mit ausnahmen:

Code: Alles auswählen

<tr class="normal Leselinie">
<td class="VBlock">5c</td>
<td class="VBlock">Mi 8.</td>
<td class="VBlock">xxxxx / E</td>
<td class="VBlock">entf�llt</td>
<td class="VBlock"></td>
<td class="VBlock"></td>
<td class="VBlock" style="white-space:normal">verlegt auf Fr 29. Mrz 6. Std.</td>
</tr>
Aber ich glaub ich brauch das gar nicht mehr..meine Ausgabe ist jetzt immer:

Code: Alles auswählen

Für dich entfällt am  Mi die  4. F
Für dich entfällt am  Mi die  5. D
Für dich entfällt am  Mi die  6. D
Für dich entfällt am  Do die  4. Ch
Soweit passt das auch..mein code:

Code: Alles auswählen

stundenplan = []
for absatz in absaetze:
    eintrag = absatz.text.split()  # TODO: besseres Aufbereiten der Einträge
    stundenplan.append(eintrag)

with open('csvfile.csv', 'a', newline='') as csvfile:
    writer = csv.writer(csvfile, delimiter='|')
    writer.writerows(stundenplan)
  # oder '6d'
for eintrag in stundenplan:
    if eintrag[0] == klasse:
        tag = eintrag[1]
        stunde = eintrag[2]
        fach = eintrag[5]
        print("Für dich entfällt am ", tag, "die ", stunde, fach)
So bin ich schonmal zufrieden..nur will ich natürlich nicht schreiben, wenn eine raumänderung war, dass irgendwas entfällt. ich dachte mir, dass ich das einfach mit einem if machen könnte:

Code: Alles auswählen

for eintrag in stundenplan:
    if eintrag[0] == klasse:
        tag = eintrag[1]
        stunde = eintrag[2]
        fach = eintrag[5]

        if eintrag[6] == "entf�llt":
            print("Für dich entfällt am ", tag, "die ", stunde, fach)
        elif eintrag[6] == "Raum�nderung":
            print("Raumänderung")
tja..so gehts wohl nicht..ich schätze es ist wegen der falschen Kodierung des ä
kann man das irgendwie ganz einfach festlegen, wie die csv-Datei kodiert werden soll und wie sie abgerufen wird?
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie hast Du denn jetzt das HTML gelesen? Diesen Teil des Codes verschweigst Du noch immer. Dort liegt der Fehler, dass Du die Spalten der Tabelle alle zusammenwirfst und dort liegt wahrscheinlich auch der Fehler mit dem Encoding.
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Ich zeig dir einfach alles was ich bisher hab..

Code: Alles auswählen

import requests
from bs4 import BeautifulSoup
import csv


URL = "http://vertretungsplan.jkg-reutlingen.de/Vertretungen-Sa.html"

r = requests.get(URL)

# htmlcode = r.text       #das ist eigentlich das normale was ich aktiv machen würde, aber da die website gerade nichts anzeigt, greif ich mit htmlcode=open(...)auf den code zu den ich mir mal rauskopiert habe

htmlcode = open("sourcecode.txt", "r").read()

print("Wähle deine Stufe.")
stufe = input()
print("Wähle deine Klasse.")
klassenbuchstabe = input()
klasse = stufe+klassenbuchstabe
print("Deine Klasse ist: "+klasse)
ent = "entfällt"

soup = BeautifulSoup(htmlcode, 'html.parser')

absaetze = soup.find_all('tr')

stundenplan = []
for absatz in absaetze:
    eintrag = absatz.text.split()
    stundenplan.append(eintrag)

with open('csvfile.csv', 'a', newline='') as csvfile:
    writer = csv.writer(csvfile, delimiter='|')
    writer.writerows(stundenplan)

for eintrag in stundenplan:
    if eintrag[0] == klasse:
        tag = eintrag[1]
        stunde = eintrag[2]
        fach = eintrag[5]

        if eintrag[6] == "entf�llt":
            print("Für dich entfällt am ", tag, "die ", stunde, fach)
        elif eintrag[6] == "Raum�nderung":
            print("Raumänderung")
Danke übrigens für deine schnellen und tollen Antworten!
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Beim Lesen aus der Text-Datei mußt Du natürlich das selbe Encoding benutzen, wie beim Schreiben, also wahrscheinlich das der ursprünglichen HTML-Seite, also `ISO-8859-1`.

Code: Alles auswählen

with open("sourcecode.txt", encoding='ISO-8859-1') as source:
    htmlcode = source.read()
Zum Parsen der Tabelle, nutze BeautifulSoup:

Code: Alles auswählen

vertretungsplan = []
for row in vertretungsplan.find_all('tr'):
    vertretungsplan.append([c.text for c in row.find_all('td')])
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Sirius3 hat geschrieben: Montag 12. August 2019, 12:22 Beim Lesen aus der Text-Datei mußt Du natürlich das selbe Encoding benutzen, wie beim Schreiben, also wahrscheinlich das der ursprünglichen HTML-Seite, also `ISO-8859-1`.

Code: Alles auswählen

with open("sourcecode.txt", encoding='ISO-8859-1') as source:
    htmlcode = source.read()
Zum Parsen der Tabelle, nutze BeautifulSoup:

Code: Alles auswählen

vertretungsplan = []
for row in vertretungsplan.find_all('tr'):
    vertretungsplan.append([c.text for c in row.find_all('td')])
Soweit so gut, aber ich bekomm edn Error:

Code: Alles auswählen

    for row in vertretungsplan.find_all('tr'):
AttributeError: 'list' object has no attribute 'find_all'
Kann ich da nicht anstelle von find_all filter nehmen?
EDIT:
ne kann ich nicht..gleicher Error, aber wieso? Ich dachte filter wäre eine methode von einer liste
Sirius3
User
Beiträge: 18270
Registriert: Sonntag 21. Oktober 2012, 17:20

Muß ja an der Stelle auch `htmlcode´ statt `vertretungsplan` heißen.
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Code: Alles auswählen

    for row in htmlcode.find_all('tr'):
AttributeError: 'list' object has no attribute 'find_all'
...also entweder steh ich vollkommen auf dem Schlauch oder es geht echt nicht
Antworten