Requests + Click

Co je cílem tohoto cvičení?

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ředpoklady

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.

Requests

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

Parametry pro GET

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.

Posílání dat

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.

Stažení velkého souboru

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.

Cvičení

Č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.

Click

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.

Trocha teorie

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ší.

Instalace

Nic překvapivého:

(venv) $ python -m pip install click

Hello world

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.

Další možnosti

Možné typy přepínačů (použití: @click.option(…, type=click.X, …):

  • click.INT – celé číslo
  • click.FLOAT – číslo s desetinnou tečkou
  • click.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.

Cvičení

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.

Dokončení programu

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.

Řešení

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