JSON Ordner einlesen

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
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

Hallo
Ich betreibe eine PV-Anlage und diese stellt mir minütlich die Leistungswerte per JSON-Format zu Verfügung.
Bisher habe ich auf meinem NAS QNAP per Crontab nen Skript minütlich laufen lassen welche die Daten in eine SQL-Datenbank schreibt.
Soweit so gut.
Dabei wurde die JSON Datei minütlich vom Wechselrichter neugeschrieben.

Ich würde aber gerne die kleine Last vom NAS nehmen und die Daten nur stündlich schreiben lassen.
Die Dateien würden vom Wechselrichter weiterhin geschrieben werden, ändern würde sich der Dateiname mit dem Format {DATE}_{TIME}.json.

So hätte ich in einem Ordner viele Dateien welche regelmäßig eingelesen werden müssten und anschließen verschoben bzw. gelöscht werden.

So nun die große Frage kann Python einen Ordner mit JSON-Files einlesen oder bin ich auf dem kleinem Holzweg. Oder oder oder .... gibt es eine eleganteren Lösungsansatz.

Ziel des ganzen ist es die Daten später auf einer Homepage graphisch darzustellen, dies ist aber noch im Aufbau.

Hier der CODE welchen ich bisher verwendet habe und auch gut lief:

Code: Alles auswählen

#python3
from __future__ import print_function
from datetime import date, datetime, timedelta
from time import gmtime, strftime
import time
import datetime
import mysql.connector
import json
from pprint import pprint
json_file='/share/solarlog/3.json'
cube='1'
json_data=open(json_file)
data = json.load(json_data)
PV_jetzt=data["Body"]["PAC"]["Values"]["1"]
PV_Unit_tag=data["Body"]["PAC"]["Unit"]
cnx = mysql.connector.connect(user='root', password='admin', database='PV')
cursor = cnx.cursor()

add_data = ("INSERT INTO sensorwerte "
               "(sensorid, datumzeit, sensorwert) "
               "VALUES ( %s, %s, %s)")

data_PV = ("1", strftime ("%Y-%m-%d %H:%M:%S") , PV_jetzt)

cursor.execute(add_data, data_PV)




cnx.commit()

cursor.close()
cnx.close()
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

der_Arne hat geschrieben:Hallo
So nun die große Frage kann Python einen Ordner mit JSON-Files einlesen oder bin ich auf dem kleinem Holzweg. Oder oder oder .... gibt es eine eleganteren Lösungsansatz.
Wieso nicht? Das ist ja keine Frage der Programmiersprache selbst. Was du vmtl. meinst, ist, ob es mit gängigen Python Modulen einfach möglich ist, das umzusetzen. Einen Ordner als Ganzes zu importieren geht mit dem gängigen JSON-Modul m. W. n. nicht, aber in der Standardbibliothek findest du die Module glob und json, die dafür meiner Meinung nach die richtige Wahl sind. Mit glob iterierst du über die (passenden) Dateinamen in dem Ordner, mit json liest du sie ein. Dann merkst du dir vielleicht noch den Datumsbestandteil des Dateinamens der zuletzt eingelesenen Datei.
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

Hallo
Danke für den Tipp mit GLOB,
Ich habe mir das mal angeschaut, grundsätzlich ja recht einfach - GLOB - findet alle Dateien in einem PFAD mit bestimmten Kriterien.
Bei mir würden sich so mehrere Dateien finden, ich kann die Ergebnisliste ja leider nicht auf eine Datei begrenzen, oder?
Sonst hätte ich gedacht ich lasse das Verzeichnis mit GLOB durchsuchen, bekomme als Ergebnis nur eine Datei, diese wird als json_file deklariert eingelesen in die DB geschrieben und anschließend in einen anderen Ordner verschoben. Das ganze würde im LOOP arbeiten bis GLOB keine Dateien mehr findet.
Ich denke mir bei ca. 60 Dateien mit einer Größe von 544B sollte dies nicht so lange dauern und vielleicht innerhalb von einer Minute abgearbeitet sein.
Nur wie kann ich GLOB sagen das er nur eine DATEI finden soll obwohl er mehrere zu Auswahl hätte?

Grüße der Arne
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@der_Arne: der LOOP ist ja die for-Schleife über die Liste, die glob liefert. Die zusätzliche Verschachtelung, die Du Dir da zusammenreimst, ist also unnötig.
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

Glob ist dazu gedacht, alle Dateinamen, die einem bestimmten Muster folgen, zurück zu liefern. Wenn du wirklich nur eine einzelne Datei haben wollen würdest, wäre das das falsche Werkzeug. Deine Frage klingt so, als würdest du glob per "files = glob.glob('*.json')" o.ä. aufrufen und dich nun fragen, wie du an einzelne Elemente des Ergebnisses herankommst. Du kannst natürlich über die Elemente des Ergebnisses iterieren, statt des komplizierten Verfahrens, das du beschreibst.

