python post-request probleme

OP #7828101
Lesenswert?

Hallo, ich möchte gerne mit python und requests dateien von https://garmin-connect-weight-api.onrender.com/ runterladen. Damit bekomme ich dann .fit-Dateien, die ich auf Garmin hochladen kann. Da kann man folgendes importieren: Garmin-Aktivitätsdateien (TCX-, FIT- oder GPX-Format) Fitbit®-Daten zu Gesundheit oder Aktivitäten (XLS-, XLSX- oder CSV-Format).

Ich kann die Daten von meiner Waage/App (Waage: Fitleap; App: Fitdays) als .xlsx exportieren (Beitrag "csv parsen kryptische Symbole"), aber dies ist dennoch ein anderes Format.

mein Problem ist aber, dass das mit dem post-request bei mir nicht so ganz funktioniert

1
import requests
2

3
if __name__ == '__main__':
4
    url = 'https://garmin-connect-weight-api.onrender.com'
5
    headers = {"Content-Type": "application/json", "Content-Length":"73", "path":"weight", "Accept-Language":"de", "Accept":"*/*", "Origin":"https://garmin-connect-weight-api.onrender.com"}
6
    data={"weight":80.1, "percentFat":20,}
7
    
8
    response=requests.post(url, headers=headers, json=data)
9
    print(response.status_code)

mein Status-Code ist 405.

Wenn ich "/weight" an meiner url direkt mit anhäge, bekomme ich einen status_code=500.

Hat jemand einen tip woran das liegt?

#7828125
Lesenswert?

Chandler B. schrieb:

ich möchte gerne mit python und requests dateien von https://garmin-connect-weight-api.onrender.com/ runterladen. Damit bekomme ich dann .fit-Dateien, die ich auf Garmin hochladen kann. [...] mein Problem ist aber, dass das mit dem post-request bei mir nicht so ganz funktioniert

1
>     headers = {"Content-Type": "application/json", 
2
> "Content-Length":"73", "path":"weight", "Accept-Language":"de", 
3
> "Accept":"*/*", 
4
> "Origin":"https://garmin-connect-weight-api.onrender.com"}
5
>     data={"weight":80.1, "percentFat":20,}
6
>

[...] Hat jemand einen tip woran das liegt?

Ja, an zwei Dingen: erstens POSTest DU auf den flashcen URL, deswegen kommt dort korrekterweise ein 405 ("Method not allowed"). Wenn ich das ECMAScript unten auf der Seite richtig lese, ist "/weight" der korrekte Endpunkt.

Auf "/weight" kommt jedoch das zweite Ding zum Tragen, Du POSTest nämlich gar kein JSON, wenngleich Dein Header das behauptet. In Wahrheit sendest Du Daten im MIME-Typ "application/x-www-form-urlencoded" -- das macht requests nämlich so, wenn "data" ein Dictionary übergeben wird. Der Server sieht also den Body 'weight=80.1&percentFat=20' anstelle des ausweislich des genannten ECMAScript gewünschten JSON-Formats '{"weight":80.1,"percentFat":20}'.

Dein "Content-type"-Header behauptet also einen anderen Content-type als Du sendest. Der Server wird dann versuchen, Deinen Body in "application/x-www-form-urlencoded" mit einem JSON-Parser zu dekodieren. Das schlägt natürlich fehl und deswegen bekommst Du einen HTTP/500 "Internal Server Error" zurück. So weit, so korrekt.

Wenn Du "application/json" senden willst, wie es dieser Service anscheinend erwartet, dann mußt Du Deine Daten entsprechend formatieren, zum Beispiel mit der Funktion "dumps" aus den Standardmodul "json" -- das macht "requests" nicht automatisch für Dich, nur weil Du den Content-type gesetzt hast.

Ein wohlerzogener Server könnte möglicherweise auch Daten in "application/x-www-form-urlencoded" verarbeiten, wenn der Header korrekt gesetzt wäre. Aber ich würde mich an Deiner Stelle eher nicht darauf verlassen. :-)

Gast #7828155
Lesenswert?

Dein Contentlength stimmt nicht, wenn ich den weglasse geht es:

