Po projití této lekce byste měli být obeznámeni se základním použitím knihoven
requests
a click
. Skončíme s programem, který bude umět převádět peníze z
českých korun do jiných měn podle aktuálního kurzu.
Předpokládáme základní znalost Pythonu. Měli byste mít počítač s nainstalovaným interpretem jazyka Python ve verzi aspoň 3.6. Pro začátek si také vytvořte nové virtuální prostředí.
Dále se vám bude hodit základní přehled o tom, jak funguje internet, co je to URL a podobné drobnosti. Pokud si nejste jistí, začněte tímto shrnutím pro začátečníky.
Začneme seznámením s knihovnou requests. Je to knihovna určená pro HTTP požadavky na straně klienta. Poskytuje mnohem pohodlnější rozhraní než standardní knihovna Pythonu.
Prvním krokem by měla být instalace ve virtuálním prostředí:
(venv) $ python -m pip install requests
První pokus je ideální provádět v interaktivní konzoli Pythonu. Začneme tím, že
si naimportujeme modul requests
. Komunikace přes protokol HTTP používá model
požadavek/odpověď (request/response). Klient tedy nejprve pošle požadavek,
a server potom odpovídá. Takto se střídají, dokud klient nemá vše, co
potřebuje, nebo nedojde k chybě.
Pro začátek se podíváme na stránku https://example.com
.
>>> import requests
>>> response = requests.get("https://example.com/")
>>> response
<Response [200]>
Takto vypsaná odpověď není příliš užitečná. To naštěstí není zase takový
problém. V proměnné response
teď máme object, který má potřebná data uložená
v různých atributech.
Zkuste si vypsat, co obsahují atributy response.text
, response.status_code
,
response.encoding
a response.history
. Taky vyzkoušejte zavolat metodu
response.json()
. Existuje jich mnohem více, ale tyto jsou docela zajímavé a
relativně často užívané.
Na tyto experimenty použijte dvě jiné adresy (protože example.com
není příliž
zajímavý web).
https://httpbin.org/get
https://httpbin.org/redirect-to?url=http://example.com&status_code=301
https://httpbin.org/ je velice užitečná služba, pokud si potřebujete vyzkoušet komunikaci přes HTTP. Bude vám odpovídat na všemožné požadavky podle toho, jak si řeknete. Podívejte se v prohlížeči a uvidíte docela pěkný seznam všech možností (akorát v angličtině)
Pojďme se tedy podívat, co dělají zmíněné jednotlivé atributy:
Atribut text
obsahuje tělo odpovědi, tak jak nám oze serveru přišla. Pro
většinu stránek to bude kód v jazyku HTML, nebo v data v různých formátech.
Každá odpověď od serveru obsahuje číselný kód, který popisuje výsledek akce.
Tento kód si můžete přečíst z atributu status_code
. 1xx
jsou informační
zprávy, na které moc často nenarazíte. 2xx
jsou úspěšné odpovědi. Někdy se
může stát, že server místo odpovědi, kterou chcete, odešle přesměrování. To
má podobu odpovědi s kódem 3xx
. Přímo tuto odpověď neuvidíte, protože
knihovna requests
ví, že je to přesměrování a proto automaticky půjde na
adresu, kam vás server poslal.
Ke každému číselnému kódu existuje i texotvý popis. Ty najdete třeba na Wikipedii, nebo můžete použít https://http.cat.
Pokud dojde k přesměrování (a může jich být i několik), můžete se podívat na
jednotlivé odpovědi v atributu history
. Je to seznam, který bude pro každé
přesměrování obsahovat jeden objekt.
Atribut encoding
je užitečný v případě, že vám správně nefungují české znaky
v odpovědi. Můžete se v něm podívat, co vám server tvrdí o datech, která vám
posílá.
Nakonec nám zůstává metoda json()
. JSON je datový formát, který používá mnoho
různých webových služeb. Proto requests
nabízí tuto zkratku, jak se k datům
dostat. Ale pozor! Pokud v odpovědit nejsou data v tomto formátu, dostanete
chybu! (A toto je očekávané chování u druhé testovací URL.)
Ve druhé testovací URL si můžete všimnout, že obsahuje otazník a za ním nějaké další informace. Toto jsou parametry pro server, které mu říkají, co přesně od něj chceme. Typický příklad ze života je vyhledávací políčko na libovolném webu. Vyhledávaná fráze se na server stejným způsobem jako parametr.
Ruční zpracování a přilepení k samotné URL ale není úplně jednoduché. Musíte
myslet na to, že některé znaky je potřeba zakódovat. Proto requests
poskytují
lepší možnost, jak s parametry pracovat.
Můžeme si nadefinovat slovník, kde klíče budou názvy parametrů (které obvykle závisí na tom, co server očekává), a hodnoty budou samotná data, která chceme posílat.
>>> parametry = {"status_code": 301, "url": "https://example.com"}
>>> r = requests.get("https://httpbin.org/redirect-to", params=parametry)
V tomto případě httpbin potřebuje informaci o tom, kam a jak nás má přesměrovat.
Knihovna requests
umí data nejenom přijímat, ale i posílat. K tomu slouží
metoda post()
.
Jendoduchý příklad je:
>>> r = requests.post("https://httpbin.org/post", {"ahoj": "svete"})
V praxi bývá často potřeba řešit situaci, že server vyžaduje přihlášení. A tam
je potřeba pracovat případ od případu. Každopádně knihovna requests
vám
umožní použít všechny obvyklé přihlašovací metody.
Jeden detail, který je poměrně snadné přehlédnout, je to, že všechny příklady výše provedou požadavek, a potom stáhnou celou odpoveď a uloží ji v paměti počítače. To je v pohodě, pokud to je něco relativně malého. Pokud budete stahovat třeba video, úplně fajn to není. Proto můžete použít tento recept, který vytvoří spojení se serverem, potom čte kousky dat po 8 kilobajtech a rovnou je zapisuje do souboru.
import requests
with requests.get("https://placekitten.com/400/600") as r:
r.raise_for_status()
with open("kitten.jpg", "w") as f:
for chunk in r.iter_content(8196):
if chunk:
f.write(chunk)
Za vypíchnutí tady stojí jedna nová metoda: raise_for_status()
. Po provedení
požadavku je potřeba zkontrolovat, jestli se nám to podařilo. Klasicky se to
dělá kontrolou hodnoty atributu status_code
. Metoda raise_for_status()
je
zkratka: pokud nám server vrátil nějakou chybu, tato metoda vyhodí výjimku,
kterou můžeme zpracovat. Pro úspěšnou odpověď tato metoda neědělá nic.
Česká národní banka zveřejňuje denní kurzy, které je možné si stáhnout. Navíc jsou v pěkném textovém formátu, se kterým se nám bude pěkně pracovat.
Adresa je http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt?date=01.04.2019. Datum je ve formátu den.měsíc.rok. Pokud datum nezadáte nebo je špatně, dostanete poslední kurzy.
Napište si funkci, která dostane jeden argument: datum ve správném formátu (jako řetezec). Tato funkce stáhne kurzovní lístek a vrátí data v libovolné podobě, se kterou se nám bude dále pracovat.
Mohla by se vám hodit tato funkce, která přečte textovou odpoveď, rozseká ji na kousky a vrátí slovník. Klíče jsou zkratky měn, hodnoty jsou kurzy.
def parse_rates(text):
hlavicka, jmena, *radky = text.splitlines()
kurzy = {}
for radek in radky:
_, _, castka, mena, hodnota = radek.replace(",", ".").split("|")
kurzy[mena] = float(castka) / float(hodnota)
return kurzy
Řešení najdete na konci této stránky.
Když instalujete knihovnu, zadáváte příkaz python -m pip install foo
. V tomto
případě python
je název příkazu, který chcete spustit, a ostatní slova na
tomto řádku (oddělená mezerami), jsou argumenty tohoto příkazu.
Dříve nebo později narazíte na to, že vaše programy budou potřebovat nějaký
vstup od uživatele. Číst je vždy přes funkci input()
není úplně pohodlné ani
pro uživatele, ani pro programátora. Proto je dobré vědět, jak definovat a
používat argumenty.
Existuje hodně knihoven, které umožňují zpracovávat argumenty na příkazové
řádce. Jenom samotná standardní knihovna Pythonu má getopt
, optparse
a
argparse
. Ty ale nejsou úplně příjemné na používání.
Oproti tomu knihovna click poskytuje rozhraní, ve kterém můžete jednoduché programy sekat jako Baťa cvičky. Cenou je lehce magický způsob, jak argumenty definovat, a taky ztráta možnosti ovlivnit do nejjemnějších detailů, jak se program má chovat. To ale obvykle není problém.
Různé systémy používají různé konvence, jak by měly argumenty vypadat a fungovat. Tady si popíšeme, jak se slušně vychované programy chovají na Linuxu (nebo na Macu).
Existují dvě základní kategorie: argumenty a přepínače. Argumenty jsou většinou (ale ne vždy) vyžadované, přepínače obvykle potřeba nejsou. Argumenty jsou dané pořadím (pokud jich je víc), přepínače mají jména.
Jména přepínačů obvykle začínají dvěmi pomlčkami, pokud mají hezké čitelné jméno, nebo jednou pomlčkou, pokud je to jenom jedno písmeno. Dost často jeden přepínač může mít jak jednopísmenné jméno, tak i delší a čitelnější.
Nic překvapivého:
(venv) $ python -m pip install click
Na tomto jednoduchém programu si ukážeme, jak se dá funkce změnit v něco, co bude pěkně použitelné na příkazové řádce.
import click
@click.command()
@click.option("--kolikrat", default=1, help="Kolikrát budeme zdravit")
@click.option("--jmeno", prompt="Tvoje jméno",
help="Koho budeme zdravit")
def hello(kolikrat, jmeno):
for x in range(kolikrat):
click.echo(f"Ahoj {jmeno}!")
if __name__ == "__main__":
hello()
Funguje to takto:
(venv) $ python hello.py --kolikrat 3 --jmeno Adame
Ahoj Adame!
Ahoj Adame!
Ahoj Adame!
Příkazům začínajícím zavináčem před definicí funkce říkáme dekorátory. Je to možnost, jak v Pythonu můžeme ovlivnit chování funkce (a pravděpodobně se jim budeme věnovat trochu více v některé následující lekci).
První řádek @click.command()
říká, že následující funkce by se měla chovat
jako příkaz.
Další dva řádky definují přepínače tohoto příkazu.
První z nich se jmenuje --kolikrat
, a pokud ho nezadáme, dostane výchozí
hodnotu 1. Click z této výchozí hodnoty pozná, že hodnotou toho přepínače
bude vždy číslo. Takže když zkusíme zadat jiný text, dostaneme chybu. Argument
předaný do funkce hello()
bude už typu int
.
Druhý argument bude jméno. Typ nijak nespecifikuje, takže to bude řetězec.
prompt
říká, že pokud přepínač nezadáme, program se nás zeptá.
Zkuste si s tímto programem chvilku hrát. Nezapomeňte, že click vypíše pěknou
nápovědu, pokud program spustíte s přepínačem --help
.
Možné typy přepínačů (použití: @click.option(…, type=click.X, …
):
click.INT
– celé čísloclick.FLOAT
– číslo s desetinnou tečkouclick.FILE
– název souboru na příkazové řádce, ale funkce už dostane
otevřený soubor a click se sám postará i o zavřeníDalší možnosti jsou třeba multiple=True
. Tím přepínač změníme tak, že ho bude
možné zadávat několikrát. Funkce potom dostane n-tici hodnot.
Argumenty se definují velmi podobně jako přepínače. Jediný rozdíl je v použitém
dekorátoru @click.argument()
. Jména argumentů se zadávají bez úvodních
pomlček.
Click taky umožňuje vypisování na výstup. click.echo
se chová velmi podobně
jako print
, akorát se snaží lépe fungovat, pokud máte rozbitý terminál.
Napište program, který bude vypisovat tuto nápovědu:
(venv) $ python cnb.py --help
Usage: cnb.py [OPTIONS] CASTKA
Options:
--datum TEXT
--mena TEXT může být zadaný vícekrát
--help Show this message and exit.
Zkombinujte výsledky obou cvičení do jednoho programu. Tento program bude vyžadovat jedno číslo. To bude částka v korunách. Program načte buď kurzy podle zadaného data, nebo poslední zveřejněné.
Pokud nebude zadaná žádná měna, program převede částku do všech dostupných měn a vypíše je v nějakém pěkném formátu. Pokud budou nějaké měny zadané, bude převádět jen do nich.
Zkus si ale cvičení nejdřív vyřešit bez pomoci :)
import click
import requests
def parse_rates(text):
hlavicka, jmena, *radky = text.splitlines()
kurzy = {}
for radek in radky:
_, _, castka, mena, hodnota = radek.replace(",", ".").split("|")
kurzy[mena] = float(castka) / float(hodnota)
return kurzy
def get_exchange_rates(datum=None):
parametry = {}
if datum:
# Pokud máme datum, použijeme ho. Prázdný slovník parametrů nemá na
# výsledek žádný vliv.
parametry["date"] = datum
response = requests.get(
"http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt",
params=parametry,
)
return parse_rates(response.text)
@click.command()
@click.option("--datum")
@click.option("--mena", multiple=True)
@click.argument("castka", type=click.FLOAT)
def cnb(castka, datum, mena):
kurzy = get_exchange_rates(datum)
for zkratka_meny in sorted(kurzy):
# Pokud nemáme žádné měny, nebo tato měna byla zadaná …
if not mena or zkratka_meny in mena:
# … tak převedeme částku a vypíšeme ji.
prevedeno = castka * kurzy[zkratka_meny]
click.echo(f"{castka} CZK = {prevedeno} {zkratka_meny}")
if __name__ == "__main__":
cnb()
{ "data": { "sessionMaterial": { "id": "session-material:2019/brno-jaro-knihovny:requests-click:1", "title": "Requests a Click", "html": "\n \n \n\n <h1>Requests + Click</h1>\n<h2>Co je cílem tohoto cvičení?</h2>\n<p>Po projití této lekce byste měli být obeznámeni se základním použitím knihoven\n<code>requests</code> a <code>click</code>. Skončíme s programem, který bude umět převádět peníze z\nčeských korun do jiných měn podle aktuálního kurzu.</p>\n<h2>Předpoklady</h2>\n<p>Předpokládáme základní znalost Pythonu. Měli byste mít počítač s nainstalovaným\ninterpretem jazyka Python ve verzi aspoň 3.6. Pro začátek si také vytvořte nové\nvirtuální prostředí.</p>\n<p>Dále se vám bude hodit základní přehled o tom, jak funguje internet, co je to\nURL a podobné drobnosti. Pokud si nejste jistí, začněte <a href=\"/2019/brno-jaro-knihovny/fast-track/http/\">tímto shrnutím pro\nzačátečníky</a>.</p>\n<h2>Requests</h2>\n<p>Začneme seznámením s knihovnou <a href=\"http://docs.python-requests.org/en/master/\">requests</a>. Je to knihovna určená pro HTTP\npožadavky na straně klienta. Poskytuje mnohem pohodlnější rozhraní než\nstandardní knihovna Pythonu.</p>\n<p>Prvním krokem by měla být instalace ve virtuálním prostředí:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(venv) $ </span>python -m pip install requests\n</pre></div><p>První pokus je ideální provádět v interaktivní konzoli Pythonu. Začneme tím, že\nsi naimportujeme modul <code>requests</code>. Komunikace přes protokol HTTP používá model\npožadavek/odpověď (<em>request</em>/<em>response</em>). Klient tedy nejprve pošle požadavek,\na server potom odpovídá. Takto se střídají, dokud klient nemá vše, co\npotřebuje, nebo nedojde k chybě.</p>\n<p>Pro začátek se podíváme na stránku <code>https://example.com</code>.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">>>> </span><span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"gp\">>>> </span><span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">requests</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">"https://example.com/"</span><span class=\"p\">)</span>\n<span class=\"gp\">>>> </span><span class=\"n\">response</span>\n<span class=\"go\"><Response [200]></span>\n</pre></div><p>Takto vypsaná odpověď není příliš užitečná. To naštěstí není zase takový\nproblém. V proměnné <code>response</code> teď máme object, který má potřebná data uložená\nv různých atributech.</p>\n<p>Zkuste si vypsat, co obsahují atributy <code>response.text</code>, <code>response.status_code</code>,\n<code>response.encoding</code> a <code>response.history</code>. Taky vyzkoušejte zavolat metodu\n<code>response.json()</code>. Existuje jich mnohem více, ale tyto jsou docela zajímavé a\nrelativně často užívané.</p>\n<p>Na tyto experimenty použijte dvě jiné adresy (protože <code>example.com</code> není příliž\nzajímavý web).</p>\n<ul>\n<li><code>https://httpbin.org/get</code></li>\n<li><code>https://httpbin.org/redirect-to?url=http://example.com&status_code=301</code></li>\n</ul>\n<div class=\"admonition note\"><p><a href=\"https://httpbin.org/\">https://httpbin.org/</a> je velice užitečná služba, pokud si potřebujete\nvyzkoušet komunikaci přes HTTP. Bude vám odpovídat na všemožné požadavky\npodle toho, jak si řeknete. Podívejte se v prohlížeči a uvidíte docela pěkný\nseznam všech možností (akorát v angličtině)</p>\n</div><p>Pojďme se tedy podívat, co dělají zmíněné jednotlivé atributy:</p>\n<p>Atribut <code>text</code> obsahuje tělo odpovědi, tak jak nám oze serveru přišla. Pro\nvětšinu stránek to bude kód v jazyku HTML, nebo v data v různých formátech.</p>\n<p>Každá odpověď od serveru obsahuje číselný kód, který popisuje výsledek akce.\nTento kód si můžete přečíst z atributu <code>status_code</code>. <code>1xx</code> jsou informační\nzprávy, na které moc často nenarazíte. <code>2xx</code> jsou úspěšné odpovědi. Někdy se\nmůže stát, že server místo odpovědi, kterou chcete, odešle <em>přesměrování</em>. To\nmá podobu odpovědi s kódem <code>3xx</code>. Přímo tuto odpověď neuvidíte, protože\nknihovna <code>requests</code> ví, že je to přesměrování a proto automaticky půjde na\nadresu, kam vás server poslal.</p>\n<p>Ke každému číselnému kódu existuje i texotvý popis. Ty najdete třeba na\n<a href>Wikipedii</a>, nebo můžete použít <a href=\"https://http.cat\">https://http.cat</a>.</p>\n<p>Pokud dojde k přesměrování (a může jich být i několik), můžete se podívat na\njednotlivé odpovědi v atributu <code>history</code>. Je to seznam, který bude pro každé\npřesměrování obsahovat jeden objekt.</p>\n<p>Atribut <code>encoding</code> je užitečný v případě, že vám správně nefungují české znaky\nv odpovědi. Můžete se v něm podívat, co vám server tvrdí o datech, která vám\nposílá.</p>\n<p>Nakonec nám zůstává metoda <code>json()</code>. JSON je datový formát, který používá mnoho\nrůzných webových služeb. Proto <code>requests</code> nabízí tuto zkratku, jak se k datům\ndostat. Ale pozor! Pokud v odpovědit nejsou data v tomto formátu, dostanete\nchybu! (A toto je očekávané chování u druhé testovací URL.)</p>\n<h3>Parametry pro GET</h3>\n<p>Ve druhé testovací URL si můžete všimnout, že obsahuje otazník a za ním nějaké\ndalší informace. Toto jsou parametry pro server, které mu říkají, co přesně od\nněj chceme. Typický příklad ze života je vyhledávací políčko na libovolném\nwebu. Vyhledávaná fráze se na server stejným způsobem jako parametr.</p>\n<p>Ruční zpracování a přilepení k samotné URL ale není úplně jednoduché. Musíte\nmyslet na to, že některé znaky je potřeba zakódovat. Proto <code>requests</code> poskytují\nlepší možnost, jak s parametry pracovat.</p>\n<p>Můžeme si nadefinovat slovník, kde klíče budou názvy parametrů (které obvykle\nzávisí na tom, co server očekává), a hodnoty budou samotná data, která chceme\nposílat.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">>>> </span><span class=\"n\">parametry</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s2\">"status_code"</span><span class=\"p\">:</span> <span class=\"mi\">301</span><span class=\"p\">,</span> <span class=\"s2\">"url"</span><span class=\"p\">:</span> <span class=\"s2\">"https://example.com"</span><span class=\"p\">}</span>\n<span class=\"gp\">>>> </span><span class=\"n\">r</span> <span class=\"o\">=</span> <span class=\"n\">requests</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">"https://httpbin.org/redirect-to"</span><span class=\"p\">,</span> <span class=\"n\">params</span><span class=\"o\">=</span><span class=\"n\">parametry</span><span class=\"p\">)</span>\n</pre></div><p>V tomto případě <em>httpbin</em> potřebuje informaci o tom, kam a jak nás má\npřesměrovat.</p>\n<h3>Posílání dat</h3>\n<p>Knihovna <code>requests</code> umí data nejenom přijímat, ale i posílat. K tomu slouží\nmetoda <code>post()</code>.</p>\n<p>Jendoduchý příklad je:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">>>> </span><span class=\"n\">r</span> <span class=\"o\">=</span> <span class=\"n\">requests</span><span class=\"o\">.</span><span class=\"n\">post</span><span class=\"p\">(</span><span class=\"s2\">"https://httpbin.org/post"</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"s2\">"ahoj"</span><span class=\"p\">:</span> <span class=\"s2\">"svete"</span><span class=\"p\">})</span>\n</pre></div><p>V praxi bývá často potřeba řešit situaci, že server vyžaduje přihlášení. A tam\nje potřeba pracovat případ od případu. Každopádně knihovna <code>requests</code> vám\numožní použít všechny obvyklé přihlašovací metody.</p>\n<h3>Stažení velkého souboru</h3>\n<p>Jeden detail, který je poměrně snadné přehlédnout, je to, že všechny příklady\nvýše provedou požadavek, a potom stáhnou celou odpoveď a uloží ji v paměti\npočítače. To je v pohodě, pokud to je něco relativně malého. Pokud budete\nstahovat třeba video, úplně fajn to není. Proto můžete použít tento recept,\nkterý vytvoří spojení se serverem, potom čte kousky dat po 8 kilobajtech a\nrovnou je zapisuje do souboru.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"k\">with</span> <span class=\"n\">requests</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">"https://placekitten.com/400/600"</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">r</span><span class=\"p\">:</span>\n <span class=\"n\">r</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n <span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s2\">"kitten.jpg"</span><span class=\"p\">,</span> <span class=\"s2\">"w"</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">f</span><span class=\"p\">:</span>\n <span class=\"k\">for</span> <span class=\"n\">chunk</span> <span class=\"ow\">in</span> <span class=\"n\">r</span><span class=\"o\">.</span><span class=\"n\">iter_content</span><span class=\"p\">(</span><span class=\"mi\">8196</span><span class=\"p\">):</span>\n <span class=\"k\">if</span> <span class=\"n\">chunk</span><span class=\"p\">:</span>\n <span class=\"n\">f</span><span class=\"o\">.</span><span class=\"n\">write</span><span class=\"p\">(</span><span class=\"n\">chunk</span><span class=\"p\">)</span>\n</pre></div><p>Za vypíchnutí tady stojí jedna nová metoda: <code>raise_for_status()</code>. Po provedení\npožadavku je potřeba zkontrolovat, jestli se nám to podařilo. Klasicky se to\ndělá kontrolou hodnoty atributu <code>status_code</code>. Metoda <code>raise_for_status()</code> je\nzkratka: pokud nám server vrátil nějakou chybu, tato metoda vyhodí výjimku,\nkterou můžeme zpracovat. Pro úspěšnou odpověď tato metoda neědělá nic.</p>\n<h3>Cvičení</h3>\n<p>Česká národní banka zveřejňuje denní kurzy, které je možné si stáhnout. Navíc\njsou v pěkném textovém formátu, se kterým se nám bude pěkně pracovat.</p>\n<p>Adresa je\n<a href=\"http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt?date=01.04.2019\">http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt?date=01.04.2019</a>.\nDatum je ve formátu den.měsíc.rok. Pokud datum nezadáte nebo je špatně,\ndostanete poslední kurzy.</p>\n<p>Napište si funkci, která dostane jeden argument: datum ve správném formátu\n(jako řetezec). Tato funkce stáhne kurzovní lístek a vrátí data v libovolné\npodobě, se kterou se nám bude dále pracovat.</p>\n<p>Mohla by se vám hodit tato funkce, která přečte textovou odpoveď, rozseká ji na\nkousky a vrátí slovník. Klíče jsou zkratky měn, hodnoty jsou kurzy.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">parse_rates</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"n\">hlavicka</span><span class=\"p\">,</span> <span class=\"n\">jmena</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"n\">radky</span> <span class=\"o\">=</span> <span class=\"n\">text</span><span class=\"o\">.</span><span class=\"n\">splitlines</span><span class=\"p\">()</span>\n <span class=\"n\">kurzy</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n <span class=\"k\">for</span> <span class=\"n\">radek</span> <span class=\"ow\">in</span> <span class=\"n\">radky</span><span class=\"p\">:</span>\n <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">castka</span><span class=\"p\">,</span> <span class=\"n\">mena</span><span class=\"p\">,</span> <span class=\"n\">hodnota</span> <span class=\"o\">=</span> <span class=\"n\">radek</span><span class=\"o\">.</span><span class=\"n\">replace</span><span class=\"p\">(</span><span class=\"s2\">","</span><span class=\"p\">,</span> <span class=\"s2\">"."</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">split</span><span class=\"p\">(</span><span class=\"s2\">"|"</span><span class=\"p\">)</span>\n <span class=\"n\">kurzy</span><span class=\"p\">[</span><span class=\"n\">mena</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">castka</span><span class=\"p\">)</span> <span class=\"o\">/</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">hodnota</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">kurzy</span>\n</pre></div><p>Řešení najdete na konci této stránky.</p>\n<h2>Click</h2>\n<p>Když instalujete knihovnu, zadáváte příkaz <code>python -m pip install foo</code>. V tomto\npřípadě <code>python</code> je název příkazu, který chcete spustit, a ostatní slova na\ntomto řádku (oddělená mezerami), jsou argumenty tohoto příkazu.</p>\n<p>Dříve nebo později narazíte na to, že vaše programy budou potřebovat nějaký\nvstup od uživatele. Číst je vždy přes funkci <code>input()</code> není úplně pohodlné ani\npro uživatele, ani pro programátora. Proto je dobré vědět, jak definovat a\npoužívat argumenty.</p>\n<p>Existuje hodně knihoven, které umožňují zpracovávat argumenty na příkazové\nřádce. Jenom samotná standardní knihovna Pythonu má <code>getopt</code>, <code>optparse</code> a\n<code>argparse</code>. Ty ale nejsou úplně příjemné na používání.</p>\n<p>Oproti tomu knihovna <a href=\"https://click.palletsprojects.com/en/7.x/\">click</a> poskytuje rozhraní, ve kterém můžete jednoduché\nprogramy sekat jako Baťa cvičky. Cenou je lehce magický způsob, jak argumenty\ndefinovat, a taky ztráta možnosti ovlivnit do nejjemnějších detailů, jak se\nprogram má chovat. To ale obvykle není problém.</p>\n<h3>Trocha teorie</h3>\n<p>Různé systémy používají různé konvence, jak by měly argumenty vypadat a\nfungovat. Tady si popíšeme, jak se slušně vychované programy chovají na Linuxu\n(nebo na Macu).</p>\n<p>Existují dvě základní kategorie: argumenty a přepínače. Argumenty jsou většinou\n(ale ne vždy) vyžadované, přepínače obvykle potřeba nejsou. Argumenty jsou dané\npořadím (pokud jich je víc), přepínače mají jména.</p>\n<p>Jména přepínačů obvykle začínají dvěmi pomlčkami, pokud mají hezké čitelné\njméno, nebo jednou pomlčkou, pokud je to jenom jedno písmeno. Dost často jeden\npřepínač může mít jak jednopísmenné jméno, tak i delší a čitelnější.</p>\n<h3>Instalace</h3>\n<p>Nic překvapivého:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(venv) $ </span>python -m pip install click\n</pre></div><h3>Hello world</h3>\n<p>Na tomto jednoduchém programu si ukážeme, jak se dá funkce změnit v něco, co\nbude pěkně použitelné na příkazové řádce.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">click</span>\n\n<span class=\"nd\">@click.command</span><span class=\"p\">()</span>\n<span class=\"nd\">@click.option</span><span class=\"p\">(</span><span class=\"s2\">"--kolikrat"</span><span class=\"p\">,</span> <span class=\"n\">default</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"n\">help</span><span class=\"o\">=</span><span class=\"s2\">"Kolikrát budeme zdravit"</span><span class=\"p\">)</span>\n<span class=\"nd\">@click.option</span><span class=\"p\">(</span><span class=\"s2\">"--jmeno"</span><span class=\"p\">,</span> <span class=\"n\">prompt</span><span class=\"o\">=</span><span class=\"s2\">"Tvoje jméno"</span><span class=\"p\">,</span>\n <span class=\"n\">help</span><span class=\"o\">=</span><span class=\"s2\">"Koho budeme zdravit"</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">hello</span><span class=\"p\">(</span><span class=\"n\">kolikrat</span><span class=\"p\">,</span> <span class=\"n\">jmeno</span><span class=\"p\">):</span>\n <span class=\"k\">for</span> <span class=\"n\">x</span> <span class=\"ow\">in</span> <span class=\"nb\">range</span><span class=\"p\">(</span><span class=\"n\">kolikrat</span><span class=\"p\">):</span>\n <span class=\"n\">click</span><span class=\"o\">.</span><span class=\"n\">echo</span><span class=\"p\">(</span><span class=\"n\">f</span><span class=\"s2\">"Ahoj {jmeno}!"</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">if</span> <span class=\"vm\">__name__</span> <span class=\"o\">==</span> <span class=\"s2\">"__main__"</span><span class=\"p\">:</span>\n <span class=\"n\">hello</span><span class=\"p\">()</span>\n</pre></div><p>Funguje to takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(venv) $ </span>python hello.py --kolikrat <span class=\"m\">3</span> --jmeno Adame\n<span class=\"go\">Ahoj Adame!</span>\n<span class=\"go\">Ahoj Adame!</span>\n<span class=\"go\">Ahoj Adame!</span>\n</pre></div><p>Příkazům začínajícím zavináčem před definicí funkce říkáme dekorátory. Je to\nmožnost, jak v Pythonu můžeme ovlivnit chování funkce (a pravděpodobně se jim\nbudeme věnovat trochu více v některé následující lekci).</p>\n<p>První řádek <code>@click.command()</code> říká, že následující funkce by se měla chovat\njako příkaz.</p>\n<p>Další dva řádky definují přepínače tohoto příkazu.</p>\n<p>První z nich se jmenuje <code>--kolikrat</code>, a pokud ho nezadáme, dostane výchozí\nhodnotu 1. <em>Click</em> z této výchozí hodnoty pozná, že hodnotou toho přepínače\nbude vždy číslo. Takže když zkusíme zadat jiný text, dostaneme chybu. Argument\npředaný do funkce <code>hello()</code> bude už typu <code>int</code>.</p>\n<p>Druhý argument bude jméno. Typ nijak nespecifikuje, takže to bude řetězec.\n<code>prompt</code> říká, že pokud přepínač nezadáme, program se nás zeptá.</p>\n<p>Zkuste si s tímto programem chvilku hrát. Nezapomeňte, že <em>click</em> vypíše pěknou\nnápovědu, pokud program spustíte s přepínačem <code>--help</code>.</p>\n<h3>Další možnosti</h3>\n<p>Možné typy přepínačů (použití: <code>@click.option(…, type=click.X, …</code>):</p>\n<ul>\n<li><code>click.INT</code> – celé číslo</li>\n<li><code>click.FLOAT</code> – číslo s desetinnou tečkou</li>\n<li><code>click.FILE</code> – název souboru na příkazové řádce, ale funkce už dostane\notevřený soubor a <em>click</em> se sám postará i o zavření</li>\n</ul>\n<p>Další možnosti jsou třeba <code>multiple=True</code>. Tím přepínač změníme tak, že ho bude\nmožné zadávat několikrát. Funkce potom dostane n-tici hodnot.</p>\n<p>Argumenty se definují velmi podobně jako přepínače. Jediný rozdíl je v použitém\ndekorátoru <code>@click.argument()</code>. Jména argumentů se zadávají bez úvodních\npomlček.</p>\n<p><em>Click</em> taky umožňuje vypisování na výstup. <code>click.echo</code> se chová velmi podobně\njako <code>print</code>, akorát se snaží lépe fungovat, pokud máte rozbitý terminál.</p>\n<h3>Cvičení</h3>\n<p>Napište program, který bude vypisovat tuto nápovědu:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(venv) $ </span>python cnb.py --help\n<span class=\"go\">Usage: cnb.py [OPTIONS] CASTKA</span>\n\n<span class=\"go\">Options:</span>\n<span class=\"go\"> --datum TEXT</span>\n<span class=\"go\"> --mena TEXT může být zadaný vícekrát</span>\n<span class=\"go\"> --help Show this message and exit.</span>\n</pre></div><h2>Dokončení programu</h2>\n<p>Zkombinujte výsledky obou cvičení do jednoho programu. Tento program bude\nvyžadovat jedno číslo. To bude částka v korunách. Program načte buď kurzy podle\nzadaného data, nebo poslední zveřejněné.</p>\n<p>Pokud nebude zadaná žádná měna, program převede částku do všech dostupných měn\na vypíše je v nějakém pěkném formátu. Pokud budou nějaké měny zadané, bude\npřevádět jen do nich.</p>\n<h2>Řešení</h2>\n<p>Zkus si ale cvičení nejdřív vyřešit bez pomoci :)</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">click</span>\n<span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">parse_rates</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n <span class=\"n\">hlavicka</span><span class=\"p\">,</span> <span class=\"n\">jmena</span><span class=\"p\">,</span> <span class=\"o\">*</span><span class=\"n\">radky</span> <span class=\"o\">=</span> <span class=\"n\">text</span><span class=\"o\">.</span><span class=\"n\">splitlines</span><span class=\"p\">()</span>\n <span class=\"n\">kurzy</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n <span class=\"k\">for</span> <span class=\"n\">radek</span> <span class=\"ow\">in</span> <span class=\"n\">radky</span><span class=\"p\">:</span>\n <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">castka</span><span class=\"p\">,</span> <span class=\"n\">mena</span><span class=\"p\">,</span> <span class=\"n\">hodnota</span> <span class=\"o\">=</span> <span class=\"n\">radek</span><span class=\"o\">.</span><span class=\"n\">replace</span><span class=\"p\">(</span><span class=\"s2\">","</span><span class=\"p\">,</span> <span class=\"s2\">"."</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">split</span><span class=\"p\">(</span><span class=\"s2\">"|"</span><span class=\"p\">)</span>\n <span class=\"n\">kurzy</span><span class=\"p\">[</span><span class=\"n\">mena</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">castka</span><span class=\"p\">)</span> <span class=\"o\">/</span> <span class=\"nb\">float</span><span class=\"p\">(</span><span class=\"n\">hodnota</span><span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">kurzy</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">get_exchange_rates</span><span class=\"p\">(</span><span class=\"n\">datum</span><span class=\"o\">=</span><span class=\"bp\">None</span><span class=\"p\">):</span>\n <span class=\"n\">parametry</span> <span class=\"o\">=</span> <span class=\"p\">{}</span>\n <span class=\"k\">if</span> <span class=\"n\">datum</span><span class=\"p\">:</span>\n <span class=\"c1\"># Pokud máme datum, použijeme ho. Prázdný slovník parametrů nemá na</span>\n <span class=\"c1\"># výsledek žádný vliv.</span>\n <span class=\"n\">parametry</span><span class=\"p\">[</span><span class=\"s2\">"date"</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">datum</span>\n <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">requests</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span>\n <span class=\"s2\">"http://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt"</span><span class=\"p\">,</span>\n <span class=\"n\">params</span><span class=\"o\">=</span><span class=\"n\">parametry</span><span class=\"p\">,</span>\n <span class=\"p\">)</span>\n <span class=\"k\">return</span> <span class=\"n\">parse_rates</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">)</span>\n\n\n<span class=\"nd\">@click.command</span><span class=\"p\">()</span>\n<span class=\"nd\">@click.option</span><span class=\"p\">(</span><span class=\"s2\">"--datum"</span><span class=\"p\">)</span>\n<span class=\"nd\">@click.option</span><span class=\"p\">(</span><span class=\"s2\">"--mena"</span><span class=\"p\">,</span> <span class=\"n\">multiple</span><span class=\"o\">=</span><span class=\"bp\">True</span><span class=\"p\">)</span>\n<span class=\"nd\">@click.argument</span><span class=\"p\">(</span><span class=\"s2\">"castka"</span><span class=\"p\">,</span> <span class=\"nb\">type</span><span class=\"o\">=</span><span class=\"n\">click</span><span class=\"o\">.</span><span class=\"n\">FLOAT</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">cnb</span><span class=\"p\">(</span><span class=\"n\">castka</span><span class=\"p\">,</span> <span class=\"n\">datum</span><span class=\"p\">,</span> <span class=\"n\">mena</span><span class=\"p\">):</span>\n <span class=\"n\">kurzy</span> <span class=\"o\">=</span> <span class=\"n\">get_exchange_rates</span><span class=\"p\">(</span><span class=\"n\">datum</span><span class=\"p\">)</span>\n <span class=\"k\">for</span> <span class=\"n\">zkratka_meny</span> <span class=\"ow\">in</span> <span class=\"nb\">sorted</span><span class=\"p\">(</span><span class=\"n\">kurzy</span><span class=\"p\">):</span>\n <span class=\"c1\"># Pokud nemáme žádné měny, nebo tato měna byla zadaná …</span>\n <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">mena</span> <span class=\"ow\">or</span> <span class=\"n\">zkratka_meny</span> <span class=\"ow\">in</span> <span class=\"n\">mena</span><span class=\"p\">:</span>\n <span class=\"c1\"># … tak převedeme částku a vypíšeme ji.</span>\n <span class=\"n\">prevedeno</span> <span class=\"o\">=</span> <span class=\"n\">castka</span> <span class=\"o\">*</span> <span class=\"n\">kurzy</span><span class=\"p\">[</span><span class=\"n\">zkratka_meny</span><span class=\"p\">]</span>\n <span class=\"n\">click</span><span class=\"o\">.</span><span class=\"n\">echo</span><span class=\"p\">(</span><span class=\"n\">f</span><span class=\"s2\">"{castka} CZK = {prevedeno} {zkratka_meny}"</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">if</span> <span class=\"vm\">__name__</span> <span class=\"o\">==</span> <span class=\"s2\">"__main__"</span><span class=\"p\">:</span>\n <span class=\"n\">cnb</span><span class=\"p\">()</span>\n</pre></div>\n\n\n " } } }