Ein typisches Anwendungsbeispiel sähe so aus:

Code: Alles auswählen

for file in glob.glob('*.json'):
    with open(file) as fh:
        data = json.load(fh)
        do_something(data) # z. B. in deine Datenbank schreiben
Edit:
Zu langsam ... :)
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

Hallo nochmal
nezzcarth so wie du es beschreibst hätte ich mir dies auch gedacht. Leider komme ich mit deinem Beispiel überhaupt nicht zurecht.

Ich habe mal dein Beispiel genommen und in das Skript gepackt, leider bekomme ich da die Fehlermeldung "expected an indented block"
Bei Zeile 13

Code: Alles auswählen

	data = json.load(fh)
	    ^
	    IndentationError: expected an indented block


Code: Alles auswählen

#python3
from __future__ import print_function
from datetime import date, datetime, timedelta
from time import gmtime, strftime
import time
import datetime
import mysql.connector
import json
from pprint import pprint

for file in glob.glob("*.json"):
	with open(file) as fh:
	data = json.load(fh)

	# json_file='/share/solarlog/3.json'
cube='1'
	#json_data=open(json_file)
	#data = json.load(json_data)

PV_jetzt=data["Body"]["PAC"]["Values"]["1"]
PV_Unit_tag=data["Body"]["PAC"]["Unit"]
cnx = mysql.connector.connect(user='root', password='admin', database='PV')
cursor = cnx.cursor()

add_data = ("INSERT INTO test "
               "(sensorid, datumzeit, sensorwert) "
               "VALUES ( %s, %s, %s)")

data_PV = ("1", strftime ("%Y-%m-%d %H:%M:%S") , PV_jetzt)

cursor.execute(add_data, data_PV)




cnx.commit()

cursor.close()
cnx.close()
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

Ähnlich wie bei for muss auch bei with der Blockinhalt eingerückt werden. Da sich die weiteren Befehle so wie ich das verstehe immer auf die Verarbeitung einer konkreten Datei beziehen, sollten sie vermutlich auch innerhalb des Blocks stehen. Noch besser wäre es meiner Meinung nach, diesen Teil des Codes in eine separate Funktion auszulagen.

Ansonsten: Wenn der Code ausschließlich für Python 3 gedacht ist, brauchst du den __future__ Import nicht. Weiterhin importiert man normalerweise entweder gesamte Module und spricht die benötigten Objekte beispielsweise per datetime.timedelta an, oder man importiert sie eben direkt -- beides gleichzeitig ist nicht notwendig.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@der_Arne: die Einrückung hat in Python eine wichtige Bedeutung. Welche Zeilen sollen innerhalb der for-Schleife ausgeführt werden?

Noch ein paar weitere Anmerkungen: ein Datum sollte in der Datenbank als Datum (TIMESTAMP) gespeichert werden und nicht als String. Auch passt das aktuelle Datum nicht mehr, wenn Du jetzt nur alle paar Stunden die Daten in eine Datenbank überträgst. Da solltest Du das timestamp-Feld aus den json-Dateien nehmen.
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

Okay
Danke für den Tip. Das mit dem Einrücken hat nun geklappt.
die Import .... from ... Geschichte habe ich nun auch etwas gekürzt.

Dabei ist mir aufgefallen das ich ja immer die aktuelle Zeit genutzt habe für den Zeitstempel in der DB dies was natürlich etwas bescheiden ist wenn ich 60 Dateien habe und die alle fast den gleichen Zeitstempel haben obwohl es für den Zeitraum einer Stunde wäre.
Zum Glück wird mir in der JSON-Datei auch ein Zeitstempel angeboten, dieser wird nun auch genutzt.

Super auch das nur eine Datei ausgewählt wurde, diese Datei wird ja als "fh" definiert.

nun würde ich die Datei in ein anderen Ordner verschieben
mit
os.rename geht dies ja nun nicht, oder wie kann ich das "fh" an einen Pfad dranhängen?
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

der_Arne hat geschrieben:Okay
nun würde ich die Datei in ein anderen Ordner verschieben
mit
os.rename geht dies ja nun nicht, oder wie kann ich das "fh" an einen Pfad dranhängen?
fh ist ein Objekt, das quasi den Zugriff auf eine Datei abstrahiert. Für Operationen wie verschieben (z. B. mit dem "shutil" Modul) usw. brauchst du den Dateinamen der in 'file' steht.
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

Servus

habe shutil auch grade gefunden und es funktioniert.
Super
DANKE für die viele Hilfe und Geduld

