Webscraping irrer Fehler in Pandas Zahlen < 1000 wird das Komma gelöscht!

CED999

Lieutenant
Registriert
Juni 2011
Beiträge
980
Hallo ich habe die Dax-Kurse von folgender Seite mit Python/Selenium/Pandas gescraped:

https://www.boerse-frankfurt.de/index/dax/kurshistorie/historische-kurse-und-umsaetze

Mann muss die Seite kurze einstellen, sonst sieht man nicht worum es geht: Cookies ja und:
1. Bei "Nach Datum auflisten" dann "von": "1.1.1988" bis "15.1.1988". Und ja das "von Datum" spielt eine Rolle um den Fehler zu verstehen. "Bis" sollte einfach ein kurzer Zeitraum von 2-3 Wochen sein.
2. Auf "historische Kurse anzeigen" klicken, und kurz warten die Seite braucht 1-2 Sekunden.

Der Fehler ist nun dass in den gescrapedten Daten Alle Zahlen unter 1000 das Komma gelöscht wird und sie dadurch viel zu groß werden in Notepad++ sieht das dann so aus:

15.01.1988,,95815.0,,,
14.01.1988,,96564.0,,,
13.01.1988,,96461.0,,,
12.01.1988,,98949.0,,,
11.01.1988,,98957.0,,,
08.01.1988,,1025.28,,,
07.01.1988,,1009.01,,,
06.01.1988,,1020.16,,,
05.01.1988,,1004.34,,,
04.01.1988,,94388.0,,,
01.01.1988,,1000.0,,,
wobei der richtige Kurs am 4.1.1988 natürlich "943,88" sein muss und nicht 94K.

Mit viel Testen konnte der Fehler auf die fett markierte Zeile eingegrenzt werden also Pandas read_html Methode.

Code:
html = browser.page_source
tables = pd.read_html(StringIO(html))

lässt man sich mit enumerate den Inhalt und Typ von tables anzeigen:
if tables:
print(f'--- Seite {page} ---')
print(tables[0].head()) # Zeigt die ersten Zeilen der Tabelle
if 'Schluss' in tables[0].columns:
for idx, val in enumerate(tables[0]['Schluss']):
print(f'Zeile {idx}: Wert = {repr(val)}, Typ = {type(val)}')
erhält man:
Zeile 25: Wert = '94388', Typ = <class 'str'>
Zeile 26: Wert = '1.000,00', Typ = <class 'str'>
Wobei Zeile 25 der 4.1. also "943,88" sein müsste. und nicht 94 tausend...
Die Werte sind zwar als string eingelesen aber die Zahlen kleiner tausend haben von Pandas fälschlicherweise das Komma gelöscht bekommen.
Weiß dazu jemand mehr warum Pandas bei europäischem Zahlenformat diesen Fehler hat und wie könnte man den umgehen?

Woher wissen wir denn das Selenium-Response-Objekt "html" nicht schon falsch ist?
Der ganze Code ist etwas zu ausführlich zum hier Posten.

Ich habe aber das gescrapde Responseobjekt, bytegenau auf die Platte geschrieben und Euch angehängt. Anstatt die ganzen Einstellungen die notwendig sind bis das Webscraping läuft könnt ihr Euch einfach das Endprodukt der ganzen Mühen anschauen/einlesen. Ich konnte nur keine ".html" Dateien hier im Forum posten ihr müsst die Endung in "html" ändern.

Wenn ihr Euch da die Zahlen raussuchen wollt
Die "1000,00" vom 1.1.1988 hat als "Copy Element":
<td _ngcontent-boerse-frankfurt-c352035757="" class="widget-table-cell text-end"> 1.000,00</td>

Die 943,88:
<td _ngcontent-boerse-frankfurt-c352035757="" class="widget-table-cell text-end"> 943,88</td>

Ich habe die Zahlen auch mittels:
https://www.babelstone.co.uk/Unicode/whatisit.html

verglichen aber sie sehen identisch codiert aus. Sieht also nach einem harten Fehler in Pandas read_html aus.
 

Anhänge

  • Gefällt mir
Reaktionen: N.N.
Dokumentation lesen: https://pandas.pydata.org/docs/reference/api/pandas.read_html.html

thousands: str, optional
Separator to use to parse thousands. Defaults to ','.

Pandas macht nicht ein Komma weg, die Strings im HTML Text werden geparsed und fürs Parsen gibt es eben Regeln wie eben "." und "," ist Tausender- oder Dezimaltrennzeichen.

Der default ist halt für Zahlen aus Deutschland falsch, daher wird auch falsch geparsed. Das sind aber eigentlich Basics im Umgang mit Daten, gerade bei Zahlen.

Beim Rausschreiben in CSV oder was auch immer übrigens auch wieder drauf achten...
 
  • Gefällt mir
