Text aus einer HTML Tabelle auslesen mit bs4

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 komm gerade nicht weiter..ich möchte eigentlich nur noch den Text aus den einzelnen spalten auslesen. Irgendwie klappt bei mir nichts, was ich in anderen Beiträgen gelesen hab.

Code: Alles auswählen

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


# [0]= 1. Tag | [1]= 2. tag
dataBothDays = soup.find_all('table', attrs={'VBlock'})

dataHeute = []
dataMorgen = []
absaetzeHeute = []
eintragHeute = []

textHeute = dataBothDays[0].find_all('tr')
textMorgen = dataBothDays[1].find_all('tr')

indexcounter1 = 0
for x in textHeute[indexcounter1]:
    absatz = textHeute[indexcounter1].find_all('td')
    indexcounter1 = indexcounter1 + 1
    absaetzeHeute.append(absatz)

indexcounter2 = 0
for x in absaetzeHeute[indexcounter2]:
    pureEintrag = textHeute[indexcounter2].find_all(
        'td').get_text
    indexcounter2 = indexcounter2 + 1
    eintragHeute.append(pureEintrag)
    print(eintragHeute)
Sieht jemand das Problem? Und gibt es vllt eine übersichtlichere Methode?
vielen Dank schonmal im Voraus!
Benutzeravatar
noisefloor
User
Beiträge: 4191
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ich nutze zwar kein BS, aber ich tippe mal stark darauf, dass `get_text` eine Methode ist und kein Attribut. Sprich, du musst es wie eine Methode aufrufen (= mit Klammern dahinter).

Über eine Liste mit einem Index iterieren ist ein Anti-Pattern in Python, man kann direkt über Listen iterieren, so in der Art `for element in liste:`

Variablen werden per Konvention in Python klein_mit_unterstrich geschrieben, nicht MixedCase oder camelCase oder so.

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

"Klapt nicht" ist eine schlechte Fehlerbeschreibung. Was passiert genau und wie weicht das vom gewünschten Verhalten ab?
Deine beiden for-Schleifen machen exakt das selbe und beide sind kaputt. x wird gar nicht verwendet und die Anzahl der Durchläufe ist nicht die, die Du brauchst.
Am besten liest Du Dir das Kapitel über Schleifen nochmal im Tutorial durch.
Dann beschreib mal in Worten, wie Du vorgehen willst, um die Tabelle zu lesen.
Benutzeravatar
HarteWare
User
Beiträge: 69
Registriert: Samstag 23. Februar 2013, 21:16
Wohnort: localhost

Persönlich könnte ich Dir am schnellsten helfen, wenn Du einen kleinen HTML Ausschnitt aufzeigst und gleichzeitig die Information, welche extrahiert werden soll.

Ein weiterer Tipp: Wenn Du Dein Skript "stabiler" machen willst, immer prüfen, ob überhaupt die verwendeten Indices gültig sind (es kann schon mal passieren, dass z. B. die Website ihre Struktur ändert, und dann kann Dein Skript einfach abstürzen, ohne dass Du weißt warum).

Was die übersichtlichkeit angeht: Ich versuche oft eigenständige "Extraktionsmethoden" in eigene Funktionen aufzuteilen. Deine beiden for-Schleifen scheinen auch sehr ähnlich zu sein, das ist meistens ein Zeichen für Codeduplikation.
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@vertretung: Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Siehe auch den Style Guide for Python Code.

Dieses Deutsch/Englisch-Gemisch, sogar innerhalb von Namen, ist nicht gut. Nach welchem Kriterium hast Du denn entschieden ob ein Name(nsteil) in Deutsch oder in Englisch benannt sein soll?

Bei dem Code stimmt so einiges nicht, und es stellt sich die Frage wie es soweit kommen konnte. Die zweite Schleife hätte man gar nicht erst schreiben sollen solange die erste Schleife nicht das macht was sie soll. Mindestens zur Präsentation im Forum hätte man auch mal alles Weglassen können was gar nicht verwendet wird, denn das kann ja alles nicht Teil des Problems sein, lenkt also nur ab. `data_heute`, `data_morgen`, `text_morgen`, und `x` werden nicht verwendet. An der Stelle von `x` benötigt man syntaktisch einen Namen, da wird konventionell ein Unterstrich für verwendet. Allerdings stellt sich auch die Frage ob Du weisst welchen Wert `x` hat und *warum* das nicht verwendet wird. Macht eine Schleife über diese Werte überhaupt Sinn?

Das `set` als explizites Argument für `attrs` sieht komisch aus, weil `attrs` als Wörterbuch dokumentiert ist. Wenn ich explizit `attrs` schreibe, würde ich da auch explizit ein Wörterbuch angeben. Ansonsten einfach nur die CSS-Klasse ohne Schlüsselwort als zweites Argument. Und auch nicht in einem `set` wenn man nur nach *einer* Klasse sucht.

Magische Zahlen als Indexwerte sollte man vermeiden. Statt in einem Kommentar zu erklären was die Indizes 0 und 1 bedeuten, sollte man die beiden Elemente einfach Namen zuweisen die einen erklärenden Kommentar überflüssig machen.

Indizes braucht man in Python auch eher selten. Wenn man dann noch in einer Schleife einen Indexwert manuell hochzählen will, macht man ziemlich sicher etwas falsch. Man kann in Python *direkt* über die Elemente von Sequenzwerten wie Listen iterieren, ohne den Umweg über einen Indexwert.

