Forum: PC-Programmierung python post-request probleme


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Chandler B. (chandler)


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?

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Chandler B. schrieb:
> mein Status-Code ist 405.

Method not allowed. Die statt dessen zulässigen Request-Methoden stehen 
im Allow-Header der 405-Antwort.

: Bearbeitet durch User
von Ein T. (ein_typ)


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. :-)

von Franko S. (frank_s866)


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

von Ein T. (ein_typ)


Lesenswert?

Franko S. schrieb:
> Dein Contentlength stimmt nicht,

Das ist aber nicht der Punkt.

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

Der Punkt ist hier, daß Du einen korrekten JSON-String schickst. Darum 
klappt es bei Dir. Der TO schickt aber keinen JSON-String, und das 
schlägt fehl.

von Franko S. (frank_s866)


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.

von Ein T. (ein_typ)


Angehängte Dateien:

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ß! :-)

von Franko S. (frank_s866)


Lesenswert?

Ein T. schrieb:

Ok wenn die request-lib das automatisch immer korrekt setzt dann hast du 
nat. recht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.