V novém seriálu, který ode dneška začíná na serveru mojefedora.cz vycházet, se postupně seznámíme s nejpopulárnějšími a současně i nejužitečnějšími knihovnami dostupnými vývojářům používajícím programovací jazyk Python. První velmi známou a užitečnou knihovnou je knihovna nazvaná Requests.
Obsah
1. Užitečné knihovny a moduly pro Python: knihovna Requests
2. Instalace knihovny Requests pro přihlášeného uživatele
3. Kontrola instalace knihovny Requests
4. Základ protokolu HTTP: metody a stavové kódy
5. Nejjednodušší příklad – poslání požadavku GET na zvolenou adresu
6. Základní atributy objektu typu Response: stavový kód a indikátor korektní odpovědi
7. Přečtení vybrané hlavičky z odpovědi HTTP serveru
8. Předání informací (parametrů) serveru přímo v URL
9. Přečtení těla odpovědi serveru v původní textové podobě
10. Získání metainformací o poslaných datech (typ a délka)
11. Zpracování odpovědi, která byla vrácena ve formátu JSON
13. Předání dat serveru ve „formuláři“
14. Předání dat serveru v těle požadavku
15. Vlastní jednoduchý testovací HTTP server
17. Volání vlastního HTTP serveru s využitím knihovny Requests
19. Repositář s demonstračními příklady
1. Užitečné knihovny a moduly pro Python: knihovna Requests
S knihovnou pojmenovanou Requests, kterou je možné použít v Pythonu 2.x i (a to především) v Pythonu 3.x, se pravděpodobně někteří čtenáři tohoto článku již setkali. Jedná se o knihovnu určenou pro práci s protokolem HTTP, tj. (poněkud zjednodušeně řečeno) k posílání HTTP požadavků na nějaký server s využitím některé HTTP metody (GET, POST atd.). Podobných knihoven existuje i pro Python několik, ovšem knihovna Requests se stala populární především díky její velmi snadné použitelnosti, protože i relativně složité operace je možné provést poměrně jednoduchým způsobem. Dnes se seznámíme pouze se základními vlastnostmi této knihovny, ovšem v navazujícím článku budou popsány i některé pokročilejší možnosti, například správa sezení (session), použití cookies, autentikace, použití SSL apod.
Důležitá poznámka: všechny dále uvedené demonstrační příklady jsou určeny pro Python 3.x. Většina příkladů bude pracovat korektně i v Pythonu 2.x, ovšem implementace jednoduchého HTTP serveru si vyžádá úpravy – odlišné importy atd.
2. Instalace knihovny Requests pro přihlášeného uživatele
Instalace knihovny Requests je velmi jednoduchá a použijeme pro ni nástroj pip3, tj. pip installer určený pro Python 3. Instalaci provedeme s volbou --user, čímž zajistíme, že se všechny soubory nainstalují do adresáře .local a pro instalaci tak nebude zapotřebí mít práva superuživatele:
$ pip3 install --user requests
Průběh instalace může vypadat následovně (jedná se o čistě nainstalovanou Fedoru 27 Server):
Collecting requests Downloading https://files.pythonhosted.org/packages/65/47/7e02164a2a3db50ed6d8a6ab1d6d60b69c4c3fdf57a284257925dfc12bda/requests-2.19.1-py2.py3-none-any.whl (91kB) 100% |████████████████████████████████| 92kB 296kB/s Collecting chardet<3.1.0,>=3.0.2 (from requests) Downloading https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB) 100% |████████████████████████████████| 143kB 898kB/s Collecting idna<2.8,>=2.5 (from requests) Downloading https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl (58kB) 100% |████████████████████████████████| 61kB 1.8MB/s Collecting urllib3<1.24,>=1.21.1 (from requests) Downloading https://files.pythonhosted.org/packages/bd/c9/6fdd990019071a4a32a5e7cb78a1d92c53851ef4f56f62a3486e6a7d8ffb/urllib3-1.23-py2.py3-none-any.whl (133kB) 100% |████████████████████████████████| 143kB 1.8MB/s Collecting certifi>=2017.4.17 (from requests) Downloading https://files.pythonhosted.org/packages/7c/e6/92ad559b7192d846975fc916b65f667c7b8c3a32bea7372340bfe9a15fa5/certifi-2018.4.16-py2.py3-none-any.whl (150kB) 100% |████████████████████████████████| 153kB 976kB/s Installing collected packages: chardet, idna, urllib3, certifi, requests Successfully installed certifi-2018.4.16 chardet-3.0.4 idna-2.7 requests-2.19.1 urllib3-1.23
Pokud je již knihovna Requests nainstalována, bude celý proces mnohem kratší:
$ pip3 install --user requests Requirement already satisfied (use --upgrade to upgrade): requests in /usr/lib/python3/dist-packages Cleaning up...
Alternativně je samozřejmě možné knihovnu nainstalovat do systémových adresářů, takže bude dostupná pro všechny uživatele:
$ sudo pip3 install requests
3. Kontrola instalace knihovny Requests
Po instalaci si můžeme ověřit, zda je knihovna Requests skutečně korektně nainstalována a zda k ní má interpret Pythonu přístup. Nejprve běžným způsobem spustíme interpret Pythonu:
$ python3 Python 3.6.6 (default, Jul 19 2018, 16:29:00) [GCC 7.3.1 20180303 (Red Hat 7.3.1-5)] on linux Type "help", "copyright", "credits" or "license" for more information.
Následně se pokusíme naimportovat knihovnu Requests a pro jistotu zobrazit i její dokumentaci:
>>> import requests >>> help("requests")
V případě, že instalace proběhla v pořádku, měl by výstup vypadat přibližně následovně:
Help on package requests: NAME requests DESCRIPTION Requests HTTP Library ~~~~~~~~~~~~~~~~~~~~~ Requests is an HTTP library, written in Python, for human beings. Basic GET usage: >>> import requests >>> r = requests.get('https://www.python.org') >>> r.status_code 200 >>> 'Python is a programming language' in r.content True ... or POST: >>> payload = dict(key1='value1', key2='value2') >>> r = requests.post('http://httpbin.org/post', data=payload) >>> print(r.text) { ... "form": { "key2": "value2", "key1": "value1" }, ... } The other HTTP methods are supported - see `requests.api`. Full documentation is at <http://python-requests.org>. :copyright: (c) 2017 by Kenneth Reitz. :license: Apache 2.0, see LICENSE for more details.
4. Základ protokolu HTTP: metody a stavové kódy
Protokol HTTP neboli Hypertext Transfer Protocol byl původně navržen pro přenos hypertextových dokumentů napsaných ve formátu/jazyku HTML. Dnes se ovšem používá i pro mnohé další účely; například ho využívají REST API služby atd. Protokol HTTP pracuje způsobem dotaz-odpověď neboli request-response (ostatně právě zde můžeme vysledovat původ názvu knihovny Requests). Jak dotaz, tak i odpověď, jsou reprezentovány formátovaným textem, kde jednotlivé řádky mají předem známý význam. Protokolem HTTP je samozřejmě možné přenášet i data; ostatně pro tento účel slouží pojmenované metody s předem specifikovaným významem (viz tabulku pod tímto odstavcem). Specifikace protokolu HTTP rovněž obsahuje popis takzvaných stavových kódů, kterými server předává klientovi výsledek zpracování dotazu.
V následující tabulce je uveden přehled všech metod HTTP protokolu, přičemž nejpoužívanější jsou první dvě metody GET a POST, s jejichž použitím se seznámíme v demonstračních příkladech popsaných v navazujících kapitolách:
Metoda | Příklad použití |
---|---|
GET | Základní metoda sloužící k získání dat ze serveru. Může se například jednat o HTML stránku, statický obrázek, ale i výsledek volání REST API služby. |
POST | Metoda používaná pro odesílání dat na server. Teoreticky je sice možné použít i metodu GET, ovšem sémanticky je vhodnější použít tuto metodu (a REST API služby sémantiku operace většinou dodržují). |
PUT | Tato metoda slouží k nahrání dat na server. S touto metodou se setkáme u některých REST API služeb. |
DELETE | Slouží ke smazání dat ze serveru. Opět platí – s touto metodou se setkáme méně často u některých REST API služeb. |
HEAD | Tato metoda se částečně podobá metodě GET, ovšem server nevrátí požadovaná data, ale pouze metadata (čas změny, velikost dat, typ/formát dat apod.). Obecně je možné říci, že se tento dotaz zpracuje rychleji než GET. |
CONNECT | Používá se při použití TCP/IP tunelování. |
OPTIONS | Poslání dotazu na server, které metody podporuje. |
TRACE | Server by měl klientovi odeslat požadavek zpět, čehož se používá pro zjištění, které údaje se mění na přenosové cestě. |
PATCH | Umožňuje změnu dat na serveru, má tady jinou sémantiku než DELETE+PUT. |
Stavový kód odpovědi serveru je reprezentován celým číslem, přičemž z první číslice (stovky) lze odvodit základní vlastnost stavu (úspěch, chyba, přesměrování...):
Skupina stavových kódů | Význam |
---|---|
1xx | informační, potvrzení atd. (ovšem požadavek se prozatím nevykonal) |
2xx | úspěšné vyřízení požadavku popř. jeho akceptace serverem (202) |
3xx | přesměrování požadavku, informace o tom, že se objekt nezměnil atd. |
4xx | různé typy chyb typicky zaviněných klientem (bohužel nejrozsáhlejší skupina) |
5xx | různé chyby na serveru |
5. Nejjednodušší příklad – poslání požadavku GET na zvolenou adresu
V dnešním prvním demonstračním příkladu si ukážeme, jakým způsobem je možné použít základní HTTP metodu GET pro poslání požadavku na server. Použijeme přitom server dostupný na adrese https://httpbin.org/, který je možné využít pro otestování základních HTTP metod i jednoduchých REST API služeb. Konkrétně pošleme požadavek na adresu https://httpbin.org/get; v samotném požadavku nebudou předány žádné parametry. Dále se pokusíme stejný požadavek odeslat na neexistující URL https://httpbin.org/neexistuje:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/get"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# vypis objektu, ktery se vrati
print(response)
# nyni vyzkousime neexistujici endpoint:
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/neexistuje"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# vypis objektu, ktery se vrati
print(response)
Po spuštění tohoto příkladu by se na standardním výstupu (tj. na konzoli či na emulátoru terminálu) měly objevit pouhé dva řádky. První z nich by měl obsahovat textovou podobu prvního objektu typu Response, který představuje úspěšnou odpověď serveru s HTTP kódem 200. Druhý řádek by měl obsahovat textovou podobu objektu typu Response s HTTP kódem 404, což je ovšem pochopitelné, protože jsme se snažili přistoupit k neexistující URL:
<Response [200]> <Response [404]>
Poznámka: povšimněte si, že je možné bez problémů použít i protokol HTTPS namísto pouhého HTTP. Od této skutečnosti je programátor využívající knihovnu Requests většinou odstíněn.
6. Základní atributy objektu typu Response: stavový kód a indikátor korektní odpovědi
Ve druhém demonstračním příkladu, který bude opět velmi jednoduchý, si ukážeme, jakým způsobem je možné zjistit stav (HTTP status) předaný v odpovědi. Z předchozích kapitol, ale i z běžných zkušeností s browserem, víme, že stavový kód HTTP je představován celým číslem doplněným o textový popisek stavu. V objektu typu Response představujícího odpověď serveru je číselný kód stavu uložen v atributu pojmenovaném status_code. Kromě toho existuje ještě atribut nazvaný ok, který obsahuje pravdivostní hodnotu True v případě, že je číselný kód stavu menší než 400 a hodnotu False v opačném případě. Použití tohoto atributu tedy může být poněkud problematické, protože například některé stavy 3xx je možné v kontextu vyvíjené aplikace považovat za chybové stavy:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/get"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# vypis stavu odpovedi
print(response.status_code)
print(response.ok)
# nyni vyzkousime neexistujici endpoint:
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/neexistuje"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# vypis stavu odpovedi
print(response.status_code)
print(response.ok)
Podívejme se, jaké hodnoty se vypíšou pro korektní URL https://httpbin.org/get a jaké hodnoty pro nekorektní URL https://httpbin.org/neexistuje. Pro korektní URL získáme podle očekávání stavový kód 200 a atribut ok bude mít hodnotu True:
200 True
Pro nekorektní URL je stavový kód HTTP roven 404 a tím pádem je i atribut ok nastaven na hodnotu False:
404 False
7. Přečtení vybrané hlavičky z odpovědi HTTP serveru
Odpověď serveru ve formě pouhého HTTP stavu (číselného kódu) samozřejmě většinou není příliš přínosná (kromě dotazů na to, zda se nějaký zdroj nezměnil), protože serveru posíláme dotaz za účelem získání nějakých dat. Protokol HTTP je navržen takovým způsobem, že dokáže přenášet data v různých formátech, přičemž formát se rozpozná na základě hodnoty uložené do hlavičky content-type (opět viz předchozí kapitoly s popisem této hlavičky). Údaje ze všech hlaviček získaných z odpovědi serveru je samozřejmě možné získat, protože objekt typu Response obsahuje mj. i atribut headers, ve kterém jsou všechny hlavičky uloženy ve formě slovníku (jméno hlavičky:hodnota). V dnešním třetím demonstračním příkladu je ukázáno, jakým způsobem se přistupuje právě k hlavičce content-type obsahující typ/formát dat, které server odeslal klientovi:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/get"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# precteni hlavicek
headers = response.headers
# vypis typu internetoveho media
print(headers.get("content-type"))
Webová služba dostupná na adrese https://httpbin.org/get vrací hodnoty uložené v těle odpovědi, přičemž tyto hodnoty jsou reprezentovány ve známém a velmi často využívaném formátu JSON. Služba je tedy správně nakonfigurována takovým způsobem, že vrací typ dat:
application/json
8. Předání informací (parametrů) serveru přímo v URL
Serveru, jehož služby potřebujeme přes knihovnu Requests využívat, je samozřejmě možné předat nějaká data. Protokol HTTP podporuje dva základní způsoby předání dat. Pokud se jedná o několik parametrů s relativně malým (krátkým) obsahem, je možné takové parametry předat přímo v URL (v prohlížeči přes adresní řádek). Zápis URL by v takovém případě měl vypadat následovně:
protokol://adresa.serveru/endpoint?parametr1=hodnota1¶metr2=hodnota2¶metr2=hodnota2
Konkrétně v našem případě, kdy používáme testovací server https://httpbin.org/:
https://httpbin.org/get?x=6&y=7&answer=42
Tento způsob přináší některá omezení. Zejména je nutné zajistit, aby se ve jménech a hodnotách parametrů nevyskytovaly některé znaky, které slouží jako oddělovače ve vlastní URL. Touto problematikou, kterou lze opět řešit automaticky, se budeme zabývat příště. Také je nutné zajistit (a zjistit), zda server neomezuje délku URL, například na 1024 znaků atd.
Příklad, který serveru předá parametry přes URL, se prakticky žádným způsobem neodlišuje od prvního příkladu, takže jen ve stručnosti:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/get?x=6&y=7&answer=42"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# vypis objektu, ktery se vrati
print(response)
9. Přečtení těla odpovědi serveru v původní textové podobě
Ve chvíli, kdy serveru předáme nějaká data (či parametry), server typicky odpoví tak, že klientovi pošle zpět vyžadované údaje. V tomto případě není možné tyto údaje předat v URL (ta je jen součástí dotazu, nikoli odpovědi), takže všechna vyžadovaná data server pošle v těle odpovědi a popř. do hlaviček doplní potřebná metadata (například již zmíněný content-type apod.). V odpovědi, která je v knihovně Requests reprezentována objektem typu Response, lze k nezpracovaným datům přistupovat přes atribut text:
response = requests.get(URL) data = response.text
Opět se podívejme na jednoduchý demonstrační příklad, který testovacímu serveru pošle tři parametry x=6, y=7 a answer=42 a následně zobrazí odpověď serveru:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/get?x=6&y=7&answer=42"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# vypis tela odpovedi
print("Plain text:")
print("-" * 60) # horizontalni oddelovac
print(response.text)
print("-" * 60) # horizontalni oddelovac
Příklad odpovědi:
Plain text: ------------------------------------------------------------ { "args": { "answer": "42", "x": "6", "y": "7" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "origin": "213.175.37.10", "url": "https://httpbin.org/get?x=6&y=7&answer=42" } ------------------------------------------------------------
Poznámka: povšimněte si, že testovací server nám vlastně jen vrací údaje, které od nás získal, což je dobře, protože ho skutečně budeme moci použít pro další pokusy.
10. Získání metainformací o poslaných datech (typ a délka)
Zajímavé a v některých případech i užitečné bude zjištění základních metainformací o údajích, které nám server zaslal ve své odpovědi. Tyto metainformace se předávají formou hlaviček, a to zejména hlavičky content-type (typ/formát dat, již známe), content-length (délka dat) a popř. i date (datum vygenerování dat). Údaje z těchto hlaviček získáme velmi jednoduše, což je ostatně ukázáno i v pořadí již pátém demonstračním příkladu:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/get?x=6&y=7&answer=42"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# precteni hlavicek
headers = response.headers
print("Metadata:")
print("-" * 60)
# vypis typu internetoveho media
print("Typ dat:", headers.get("content-type"))
# vypis delky dat predanych v tele
print("Delka dat:", headers.get("content-length"))
# vypis delky dat predanych v tele
print("Datum:", headers.get("date"))
print("-" * 60)
# vypis tela odpovedi
print("Plain text:")
print("-" * 60)
print(response.text)
print("-" * 60)
Po spuštění tohoto demonstračního příkladu získáme přibližně následující výstup (ve vašem konkrétním případě bude odlišné datum a popř. i hlavička User-Agent):
Metadata: ------------------------------------------------------------ Typ dat: application/json Delka dat: 385 Datum: Sat, 04 Aug 2018 07:26:26 GMT ------------------------------------------------------------ Plain text: ------------------------------------------------------------ { "args": { "answer": "42", "x": "6", "y": "7" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate, compress", "Connection": "close", "Host": "httpbin.org", "User-Agent": "python-requests/2.2.1 CPython/3.4.3 Linux/3.13.0-139-lowlatency" }, "origin": "37.48.1.40", "url": "https://httpbin.org/get?x=6&y=7&answer=42" } ------------------------------------------------------------
11. Zpracování odpovědi, která byla vrácena ve formátu JSON
Mnoho webových služeb, především těch, které jsou postaveny na architektuře REST (Representational state transfer), vrací údaje ve formátu JSON. Přesněji řečeno – odpovědi serveru obsahují stavovou informaci, všechny potřebné hlavičky s metainformacemi a taktéž tělo představující data serializovaná právě do formátu JSON. Ve skutečnosti je zpracování těchto dat velmi jednoduché, protože lze využít metodu json() objektu typu Response. V případě, že server skutečně odeslal data ve formátu JSON, jsou tato data deserializována a vrácena programu ve formě seznamu či (častěji) slovníku. Následně je možné tato data zpracovat. V případě, že data nejsou ve formátu JSON, vyvolá se výjimka ValueError:
# zpracovani odpovedi, ktera prisla ve formatu JSON
data = response.json()
# celý desrializovaný obsah JSONu
print(data)
# vybraná část
args = data["args"]
print(args)
print("x =", args["x"])
print("y =", args["y"])
print("answer =", args["answer"])
Zpracování údajů vrácených testovacím serverem https://httpbin.org/ je ukázáno v dnešním šestém demonstračním příkladu:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/get?x=6&y=7&answer=42"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# zpracovani odpovedi, ktera prisla ve formatu JSON
data = response.json()
print(data)
args = data["args"]
print(args)
print("x =", args["x"])
print("y =", args["y"])
print("answer =", args["answer"])
Po spuštění tohoto příkladu by se nejprve měl vypsat obsah celého těla odpovědi (deserializovaný z JSONu):
{'args': {'answer': '42', 'x': '6', 'y': '7'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.13.0'}, 'origin': '213.175.37.10', 'url': 'https://httpbin.org/get?x=6&y=7&answer=42'}
Následně by se měly vypsat jen vybrané údaje:
{'answer': '42', 'x': '6', 'y': '7'} x = 6 y = 7 answer = 42
12. Použití HTTP metody POST
Kromě metody GET protokolu HTTP je samozřejmě možné použít i metodu POST. Tato metoda se typicky používá ve chvíli, kdy je zapotřebí předat serveru větší množství parametrů a/nebo rozsáhlejších dat. Existuje několik způsobů, jak tato data předávat, ovšem v prvním příkladu, v němž metodu POST použijeme, se žádná data prozatím předávat nebudou. Úplný zdrojový kód tohoto příkladu vypadá následovně:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/post"
# poslani HTTP dotazu typu POST
response = requests.post(URL)
# vypis odpovedi v plain textu
print(response.text)
Zajímavá je odpověď serveru. Povšimněte si především toho, že nám server vrátil klíč form a json. K těmto klíčům se dostaneme později:
{ "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "0", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "json": null, "origin": "213.175.37.10", "url": "https://httpbin.org/post" }
13. Předání dat serveru ve „formuláři“
První metoda poslání parametrů od klienta na server používá takzvané „formulářové položky“. Tento poněkud nepřesný název je odvozen od toho, že se podobným způsobem posílají data z HTML formuláře (bez použití JavaScriptu, pouze čistě HTML prostředky). Pokud budeme chtít simulovat posílání dat tímto způsobem, můžeme použít nepovinný parametr data funkce requests.post():
payload = { "klic": "hodnota", "answer": 42, "question": None, "correct": True} # poslani HTTP dotazu typu POST se specifikaci hodnot formulare response = requests.post(URL, data=payload)
Tento způsob je použit i v dnešním osmém demonstračním příkladu, jehož úplný zdrojový kód vypadá následovně:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/post"
payload = {
"klic": "hodnota",
"answer": 42,
"question": None,
"correct": True}
# poslani HTTP dotazu typu POST se specifikaci hodnot formulare
response = requests.post(URL, data=payload)
# vypis tela odpovedi v plain textu
print(response.text)
Odpověď serveru opět obsahuje položku form. Povšimněte si, že server získal a následně vrátil pouze tři hodnoty – chybí zde ovšem hodnota question=None, která se ve skutečnosti ve formulářových datech nepředala (neexistuje zde totiž ekvivalent pro speciální hodnoty None či null):
{ "args": {}, "data": "", "files": {}, "form": { "answer": "42", "correct": "True", "klic": "hodnota" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "35", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "json": null, "origin": "213.175.37.10", "url": "https://httpbin.org/post" }
14. Předání dat serveru v těle požadavku
Pokud budeme chtít serveru předat větší množství dat, a to potenciálně včetně speciálních hodnot, je lepší takové údaje předat přímo v těle požadavku. Pro tento účel se ve funkci requests.post() použije nepovinný parametr nazvaný json a nikoli parametr pojmenovaný data (jako tomu bylo v příkladu předchozím). Opět se podívejme na jednoduchý demonstrační příklad, který se od příkladu předchozího odlišuje pouze v jediném detailu – nepovinném parametru funkce requests.post():
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa s testovaci REST API sluzbou
URL = "https://httpbin.org/post"
payload = {
"klic": "hodnota",
"answer": 42,
"question": None,
"correct": True}
# poslani HTTP dotazu typu POST s telem
response = requests.post(URL, json=payload)
# vypis tela odpovedi v plain textu
print(response.text)
Odpověď serveru nyní vypadá odlišně, protože nám testovací server v odpovědi říká, jak parametry získal (resp. jak mu byly předány). Povšimněte si, že nyní je pod klíčem form uložen prázdný slovník, ovšem naše parametry jsou nyní předány pod klíčem data a současně i ve zpracované podobě pod klíčem json:
{ "args": {}, "data": "{"klic": "hodnota", "answer": 42, "question": null, "correct": true}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "68", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "python-requests/2.13.0" }, "json": { "answer": 42, "correct": true, "klic": "hodnota", "question": null }, "origin": "213.175.37.10", "url": "https://httpbin.org/post" }
15. Vlastní jednoduchý testovací HTTP server
Aby bylo možné zjistit, jak přesně vlastně vypadá požadavek posílaný z klienta na server pomocí metody GET nebo POST, vytvoříme si vlastní velmi jednoduchou implementaci HTTP serveru založenou na existující (velmi užitečné) třídě BaseHTTPRequestHandler. Ta samozřejmě nebude v žádném případě určena pro produkční nasazení, protože například nijak neřeší HTTPS, souběžné zpracování většího množství požadavků, zabezpečení, autorizaci, kontrolu korektnosti požadavků atd. Server pouze velmi jednoduše zpracuje všechny požadavky typu GET a POST; klientovi přitom vrátí odpověď se stavem 200 OK a jednořádkovou (plain textovou) zprávou, která klienta pouze informuje o tom, jakou HTTP metodu při volání serveru použil. Zcela základní implementace serveru by tedy mohla vypadat následovně (bez importů, spuštění atd.):
class SimpleServer(BaseHTTPRequestHandler):
def do_GET(self):
# priprava hlavicky odpovedi
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
# odpoved serveru klientovi
self.wfile.write("*** get ***".encode("utf-8"))
def do_POST(self):
# priprava hlavicky odpovedi
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
# odpoved serveru klientovi
self.wfile.write("*** post ***".encode("utf-8"))
16. Implementace HTTP serveru
Ve skutečnosti bude implemetace našeho testovacího serveru nepatrně komplikovanější, protože budeme potřebovat, aby se u metody POST získalo i tělo požadavku, které může obsahovat data poslaná klientem. To se dá provést relativně jednoduše – nejprve zjistíme délku obsahu (v bajtech) a posléze tento obsah načteme metodou rfile.read(), které se předá délka těla požadavku:
content_length = int(self.headers['Content-Length'])
print("content length: {len}".format(len=content_length))
content = self.rfile.read(content_length)
Následně může server tato data zobrazit ve svém terminálu či do logovacího souboru, což je přesně to, co potřebujeme – získat nezpracovaný formát požadavku vytvořený knihovnou Requests.
Úplná implementace našeho HTTP serveru je založena na zdrojovém kódu, který byl poslán na https://gist.github.com/bradmontgomery/2219997, ovšem provedl jsem v něm několik úprav a oprav. Výsledek je použitelný s Pythonem 3.x:
#!/usr/bin/python3
# vim: set fileencoding=utf-8
# Original (slightly buggy) code:
# see https://gist.github.com/bradmontgomery/2219997
import socket
from http.server import BaseHTTPRequestHandler, HTTPServer
hostName = ""
hostPort = 8000
class SimpleServer(BaseHTTPRequestHandler):
def do_GET(self):
# priprava hlavicky odpovedi
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
# odpoved serveru klientovi
self.wfile.write("*** get ***".encode("utf-8"))
def do_POST(self):
print("URI: {uri}".format(uri=self.path))
# precteni tela HTTP pozadavku
content_length = int(self.headers['Content-Length'])
print("content length: {len}".format(len=content_length))
content = self.rfile.read(content_length)
print("content value: {content}".format(content=content))
# priprava hlavicky odpovedi
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
# odpoved serveru klientovi
self.wfile.write("*** post ***".encode("utf-8"))
simpleServer = HTTPServer((hostName, hostPort), SimpleServer)
try:
simpleServer.serve_forever()
except KeyboardInterrupt:
pass
simpleServer.server_close()
17. Volání vlastního HTTP serveru s využitím knihovny Requests
Výše popsaný HTTP server zavoláme celkem třikrát – jednou se použije metoda GET, podruhé metoda POST s předáním „formulářových dat“ a nakonec se opět použije metoda POST, ovšem tentokrát se data předají v těle požadavku s využitím formátu JSON:
#!/usr/bin/env python3
# vim: set fileencoding=utf-8
import requests
# adresa lokalne beziciho serveru
URL = "http://localhost:8000"
# poslani HTTP dotazu typu GET
response = requests.get(URL)
# vypis zakladnich informaci ziskanych z odpovedi
print(response)
print(response.status_code)
print(response.ok)
print(response.text)
payload = {
"klic": "hodnota",
"answer": 42,
"question": None,
"correct": True}
# poslani dat jako hodnot formulare
response = requests.post(URL, data=payload)
print(response.text)
# poslani dat v tele dotazu
response = requests.post(URL, json=payload)
print(response.text)
Na konzoli, ze které spouštíme testovací skript, se vypíšou tyto údaje. První čtyři řádky platí pro první volání GET, další dva pro volání POST:
<Response [200]> 200 True *** get *** *** post *** *** post ***
Na konzoli serveru (ovšem nikoli na konzoli, kde spouštíme testovací skript!) by se měly vypsat následující řádky, z nichž je patrný jak formát požadavku typu GET, tak i formát požadavku typu POST při předávání údajů přes formulářová data a nakonec formát požadavku předaného v těle (JSON):
127.0.0.1 - - [03/Aug/2018 13:57:57] "GET / HTTP/1.1" 200 - URI: / content length: 35 content value: b'klic=hodnota&answer=42&correct=True' 127.0.0.1 - - [03/Aug/2018 13:57:57] "POST / HTTP/1.1" 200 - URI: / content length: 68 content value: b'{"klic": "hodnota", "answer": 42, "question": null, "correct": true}' 127.0.0.1 - - [03/Aug/2018 13:57:57] "POST / HTTP/1.1" 200 -
18. Obsah druhé části seriálu
Ve druhé části seriálu o nejpopulárnějších a nejužitečnějších knihovnách určených pro vývojáře používající programovací jazyk Python dokončíme popis možností nabízených knihovnou Requests. Zaměříme se především na poněkud složitější techniky – autorizaci, využití session atd. Uvidíme, že tato knihovna většinu těchto operací přímo podporuje, a to takovým způsobem, aby bylo její použití pro vývojáře přímočaré a jednoduché (v porovnání s některými dalšími knihovnami s podobným zaměřením).
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů určených pro Python 3 a knihovnu Requests byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- Requests: HTTP for Humans (dokumentace)
http://docs.python-requests.org/en/master/ - Requests: Introduction
http://docs.python-requests.org/en/latest/user/intro/ - Requests na GitHubu
https://github.com/requests/requests - Requests (software) na Wikipedii
https://en.wikipedia.org/wiki/Requests_%28software%29 - Pip (dokumentace)
https://pip.pypa.io/en/stable/ - 20 Python libraries you can’t live without
https://pythontips.com/2013/07/30/20-python-libraries-you-cant-live-without/ - What are the top 10 most useful and influential Python libraries and frameworks?
https://www.quora.com/What-are-the-top-10-most-useful-and-influential-Python-libraries-and-frameworks - Python: useful modules
https://wiki.python.org/moin/UsefulModules - Top 15 most popular Python libraries
https://keyua.org/blog/most-popular-python-libraries/ - Hypertext Transfer Protocol
https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol - List of HTTP header fields
https://en.wikipedia.org/wiki/List_of_HTTP_header_fields - List of HTTP status codes
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes - Python requests deep dive
https://medium.com/@anthonypjshaw/python-requests-deep-dive-a0a5c5c1e093 - The awesome requests module
https://www.pythonforbeginners.com/requests/the-awesome-requests-module - Send HTTP Requests in Python
https://code-maven.com/http-requests-in-python - Introducing JSON
http://json.org/
Autor: Pavel Tišnovský 2018
9. 8. 2018 at 11:58
coooll. diky!