`indexcounter` ist auch nicht ganz richtig weil das ein Zähler für Indexwerte wäre. `index` wäre korrekter, und einer der wenigen Fälle wo es okay ist das ganze auf `i` zu kürzen. Die angehängten Zahlen bei `indexcounter1` und `indexcounter2` sind dagegen nicht in Ordnung. Man nummeriert keine Namen. Das deutet entweder darauf hin das man sich bessere/passendere Namen ausdenken sollte, oder das man eigentlich gar keine Einzelnamen sondern eine Datenstruktur verwenden will. Oft ist das eine Liste. Hier ist es aber einfach nur überflüssig, da die beiden Schleifen unabhängig voneinander sind, kann man für beide `index` oder `i` verwenden. Wobei wie gesagt: man braucht hier überhaupt gar keinen Laufindex.

Listen werden üblicherweise in der Mehrzahl dessen benannt was als Name für ein einzelnes Element Sinn machen würde. Bei `absaetze_heute` hast Du das ja auch gemacht, warum nicht bei `eintrag_heute`? Bei dem Namen würde der Leser einen einzelnen Eintrag erwarten und keine Liste von Einträgen.

Warum Du <td>-Elemente als „Absätze“ bezeichnest und warum Du zwei Listen erstellst, eine mit <td>-Elementen und eine mit dem Textinhalt der gleichen <td>-Elemente ist mir nicht wirklich klar‽

Da die Schleifen sowieso unsinn machen, weiss man jetzt nicht so genau ob das Absicht ist, aber da keine Listen für die Tabellenzeilen innerhalb der Schleifen erzeugt werden, würde es darauf hinauslaufen die 2D-Struktur der HTML-Tabelle in den Listen zu verlieren, was keine gute Idee ist.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Danke für die Antworten! Haben mir weitergeholfen. Hab's jetzt auch geschafft! Vielen Dank.

kann mir jemand noch schnell einen Tipp geben, wie ich hier:

Code: Alles auswählen

[<td class="VBlock">J1</td>, <td class="VBlock">Mo 4.</td>, <td class="VBlock">xxxxx / d2</td>, <td class="VBlock">xxxxx</td>, <td class="VBlock">d2</td>, <td class="VBlock">316</td>, <td class="VBlock" style="white-space:normal">Raumänderung</td>]
aus dieser Liste den Inhalt des jeweiligen td-Teils erhalten kann..hab bei bs4 nichts gefunden
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@vertretung: Mit dem `text`-Attribut oder der `get_text()`-Methode auf den Elementen der Liste.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

danke für die Antwort..allerdings habe ich das so auch schonmal versucht, aber es wirft mir nur errors.

Code: Alles auswählen

textHeuteLänge = len(textHeute)
for absatz in range(textHeuteLänge):
    absatz = textHeute[absatz].find_all('td')
    absaetze.append(absatz)
Wo sollte ich denn hier deiner Meinung nach das .text anhängen?
versucht habe ich schon:

Code: Alles auswählen

textHeuteLänge = len(textHeute)
for absatz in range(textHeuteLänge):
    absatz = textHeute[absatz].find_all('td').text
    absaetze.append(absatz)

Code: Alles auswählen

textHeuteLänge = len(textHeute)
for absatz in range(textHeuteLänge):
    absatz = textHeute[absatz].find_all('td').get_text()
    absaetze.append(absatz)

Code: Alles auswählen

textHeuteLänge = len(textHeute)
for absatz in range(textHeuteLänge):
    absatz = textHeute[absatz].find_all('td')
    absaetze.append(absatz.text)
Benutzeravatar
noisefloor
User
Beiträge: 4191
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

Programmieren ist nicht raten. Dierfehlen sehr offensichtlich noch Python-Grundlagen. Geh' doch erst mal einen Schritt zurück und lies' im Python-Tutorial und der Python-Doku auf docs.python.org nach, wie man in Python iteriert, z.B. die Elemente einer Liste. Das sind absolute Basics, dass muss du drauf haben. Weil: das braucht man immer und immer wieder.

Gruß, noisefloor
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Aber das Iterieren stimmt doch. Das funktioniert auch alles..nur das mit dem Text eben nicht
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Tut es ja offensichtlich nicht. Und wenn etwas “find_all” heißt - was ist wohl der Rückgabewert & was muss man damit machen um an die Elemente zu kommen?
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Hey,

angenommen in "textHeute" und "textMorgen" sind alle deine gefunden 'td'. Dann ist das eine Liste. Du kannst allerdings die get_text() Methode nicht auf eine Liste anwenden, sondern nur auf die Elemente der Liste. Vielleicht hilft dir der Code das ganze bisschen besser zu verstehen.

Code: Alles auswählen

from bs4 import BeautifulSoup

soup = BeautifulSoup('<td class="VBlock">J1</td>, <td class="VBlock">Mo 4.</td>, <td class="VBlock">xxxxx / d2</td>, <td class="VBlock">xxxxx</td>, <td class="VBlock">d2</td>, <td class="VBlock">316</td>, <td class="VBlock" style="white-space:normal">Raumänderung</td>')
founds = soup.find_all('td')
for found in founds:
    print(found.get_text())

bei dir müsste das etwa so aussehen:

Code: Alles auswählen

for element in textHeute:
    print(element.get_text())


Allgemeines:

Code: Alles auswählen

liste = [2,4,5,6,7,8,9]

for element in liste: #So iteriert man richtig über eine Liste
    print(element)
    
    
    
for index in range(len(liste)): #So iteriert man falsch über eine Liste
    print(liste[index])
vertretung
User
Beiträge: 20
Registriert: Sonntag 11. August 2019, 09:06

Vielen Dank für eure Hilfe! Ich hab's jetzt geschafft. Lag an einer falschen Zeile..naja man lernt aus seinen Fehlern ;D
Antworten