Seite 2 von 2
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 09:47
von Perlchamp
hallo Sirius3,
danke für deine tipps, passt trotzdem nicht

:
Code: Alles auswählen
cursor.execute("INSERT INTO edelmetallpreise (gold, palladium, datum) VALUES (?,?,?)",(gold_preis, palladium_preis, datum))
sqlite3.InterfaceError: Error binding parameter 0 - probably unsupported type.
ich ko::: gleich ...
ich hoffe, du bist mir nicht böse, aber "edelmetallpreise" als zweites argument/parameter zu verwenden verursacht bei mir kopfgulasch, weil die tabelle auch so heißt. ich habe das geändert:
Code: Alles auswählen
#!/usr/bin/env python3
import sqlite3, csv
from babel.numbers import parse_decimal
from dateutil.parser import parse
# datenbank
TABLE_NAME = "edelmetallpreise"
DB_PATH = f"E:\\APPDATA\\Atom\\Python\\Data\\{TABLE_NAME}.db"
#csv-datei
CSV_PATH = f"E:\\APPDATA\\Atom\\Python\\Data\\{TABLE_NAME}.csv"
def read_csv_file(file_path):
""" Funktion zum Auslesen einer csv-Datei
zurückgegeben wird ein Dictionary """
with open(file_path) as csvdatei:
dict_reader = csv.DictReader(csvdatei)
return list(dict_reader)
def csvdata_to_db(db_path, table_name, csv_data):
""" Trägt die Daten aus der csv-Datei in die Datenbank ein."""
connection = sqlite3.connect(db_path)
cursor = connection.cursor()
cursor.execute(f"CREATE TABLE IF NOT EXISTS {table_name} (id INT PRIMARY KEY, gold DECIMAL, palladium DECIMAL, datum DATE)")
for dataset in csv_data:
gold_preis = parse_decimal(dataset['gold'], locale='en')
palladium_preis = parse_decimal(dataset['palladium'], locale='en')
datum = parse(dataset['datum'])
cursor.execute(f"INSERT INTO {table_name} (gold, palladium, datum) VALUES (?,?,?)",(gold_preis, palladium_preis, datum))
connection.commit()
connection.close()
print (f"Die DB {db_path} wurde mit der Tabelle {table_name} samt Daten erfolgreich angelegt.")
def main():
csvdata = read_csv_file(CSV_PATH)
csvdata_to_db(DB_PATH,TABLE_NAME, csvdata)
if __name__ == '__main__':
main()
ich habe es natürlich zuerst 1:1 von dir übernommen, dann hat er moniert, dass i main() csvdata_to_db 3 argumente verlangt werden, und nicht zwei. das habe ich dann gerichtet, aber trotzdem die gleiche fehlermeldung bekommen. im grunde hat sich deine version von meiner in einer sache unterschieden: ich hatte in INSERT-statement nicht explizit die spaltennamen angegeben. aber er meckert ja wegen nicht unterstützten types. ich habe so viel probiert: DECIMAL, INTEGER, REAL, FLOAT, DATETIME, DATE, TEXT und was weiß ich noch alles ... immer wieder dieselbe fehlermeldung ...
mit:
bekomme ich ja auch die uhrzeit dazu. die bräuchte ich eigentlich nicht und DATE würde dann ja streng genommen auch nicht passen, müsste dann doch DATETIME sein, oder ?
ist es nun "unsinnig" folgenden code noch anzufügen: datum=datum.date() ?
und danke für deine tipps und deine zeit, Sirius3
so long
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 11:04
von Perlchamp
aaaalso, ich habe alles auf TEXT gesetzt, dann ging's. dann habe ich nur das datum auf DATE gesetzt und es ging. liegt also an den zahlen. ich habe alles ausprobiert, was sqlite3 unterstützt. dies habe ich von hier:
https://www.techonthenet.com/sqlite/datatypes.php.
hm, da ist guter rat teuer.... gibt es denn noch eine andere möglichkeit/modul (also nicht babel), mit dem ich es einmal probieren könnte ?
so long
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 11:49
von Sirius3
Warum heißt die Tabelle wohl so? Weil die Daten die drin sind, das sind, was sie sind. Also die Variable, die diese Daten enthält, sinnvollerweise genauso heißt.
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 12:49
von Perlchamp
das problem ist gelöst:
Code: Alles auswählen
#!/usr/bin/env python3
import sqlite3, csv, decimal
from dateutil.parser import parse
# datenbank
TABLE_NAME = "edelmetallpreise"
DB_PATH = f"E:\\APPDATA\\Atom\\Python\\Data\\{TABLE_NAME}.db"
# csv-datei
CSV_PATH = f"E:\\APPDATA\\Atom\\Python\\Data\\{TABLE_NAME}.csv"
# entries
decimal = decimal.Decimal
def adapt_decimal(decimal):
""" wandelt eine Dezimalzahl in einen String """
return str(decimal)
def convert_decimal(string):
""" wandelt einen String in eine Dezimalzahl um """
return D(string)
# Register the adapter
sqlite3.register_adapter(decimal, adapt_decimal)
# Register the converter
sqlite3.register_converter("decimal", convert_decimal)
def read_csv_file(file_path):
""" Funktion zum Auslesen einer csv-Datei
zurückgegeben wird ein Dictionary
file-path: absoluter Pfad der csv-Datei """
with open(file_path) as csvdatei:
dict_reader = csv.DictReader(csvdatei)
return list(dict_reader)
def csvdata_to_db(db_path, table_name, csv_data):
""" Trägt die Daten aus der csv-Datei in die Datenbank ein.
db_path: absoluter Pfad zur DB
table_name: Name der Tabelle, die in der DB erstellt wird
csv_data: Dictionary erstellt aus csv-Datei """
connection = sqlite3.connect(db_path)
cursor = connection.cursor()
cursor.execute(f"CREATE TABLE IF NOT EXISTS {table_name} (id INT PRIMARY KEY, gold DECIMAL, palladium DECIMAL, datum DATE)")
for dataset in csv_data:
id = int(dataset['id'])
gold_preis = decimal(dataset['gold'])
palladium_preis = decimal(dataset['palladium'])
datum = parse(dataset['datum'])
datum = datum.date()
cursor.execute(f"INSERT INTO {table_name} (id, gold, palladium, datum) VALUES (?,?,?,?)",(id, gold_preis, palladium_preis, datum))
connection.commit()
connection.close()
print (f"Die DB {db_path} wurde mit der Tabelle {table_name} samt Daten erfolgreich angelegt.")
def data_from_db(db_path,table_name):
"""Funktion zum Auslesen der Daten aus der DB:
db_path: absoluter Pfad zur datenbank
table_name: Name der Tabelle, aus der die Daten gelesen werden sollen """
connection = sqlite3.connect(db_path)
cursor = connection.cursor()
sql = f"SELECT * FROM {table_name}"
cursor.execute(sql)
db_data = cursor.fetchall()
return db_data
connection.close()
def main():
csvdata = read_csv_file(CSV_PATH)
csvdata_to_db(DB_PATH,TABLE_NAME,csvdata)
db_data = data_from_db(DB_PATH,TABLE_NAME)
print(db_data)
if __name__ == '__main__':
main()
vielen dank an __deets__
@Sirius3:
du magst ja recht haben, für mich ist es eben besser, wenn ich das so benenne
und nicht so:
so long und vielen dank für deine zeit !
und wenn's smarter geht, immer wieder gerne !
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 14:28
von __blackjack__
@Perlchamp: Definitiv Fehler sind das `D` was als Name schlecht ist und auch gar nicht definiert ist, und das `connection.close()` *nach* der ``return``-Anweisung. `sqlite3.Connection`-Objekte sind Kontextmanager, die kann man also mit ``with`` zusammen verwenden.
Sehr verwirrend ist das importieren des `decimal`-Moduls um den Namen `decimal` dann etwas später auf Modulebene erneut zu binden und zwar an `decimal.Decimal`. Das wäre ``from decimal import Decimal`` und dann ``Decimal`` halt auch in dieser Schreibweise zu verwenden. Was der Kommentar ``# entries`` vor dieser Umbenennung dem Leser sagen soll ist mir rätselhaft.
Sowohl `adapt_decimal()` als auch `convert_decimal()` sind als Funktionen überflüssig weil das doch einfach nur `str` und `Decimal` ist. Die beiden Aufrufe zum registrieren gehören nicht auf Modulebene.
Bei den Konstanten haben die beiden `*_PATH` einen ziemlich langen gemeinsamen Präfix den ich da herausziehen würde. Und dann auch das `pathlib`-Modul verwenden würde statt Pfade mit Zeichenkettenformatierung zu erstellen.
Der Docstring zu `read_csv_file()` ist falsch. Weder wird ein Dictionary zurückgegeben, noch muss das (falsch geschriebene) Argument ein *absoluter* Pfad sein. Das umwandeln in die richtigen Datentypen sollte schon beim einlesen der Datei stattfinden.
Wie schon gesagt sind Tabellennamen fest und de facto ist er das in Deinem Code ja auch, also macht es wirklich keinen Sinn den als Variable, die immer den selben Wert hat, zu übergeben.
Auch in der Dokumentation der `csvdata_to_db()`-Funktion werden die Daten als Dictionary beschrieben und der Pfad als absolut.
Ein Element von `csv_data` ist kein `dataset`. Ein `dataset` ist der die gesamte Datenmenge, nicht ein einzelner Datensatz. Der heisst `record`.
Beim speichern in die Datenbank kann man `executemany()` verwenden.
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python3
import csv
import sqlite3
from decimal import Decimal
from pathlib import Path
from dateutil.parser import parse as parse_date
TABLE_NAME = "edelmetallpreise"
DATA_PATH = Path("E:/APPDATA/Atom/Python/Data")
CSV_PATH = DATA_PATH / f"{TABLE_NAME}.csv"
DB_PATH = CSV_PATH.with_suffix(".db")
COLUMN_NAME_TO_CONVERTER = {
"id": int,
"gold": Decimal,
"palladium": Decimal,
"datum": lambda text: parse_date(text).date(),
}
def parse_record_values(record):
return {
key: COLUMN_NAME_TO_CONVERTER[key](value)
for key, value in record.items()
}
def read_csv_file(file_path):
"""
Liest eine CSV-Datei als Liste mit Wörterbüchern ein und gibt diese zurück.
"""
with open(file_path) as csv_datei:
return list(map(parse_record_values, csv.DictReader(csv_datei)))
def save_records_to_db(db_path, records):
"""
Trägt die Daten aus der CSV-Datei in die Datenbank ein.
Args:
db_path: Pfad zur DB.
records: Iterierbares Objekt das ein Wörterbuch pro Datensatz liefert.
"""
with sqlite3.connect(db_path) as connection:
cursor = connection.cursor()
cursor.execute(
f"CREATE TABLE IF NOT EXISTS {TABLE_NAME} ("
f" id INTEGER PRIMARY KEY,"
f" gold DECIMAL,"
f" palladium DECIMAL,"
f" datum DATE"
f")"
)
cursor.executemany(
f"INSERT INTO {TABLE_NAME} (id, gold, palladium, datum)"
f" VALUES (?,?,?,?)",
(
[record[key] for key in ["id", "gold", "palladium", "datum"]]
for record in records
),
)
connection.commit()
print(
f"Die DB {db_path!r} wurde mit der Tabelle {TABLE_NAME} samt Daten"
f" erfolgreich angelegt."
)
def load_rows_from_db(db_path):
"""
Liest die Daten aus der Datenbank ein und liefert eine Liste von
Datensätzen.
Args:
db_path: Pfad zur DB.
"""
with sqlite3.connect(db_path) as connection:
cursor = connection.cursor()
cursor.execute(f"SELECT * FROM {TABLE_NAME}")
return cursor.fetchall()
def main():
sqlite3.register_adapter(Decimal, str)
sqlite3.register_converter("decimal", Decimal)
records = read_csv_file(CSV_PATH)
save_records_to_db(DB_PATH, records)
rows = load_rows_from_db(DB_PATH)
print(rows)
if __name__ == "__main__":
main()
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 16:22
von Perlchamp
hi, hallo,
erst einmal vielen dank für deine zeit!
ich hatte auch wieder einiges an meinem script geändert und wollte es posten, aber ein anruf kam dazwischen. ich poste mein aktuelles script, worin auch bereits vorher fehler ausgemerzt wurden, die du zurecht "angeprangert" hast. ich werde mir aber gleich dein script anschauen und ausführen :
Code: Alles auswählen
#!/usr/bin/env python3
import sqlite3, csv
from dateutil.parser import parse
from pathlib import Path
from decimal import Decimal
# datenbank
TABLE_NAME = "edelmetallpreise"
MAIN_PATH = Path("e:\\APPDATA\\Atom\\Python\\Data")
DB_PATH = MAIN_PATH / "edelmetallpreise.db"
# csv-datei
CSV_PATH = MAIN_PATH / "edelmetallpreise.csv"
Decimal = decimal.Decimal
# Register the adapter
sqlite3.register_adapter(decimal, adapt_decimal)
# Register the converter
sqlite3.register_converter("decimal", convert_decimal)
def read_csv_file(file_path):
""" Funktion zum Auslesen einer .csv-Datei
file_path: Pfad (absolut/relativ) zur .csv-Datei """
with open(file_path) as csvdatei:
dict_reader = csv.DictReader(csvdatei)
return list(dict_reader)
def csvdata_to_db(db_path, csv_data):
""" Trägt die Daten aus der csv-Datei in die Datenbank ein.
db_path: Pfad (absolut/relativ) zur DB
csv_data: Daten aus der .csv-Datei """
connection = sqlite3.connect(db_path)
cursor = connection.cursor()
sql = """
CREATE TABLE IF NOT EXISTS edelmetallpreise (id INT PRIMARY KEY, gold DECIMAL,
palladium DECIMAL, datum DATE)
"""
cursor.execute(sql)
for record in csv_data:
id = int(record['id'])
gold_preis = decimal(record['gold'])
palladium_preis = decimal(record['palladium'])
datum = parse(record['datum'])
datum = datum.date()
sql = """
INSERT INTO edelmetallpreise (id, gold, palladium, datum) VALUES (?,?,?,?)
"""
cursor.execute(sql, (id, gold_preis, palladium_preis, datum))
connection.commit()
connection.close()
print ("Die DB wurde mit der Tabelle edelmetallpreise samt Daten erfolgreich angelegt.")
def data_from_db(db_path):
"""Funktion zum Auslesen der Daten aus der DB:
db_path: absoluter Pfad zur datenbank
table_name: Name der Tabelle, aus der die Daten gelesen werden sollen """
connection = sqlite3.connect(db_path)
cursor = connection.cursor()
cursor.execute("""SELECT * FROM edelmetallpreise""")
db_data = cursor.fetchall()
return db_data with connection.close()
def main():
csvdata = read_csv_file(CSV_PATH)
csvdata_to_db(DB_PATH,csvdata)
db_data = data_from_db(DB_PATH)
print(db_data)
if __name__ == '__main__':
main()
so long
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 16:30
von Perlchamp
@__black_jack__:
zu deinem script fehlen mir die worte (im positiven sinn). da habe ich nebenher noch viel zu lesen, um die einzelnen zeilen auch wirklich zu verstehen. im gesamten verstehe ich das schon, aber die kombinationen und und und sind für mich natürlich auf den ersten blick kaum zu verstehen. UND: vielen dank für deine zeit. sowas schreibt man ja nicht in 5 minuten runter ...
so long
Re: ist das guter stil ?
Verfasst: Freitag 8. Mai 2020, 18:57
von Perlchamp
@__blackjack__:
funzt
vielen dank nochmals
so long
Re: ist das guter stil ?
Verfasst: Samstag 9. Mai 2020, 13:36
von Perlchamp
@__blackjack__:
könnte ich eigentlich statt :
Code: Alles auswählen
COLUMN_NAME_TO_CONVERTER = {
"gold": Decimal,
"palladium": Decimal,
"datum": lambda text: parse_date(text).date(),
}
def parse_record_values(record):
return {
key: COLUMN_NAME_TO_CONVERTER[key](value)
for key, value in record.items()
}
def read_csv_file(file_path):
"""
Liest eine CSV-Datei als Liste mit Wörterbüchern ein und gibt diese zurück.
"""
with open(file_path) as csv_datei:
return list(map(parse_record_values, csv.DictReader(csv_datei)))
folgendes schreiben, wenn ich nur 2 strings (goldpreis = "1564.78bn", palladiumpreis ="1656.88xh") und ein datum (datum=date.today()) hätte :
Code: Alles auswählen
COLUMN_NAME_TO_CONVERTER = {
"gold": Decimal,
"palladium": Decimal,
"datum": lambda text: parse_date(text).date(),
}
def parse_record_values(record):
return {
key: COLUMN_NAME_TO_CONVERTER[key](value)
for key, value in record.items()
}
def matches():
goldpreis = goldpreis[:-2]
palladiumpreis = palladiumpreis[:-2]
datum = date.today()
dict = {'gold' : goldpreis, 'palladium' : palladiumpreis, 'datum' : datum}
return list(map(parse_record_values, dict))
ich wollte dein script als vorlage benutzen, um drei variable in eine db zu schreiben. leider besteht wieder das problem mit dem parsen ...
ich habe, wie gesagt, 2 zahlen (15687.56, 1675.89) als strings vorliegen und das datum als typ 'date'. komischerweise (ich habe python 3.8.2) moniert er das parsen mit decimal. wenn ich schreibe:
Decimal = decimal.Decimal sagt python bereits, dass decimal nicht definiert sei, obwohl ich es importiert habe (from decimal import Decimal).
besten dank im voraus
so long
Re: ist das guter stil ?
Verfasst: Samstag 9. Mai 2020, 15:53
von __blackjack__
Mit ``from decimal import Decimal`` ist natürlich `decimal` nicht definiert. Das einzige was diese Zeile in Deinem Modul definiert ist `Decimal`. Was hättest Du denn überhaupt mit ``Decimal = decimal.Decimal`` erreichen wollen? `Decimal` *ist* doch bereits definiert, durch den Import.
Wo kommen denn bei `matches()` die Namen `goldpreis` und `palladiumpreis` her? Und die letzten beiden Zeilen funktionieren natürlich nicht. `dict` ist ein Wörterbuch und wenn man darüber iteriert, dann liefert das die Schlüssel. Also die Zeichenketten "gold", "palladium", und "datum". Und auf jede wird dann `parse_record_values()` angewendet. Die Funktion erwartet aber ein Wörterbuch und keine Zeichenkette.
Wenn sich der Preis nicht einfach mit `Decimal` parsen lässt, dann braucht man dafür eine Funktion die das kann. Wenn man die in einen Ausdruck passt und man sie nur einmal bräuchte könnte man einen ``lambda``-Ausdruck direkt in das Wörterbuch stecken. Aber da man die zweimal braucht, wäre eine benannte Funktion besser. Dem Beispielcode folgend:
Code: Alles auswählen
def parse_price(text):
return Decimal(text[:-2])
COLUMN_NAME_TO_CONVERTER = {
"gold": parse_price,
"palladium": parse_price,
"datum": lambda text: parse_date(text).date(),
}
Re: ist das guter stil ?
Verfasst: Samstag 9. Mai 2020, 16:25
von Perlchamp
super, danke für deine antwort, wusste wirklich nicht, das man die keys bekommt, wenn man über ein dict iteriert. ist aber wahrscheinlich vernünftig, weil man dann mit den keys die valuies ansprechen kann ... ich mag es zwar smart, aber manchmal ist das so cryotisch, dass ich fast gar nichts verstehe. wichtiger wäre wohl in meiner lage und wissensstand, RICHTIG (stil) zu programmieren, auch wenn's etwas umständlich ist. das lernt man wohl erst mit der zeit undgenügend erfahrung ...
vielen dank nochmals für deine zeit.
so long
Re: ist das guter stil ?
Verfasst: Samstag 9. Mai 2020, 17:48
von Perlchamp
ich raff's einfach nicht. es muss doch eine einfach möglichkeit geben, ziffern als String im eine dezimalzahl umzuwandeln. langsam beschleicht mich das gefühl, dass da was mit der version 3.8.2 nicht ganz so stimmt. ich meine das folgende script ist doch wirklich gaaanz einfach. der stil ist, klar, grottenschlecht (keine funktionen, kein main() und und und), aber das ist nebensache :
Code: Alles auswählen
import requests
import locale
from lxml import html
from datetime import date
from decimal import Decimal
from babel.numbers import parse_decimal
URL = "https://www.coininvest.com/de/kurse/goldpreis/"
PATTERN = "//span[@class='live_metal_prices_li_txt']/text()"
page = requests.get(URL)
tree = html.fromstring(page.content)
prices = tree.xpath(PATTERN)
print(f"Preise: {prices}")
"""
Ausgabe:
Preise: ['1.572,08\xa0€', '1.572,10\xa0€', '1.572,08\xa0€', '1.572,08\xa0€',
'14,36\xa0€', '732,36\xa0€', '1.710,92\xa0€']
"""
goldpreis = prices[0]
palladiumpreis = prices[-1]
goldpreis = goldpreis[:-2]
palladiumpreis = palladiumpreis[:-2]
print (f"Gold Euro: {goldpreis}, Typ: {type(goldpreis)}")
print (f"Palladium Euro: {palladiumpreis}, Typ: {type(palladiumpreis)}")
"""
Ausgabe:
Gold Euro: 1.572,08, Typ: <class 'str'>
Palladium Euro: 1.710,92, Typ: <class 'str'>
"""
gpreis = parse_decimal(goldpreis, locale="de")
ppreis = parse_decimal(palladiumpreis, locale="de")
print (f"Gold Euro: {gpreis}, Typ: {type(gpreis)}")
print (f"Palladium Euro: {ppreis}, Typ: {type(ppreis)}")
"""
Ausgabe:
Gold Euro: 1572.08, Typ: <class 'decimal.Decimal'>
Palladium Euro: 1710.92, Typ: <class 'decimal.Decimal'>
ANMERKUNG:
wird von sqlite3 nicht als Decimal, float, real, etc
angenommen
"""
goldpreis = Decimal(goldpreis)
palladiumpreis = Decimal(palladiumpreis)
datum = date.today()
print (f"Gold Euro: {goldpreis}, Typ: {type(goldpreis)}")
print (f"Palladium Euro: {palladiumpreis}, Typ: {type(palladiumpreis)}")
print (f"Datum: {datum}, Typ: {type(datum)}")
"""
Ausgabe:
Gold Euro: 1.572,08, Typ: <class 'str'>
Palladium Euro: 1.710,92, Typ: <class 'str'>
Traceback (most recent call last):
File "E:\appdata\atom\python\scripte\test.py", line 48, in <module>
goldpreis = Decimal(goldpreis)
decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]
"""
wo liegt mein (denk)fehler ?
Re: ist das guter stil ?
Verfasst: Samstag 9. Mai 2020, 18:47
von Sirius3
SQlite3 hat nur ein sehr eingeschränktes Typsystem, deshalb der Rat von __blackjack__ sqlalchemy zu benutzen, dann muß man sich mit den ganzen Problemen nicht rumschlagen.
Es wäre auch nicht schwer, einen passenden Adapter zu schreiben:
https://stackoverflow.com/questions/631 ... ic#6319513
Statt einfach die letzten beiden Zeichen abzuschneiden, sollte man wohl split benutzen, um am Leerraum zu trennen.
Re: ist das guter stil ?
Verfasst: Samstag 9. Mai 2020, 20:53
von Perlchamp
danke für deine antwort. dann werde ich mir mal die seite anschauen, mich mit split beschäftigen und mich in sqlalchemy einlesen

...