werde jetzt noch versuchen nen Loop einzubauen.
Damit geprüft wird ob sich Dateien im Ordner befinden, wenn ja soll die Datei in die DB geschrieben werden wenn nein soll Programm beendet werden.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@der_Arne: den Loop hast Du doch schon: «for filename in glob.glob('*.json'):»
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

Es wird bei mir aber nur eine Datei eingelesen und verschoben.
:K :K :(

Führe ich das Skript mehrmals aus bis der Ordner leer ist, wird mit einer Fehlermeldung abgebrochen.
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

Dann zeig doch bitte noch mal deine aktuelle Version.
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

Code: Alles auswählen

#python3
import mysql.connector
import json
import glob
import shutil

for file in glob.glob("*.json"):
	with open(file) as fh:
		data = json.load(fh)


PV_ZEIT=data["Head"]["Timestamp"]
PV_jetzt=data["Body"]["PAC"]["Values"]["1"]
PV_Unit_tag=data["Body"]["PAC"]["Unit"]
cnx = mysql.connector.connect(user='root', password='admin', database='PV')
cursor = cnx.cursor()

add_data = ("INSERT INTO test "
               "(sensorid, datumzeit, sensorwert) "
               "VALUES ( %s, %s, %s)")

data_PV = ("1", PV_ZEIT , PV_jetzt)

cursor.execute(add_data, data_PV)




cnx.commit()

cursor.close()
cnx.close()

dst = '/share/solarlog/Leistung/test/alt'
shutil.move(file, dst)
Ihr seid so geduldig.....
Normaler weise würde ich bei solchen Problemen gerne mit einem Kasten Bier daneben stehen....
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

mmmmm :cry: :cry: :cry: :cry:

Ich hab den Code nun eben etwas eingerückt und es läuft

Code: Alles auswählen

#python3
import mysql.connector
import json
import glob
import shutil

for file in glob.glob("*.json"):
	with open(file) as fh:
		data = json.load(fh)


		PV_ZEIT=data["Head"]["Timestamp"]
		PV_jetzt=data["Body"]["PAC"]["Values"]["1"]
		PV_Unit_tag=data["Body"]["PAC"]["Unit"]
		cnx = mysql.connector.connect(user='root', password='admin', 		database='PV')
		cursor = cnx.cursor()

		add_data = ("INSERT INTO test "
               			"(sensorid, datumzeit, sensorwert) "
		               "VALUES ( %s, %s, %s)")

		data_PV = ("1", PV_ZEIT , PV_jetzt)

		cursor.execute(add_data, data_PV)




		cnx.commit()

		cursor.close()
		cnx.close()

		dst = '/share/solarlog/Leistung/test/alt'
		shutil.move(file, dst)
nezzcarth
User
Beiträge: 1634
Registriert: Samstag 16. April 2011, 12:47

der_Arne hat geschrieben:
Ich hab den Code nun eben etwas eingerückt und es läuft
Genau, alles was wiederholt ausgeführt werden soll, muss entsprechend eingerückt sein. Die Verbindung zu Datenbank gehört da vielleicht nicht unbedingt zu ;)

Ich hätte jetzt (in Ermangelung einer mysql-Datenbank ungetestet) deinen Code wie folgt umgeschrieben:

Code: Alles auswählen

#!/usr/bin/env python3

import glob
import shutil
import json
import mysql.connector


def main():
    connection = mysql.connector.connect(user='root', password='admin', database='PV')
    cursor = connection.cursor()
    for file_name in glob.glob("*.json"):
        with open(file_name) as fh:
            data = json.load(fh)
            PV_ZEIT = data["Head"]["Timestamp"]
            PV_jetzt = data["Body"]["PAC"]["Values"]["1"]
            PV_Unit_tag = data["Body"]["PAC"]["Unit"]
            add_data = ("INSERT INTO test "
                "(sensorid, datumzeit, sensorwert) "
                "VALUES ( %s, %s, %s)")
            data_PV = ("1", PV_ZEIT , PV_jetzt)
            cursor.execute(add_data, data_PV)
            connection.commit()
            dst = '/share/solarlog/Leistung/test/alt'
            shutil.move(file_name, dst)
    cursor.close()
    connection.close()


if __name__ == '__main__':
    main()
Ich weiß nicht, ob die Mysql Bibliothek für die Verbindung die Verwendung als Kontextmanager implementiert. Falls ja, könntest du auch dafür ein entsprechendes with Statement einfügen und auf das explizite Schließen der Verbindung verzichten.
der_Arne
User
Beiträge: 10
Registriert: Samstag 23. Juli 2016, 13:28

DANKE Nochmal nezzcarth

ich hab deinen Code grade nochmal probiert und dieser läuft auch durch ohne Fehlermeldung... :D :D :D :D
Antworten