Python Wie öffne ich ein BytesIO Objekt um binärdateien von einer url lesen zu können?

Woodz

Lieutenant
Registriert
Apr. 2009
Beiträge
699
Hallo Leute.
Ich habe das Problem auch schon auf Stackoverflow gepostet, also wundert euch nicht, wenn ihr dort auf die gleiche Frage trefft.

Folgende Situation:
Ich möchte eine bufr-Datei unter einer genauen URL im Web öffnen, ohne sie vorher auf Festplatte downloaden zu müssen.
Das hab ich bis jetzt "geleistet":

Python:
import requests
from io import BytesIO
import eccodes as ecc

my_url = "https://opendata.meteo.be/ftp/observations/synop/2025/02/04/11/synop_6494_2025020411.bufr"

with requests.request(method='GET', url=my_url, headers={'cache-control': "no-cache"}) as response:
    if response.ok:

        with open(BytesIO(response.content), 'rb') as fp: 
            num_msgs = ecc.codes_count_in_file(fp)
            bid = ecc.codes_bufr_new_from_file(fp)
            print(num_msgs)
    else:
        print(f'{response.reason}')

Dieser Code endet in einen:
TypeError: expected str, bytes or os.PathLike object, not BytesIO

Scheinbar kann die hauseigene Python-Funktion open() Objekte vom Typ BytesIO nicht öffnen.
Hat jemand eine brauchbare Idee? :)

Beste Grüße
 
Python:
with BytesIO(response.content) as fp:
    ...

Habe noch nie damit gearbeitet, aber die API verstehe ich so, dass man das so verwenden kann.
 
vorab, keien Erfahrung mit BytesIO, aber ich vermute, du kannst das BytesIO Object direkt auslesen wenn ich mir das hier so ansehe: https://www.geeksforgeeks.org/python-stringio-and-bytesio-compared-with-open/

also:
Python:
if response.ok:

        fp = BytesIO(response.content).read()
        num_msgs = ecc.codes_count_in_file(fp)
        bid = ecc.codes_bufr_new_from_file(fp)
        print(num_msgs)
    else:
        print(f'{response.reason}')
 
Ohne "with" Block geht es sicher auch, die Frage ist am Ende nur: Muss man das Objekt schließen oder nicht?
Wenn man es auf jeden Fall muss, dann ist mit "with" immer sicherer, bevor irgendwann der Speicher voll läuft
 
Absolut guter Punkt!
meine Intention war, das Objekt direket auszulesen. ob man das in ein
Code:
with open(BytesIO(response.content).read(), 'rb') as fp:
reinsteckt muss man eh sehen.
 
Das open(..) braucht es in keinem Fall.

Ein with Block ist soviel ich weiß am Ende nur eine Zuweisung mit einem garantieren "open" und "close:

Z.B.:
Python:
#IOObject (irgendeine Klasse mit Konstruktor, .open() und .close())

#Entweder man macht folgendes (try, finally kann man weglassen):
 
a = IOObject() #wir nehmen an dass .open() bei der Erstellung schon angewendet wird
try:
   ...
finally:
    a.close()

#Oder man benutzt eine with Clause, die dem vorherigen äquivalent ist:

with IOObject() as a:
    ...
 
#Beide Varianten machen exakt dasselbe, nur dass man bei with close nicht vergessen kann.
#With garantiert den Aufruf von  .close() beim Verlassen, EGAL wie man den Block verlässt.

Beim Dateizugriff könnte man es genauso machen, also ohne with mit open(...) und danach close(...), aber das macht keiner so. :)

Edit: habs gegoogelt und so korrigiert wie es wirklich passt. :)
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: mercury
unterschreib ich. bin da auch net der große "streamer" :D
meine nur in dunkler Erinnerung zu haben, dass nicht alle Objekttypen den with Block unterstützen. da wäre das open() ein workaroudn vermut ich. war nur schnell vom OP übernommen.
 
Vielen Dank. Das funktioniert soweit. Allerdings ist dabei ein neuer Fehler aufgetreten:

"fileno
Traceback (most recent call last):File "lib/python3.10/site-packages/gribapi/gribapi.py", line 435, in grib_count_in_fileerr = lib.grib_count_in_file(ffi.NULL, fileobj, num_p)
io.UnsupportedOperation: fileno"

Scheinbar hat eccodes ein Problem damit "bytes" aus dem Arbeitsspeicher zu lesen, die nicht als Datei vor Ort vorliegen sonder als Stream.
 
mercury schrieb:
meine nur in dunkler Erinnerung zu haben, dass nicht alle Objekttypen den with Block unterstützen.
Ja hast Recht, muss man schauen ob IOBytes funktioniert oder nicht.

Ich habe es aber gerade ausprobiert und für Context Manager Protocol braucht man nur enter und exit Methoden. Der folgende Code funktioniert.

Python:
class A:
    def __init__(self,a):
        self.a = a
    def __exit__(self,x,y,z): #exakte Typen für x,y,z muss man nachlesen, die Methode muss aber 4 Variablen annehmen.
        print(self.a)
    def __enter__(self):
        return self

with A(10) as f:
    pass

#Konsole gibt dann korrekt 10 aus
Ergänzung ()

Woodz schrieb:
Scheinbar hat eccodes ein Problem damit "bytes" aus dem Arbeitsspeicher zu lesen, die nicht als Datei vor Ort vorliegen sonder als Stream.

Stackoverflow sagt, starte das Projekt mal ohne IDE direkt in der Konsole.
Falls du das schon tust, ich denke das ist irgendein Python2 -> Python3 Wrapper Problem, so wie ich die Doku zu eccodes lese.... die Lib sieht schon echt alt aus.
 
Zuletzt bearbeitet:
Ich danke Euch für Eure Hilfe. Aufgrund der Probleme mit eccodes musste ich mich doch dazu entschließen die files temporär zu speichern, damit die Sache halbwegs lesbar/verständlich und sauber wird.

Beste Grüße
 
Ich hätte einfach

content = urllib.request.urlopen(req).read().decode('utf-8')

bzw.

Python:
resource = urllib.request.urlopen(req)
content  = resource.read().decode(resource.headers.get_content_charset())

gemacht.
 
Zurück
Oben