Reaktionen: netzgestaltung, eigsi124, cfreak84 und eine weitere Person
@DaysShadow Vielleicht habe ich es übersehen, aber das ist es nicht weil:
  1. Die "Zahlen" werden ja als string eingelesen. Typ ist ja auch str wie im Output zu erkennen ist.
  2. In jedem Szenario ist das Erstaunliche ja dass die Zahlen >1000 völlig richtig und nur die Zahlen < 1000 falsch eingelesen werden.
Als ob ein Tausendertrennzeichen "." da sein muss damit hinten das Komma stehen bleibt.
 
Wozu überhaupt umständlich scrapen, unter den ersten 10 Suchergebnissen hatte ich schon eine Seite wo man ohne Login immerhin jahrweise als CSV exportieren konnte... und zwei weitere die CSV-Export nach Registrierung anbieten.
 
  • Gefällt mir
Reaktionen: eigsi124 und N.N.
Immer noch Doku lesen?

decimal: str, default ‘.’
Character to recognize as decimal point (e.g. use ‘,’ for European data).

Zwei Einträge unter thousands. Muss natürlich entsprechend beides passen und der Default passt ja bei beiden nicht. Oder hast du das auch schon genutzt?
 
  • Gefällt mir
Reaktionen: eigsi124
@floq0r: Ja Pandas möchte da was automatisch anpassen. Das verrückte ist nur auch wenn man den Typ string erzwingt mit: converters={'Schluss': str} Dann macht es Pandas read_html immer noch falsch!! Dabei soll es ja nur als string eingelesen werden und gerade keine automatische Typumwandlung etc. machen....
 
CED999 schrieb:
Dann macht es Pandas read_html immer noch falsch!! Dabei soll es ja nur als string eingelesen werden und gerade keine automatische Typumwandlung etc. machen....
Dafür gibt es z.B. beautiful soup. Mit Pandas willst du ja explizit eine Datenauswertung vornehmen für die ein Parsing in Zahlentypen unerlässlich ist. Mit Strings kann man ja nichts wirklich viel anfangen.

https://pandas.pydata.org/docs/reference/api/pandas.read_html.html

Der read_html Methode kannst du mitteilen wie die Daten interpretiert werden sollen, dann ist vermutlich alles gut.
 
  • Gefällt mir
Reaktionen: eigsi124
DaysShadow schrieb:
Zwei Einträge unter thousands. Muss natürlich entsprechend beides passen und der Default passt ja bei beiden nicht. Oder hast du das auch schon genutzt?

Da wusste ich nicht wie ich das auf die 2. Spalte nur eingrenze, weiß nicht ob das geht. Sonst gibt das mit dem Datum in der ersten Spalte Probleme.

Aber: Das ist eh egal, weil: Du hängst da die ganze Zeit auf der automatischen Typumwandlung fest. DAS SOLL ja gar NICHT gemacht werden sie sollen als string eingelesen werden. Und außerdem kannst Du mir dann erklären warum es bei den Zahlen über 1000,00 funktioniert? Und nur Zahlen unter 1000 nicht gehen? Das steht schon im Titel des Threads...
Ergänzung ()


@KingLz : Wie sähe der Code dafür in BS aus? Irgendeinen Vorschlag wenn einfach von der ersten Tabelle der Seite table[0] die Spalte Datum und die Kurse kennst DU einfach stur als String eingelesen werden sollen, 0 Typumwandlung einfach nix. Dann PD Dataframes daraus erstellen?
KingLz schrieb:
Dafür gibt es z.B. beautiful soup. Mit Pandas willst du ja explizit eine Datenauswertung vornehmen für die ein Parsing in Zahlentypen unerlässlich ist. Mit Strings kann man ja nichts wirklich viel anfangen.

https://pandas.pydata.org/docs/reference/api/pandas.read_html.html

Der read_html Methode kannst du mitteilen wie die Daten interpretiert werden sollen, dann ist vermutlich alles gut.
 
Zuletzt bearbeitet:
CED999 schrieb:
Wie sähe der Code dafür in BS aus
In BS musst du dich effektiv durch das html (DOM) selbst hangeln: (soup ist schon das geparste html und keine Garantie dass der Code auch funktioniert, das soll nur die herangehensweise verdeutlichen.)

Python:
table = soup.find("table", {class_:"table-responsive"})

tbody = table.find("tbody")


for row in tbody.find_all("tr"):
    for col in row.find_all("td"):
        cell_data = col.text

Cell_data ist dann der text zwischen 2 td tags in der tablerow (tr) im tbody im table.

Solange du html verstehst weißt du auch wie man sich im DOM langbewegt. BS bildet effektiv das DOM in Python ab.

Bisschen Doku lesen muss man am Anfang schon und du hast vermutlich deutlich mehr Arbeit als wenn Pandas alles automatisch auswertet. Andererseits weißt du ganz genau was passiert.
 
  • Gefällt mir
Reaktionen: CED999
Zurück
Oben