1
curl \
2
-D headers_received.txt \
3
-H 'Content-Type: application/json' \
4
-H 'path: weight' \
5
-H 'Accept-Language: de' \
6
-H 'Accept: */*' \
7
-H 'Origin: https://garmin-connect-weight-api.onrender.com' \
8
-H 'Accept-Language: de,en-US;q=0.7,en;q=0.3' \
9
-H 'Accept-Encoding: gzip, deflate, br, zstd' \
10
-H 'Referer: https://garmin-connect-weight-api.onrender.com/' \
11
-H 'Alt-Used: garmin-connect-weight-api.onrender.com' \
12
-H 'Connection: keep-alive' \
13
-H 'Sec-Fetch-Dest: empty' \
14
-H 'Sec-Fetch-Mode: cors' \
15
-H 'Sec-Fetch-Site: same-origin' \
16
-H 'Priority: u=0' \
17
-H 'TE: trailers' \
18
--data '{"weight":80.1, "percentFat":20}' \
19
-X POST \
20
--output fettsack.fit \
21
https://garmin-connect-weight-api.onrender.com/weight
Gast #7828200
Lesenswert?

Ein T. schrieb:

Franko S. schrieb:

Dein Contentlength stimmt nicht,

Das ist aber nicht der Punkt.

--data '{"weight":80.1, "percentFat":20}'

Der Punkt ist hier, daß Du einen korrekten JSON-String schickst. Darum klappt es bei Dir.

Und seine fasche Content-length weglasse. Setzt man diese funktioniert es nämlich nicht mehr obwohl der Rest korrekt ist also liegt es auch an seinem falschen Content-Length-Angabe.

Der TO schickt aber keinen JSON-String, und das schlägt fehl.

ja, u.a. Was das Pythondignens reponse da noch vorher drann rumflickwerkt weiss auch keiner. Deshalb nimmt man erst mal curl und schaut ob es formal richtig ist, wenn es dann immer noch nicht geht, liegt es an der der request-lib von Python oder was auch immer man da verwendet. Content-Length selber setzen ist eh meistens überflüssig wenn man ne lib nutzt, die erzeugt das automatisch richtig, vor allem wenn man das noch encoded, packt,... ausser man fummelt da ohne lib herum, von einem Mc heraus wo man http noch selber zusammenklöppelt weil der MC zu schwach/wenig Speicher hat.

#7828222
Lesenswert?

Franko S. schrieb:

Ein T. schrieb:

Franko S. schrieb:

Dein Contentlength stimmt nicht,

Der Punkt ist hier, daß Du einen korrekten JSON-String schickst. Darum klappt es bei Dir.

Und seine fasche Content-length weglasse. Setzt man diese funktioniert es nämlich nicht mehr obwohl der Rest korrekt ist also liegt es auch an seinem falschen Content-Length-Angabe.

Ja, äh... nein. Es ist nicht /seine/ falsche Content-Length-Angabe, sondern es ist leider Deine falsche Content-Length-Angabe, wie wir gleich sehen.

Deshalb nimmt man erst mal curl und schaut ob es formal richtig ist,

... und baut dabei einen neuen Fehler ein, der mit dem ursprünglichen leider nichts zu tun hat. War gut gemeint von Dir, aber... :-)

wenn es dann immer noch nicht geht, liegt es an der der request-lib von Python oder was auch immer man da verwendet.

Ach, weißt Du, das "requests"-Paket in Python ist weit verbreitet und gut getestet, da würde ich mal vermuten, daß es seine Sache richtig macht. Und siehe da: das tut es dann auch.

Das "requests"-Modul setzt die Content-Length nämlich vollkommen korrekt, und zwar unabhängig davon, ob und wie sie im Header angegeben wurde. Der Request

1
requests.post(
2
    "http://localhost:2000", 
3
    data=json.dumps({'foo': 'bar', 'num': 2}), 
4
    headers={"Content-Length": "234"}
5
)

schickt die korrekte Content-Length von 24:

1
POST /asdf
2
User-Agent: python-requests/2.31.0
3
Accept-Encoding: gzip, deflate, br
4
Accept: */*
5
Connection: keep-alive
6
Host: localhost:2000
7
Content-Length: 21
8

9
{"foo":"bar","num":2}

Beispiel-Server und -Client habe ich angehängt, wenn es jemand selbst ausprobieren möchte. Viel Spaß! :-)

Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren