<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>Modul „urllib“</title>
  <date>2011-04-28</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
</meta>
<!--
  „“–
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->


<slide title="Úvod">

  <p>
    Moduly pro podporu internetových protokolů jsou jednou z nejzměněnějších částí Python'u 3.x. Neustále se rozrůstající a čím dál tím více nepřehledná „sběř“ modulů pro práci s nimi byla „umravněna“ a přesunuta pod jeden společný rodičovský modul <em>urllib</em>. V rámci přesunutí došlo též k jednotnému přejmenování dílčích modulů podle jejich funkce a sdružení souvisejících částí kódů do společných modulů.
  </p>
  
  <p>
    Z mnoha dostupných objektů nás v tomto úvodu bude zajímat především objekt <code>urllib.request</code>, jehož primárním úkolem je zajištění komunikace po protokolech HTTP, FTP a <em>file</em> (tedy lokální soubory). Modul je to dosti obsáhlý, ale pro začátek si vystačíme s minimem.
  </p>
  <note>
    Tento modul je přímým pokračovatelem modulu <em>urllib2</em> z Python'u 2.x.
  </note>
  <handout>
    Asi až po Python 3.1 bylo HTTP omezeno pouze na verze 0.9 a 1.0?
  </handout>

</slide>
<slide title="Navázání komunikace – „urlopen()“">

  <p>
    Navázání komunikace se servrem (v nejjednodušším možném případě tedy zaslání dotazu typu <em>GET</em>) se používá metoda <code>urllib.request.urlopen()</code>. K jejímu výstupu se dá přistupovat v podstatě jako k práci s binárním souborovým proudem (<em>stream</em>). Budeme-li prozatím ignorovat možná selhání při spojení apod., stačí tedy v podstatě napsat pouze..
  </p>
  <example lang="python">
    stream = urllib.request.urlopen('URL')
  </example>
  <p>
    ..jak ostatně ukazuje výstup následujícího programu:
  </p>
  <example layout="vertical">
    <program src="_files/download.0.py" lang="python"/>
    <out src="_files/download.0.out" lang="python"/>
  </example>
  <note>
    Některé z uvedených atributů nejsou přímo vlastními atributy objektu <em>urllib.request</em>, ale „probublají“ do něj odjinud, např.: <em>info()</em> z <em>urllib.response</em>, <em>code()</em> z <em>urllib.error</em> apod.
  </note>

</slide>
<slide title="Načítání dat">

  <p>
    Jelikož návratový objekt metody <em>urlopen()</em> se pro všechny praktické účely tváří jako binární soubor, obsah odkazovaného dokumentu snadno přečteme např. pomocí metody <em>read()</em>:
  </p>
  <example layout="vertical">
    <program src="_files/download.1.py" lang="python"/>
    <out src="_files/download.1.out" lang="python"/>
  </example>
  <note>
    Obsah návratového objektu je text v konkrétním kódování, zde UTF-8. Pro jeho převod z binární podoby tedy musíme použít metodu binárního řetězce <em>decode()</em>.
  </note>

</slide>
<slide title="POST – „data=“ &amp; „Request()“">

  <p>
    Běžné dotazy na server (z předchozího slajdu) probíhají jako GET. Pokud zavedeme požadavek i s částí <em>data</em>, bude změněn na POST:
  </p>
  <example lang="python">
    stream = urllib.request.urlopen('URL', data=b'DATA')
  </example>
  <p>
    Jelikož <em>urlopen()</em> očekává v parametru <em>data</em> bajtový řetězec, je pro jednoduché dotazy snažší použít okliku (nebo spíš plnou cestu, protože <em>urlopen()</em> je v podstatě zkratka) přes objekt typu <code>Request</code>, kterému pro (jednoduchá) data stačí řetězec. Ukažme si to rovnou na příkladu:
  </p>

  <p>
    Nechť v adresáři <em>/cgi-bin/</em> našeho serveru sídlí následující CGI-skript:
  </p>
  <example lang="python">
#!/usr/bin/env python

import sys

data = sys.stdin.read()

print('Content-type: text-plain\n\nPrijata data: "%s"' % data)
  </example>

  <p>
    Tomuto skriptu pošleme vstupní data např. takovýmto způsobem:
  </p>
  <example layout="vertical">
    <program src="_files/cgi.1.test.py" lang="python"/>
    <out src="_files/cgi.1.test.out" lang="python"/>
  </example>

</slide>
<slide title="POST – „urlencode()“">

  <p>
    Praktičtější (a častější) je poslat komplikovanější dotaz obsahující mapování typu <em>klíč=hodnota</em>. V takovém případě je třeba zasílaná data vhodným způsobem zakódovat, o což se stará metoda <code>urllib.parse.urlencode()</code>.
  </p>
  <note>
    Její výstup můžeme předhodit parametru <em>data</em> jak u <em>urlopen()</em>, tak u <em>Request()</em>.
  </note>
  
  <p>
    Budeme-li pokračovat v příkladu z předchozího slajdu, upravený kód vypadá takto:
  </p>
  <example layout="vertical">
    <program src="_files/cgi.2.test.py" lang="python"/>
    <out src="_files/cgi.2.test.out" lang="python"/>
  </example>

</slide>
<slide title="HTTP-hlavičky">

  <p>
    Často je třeba doplnit POST-dotaz o konkrétní HTTP-hlavičky. O to se stará další nepovinný parametr konstruktoru <em>Request()</em>.
  </p>
  <note>
    A tentokrát už <em>urlopen()</em> ekvivalentní parametr nemá, pro práci s hlavičkami tedy musíte použít <em>Request()</em>.
  </note>
  <p>
    Ukažme si jeho použití na jednoduchém příkladu:
  </p>
  
  <p>
    Velmi často se vám může stát, že server s vámi odmítne komunikovat, pokud se neohlásíte jako určitý známý klient. Jelikož knihovna <code>urllib</code> se ve výchozím nastavení hlásí jako <em>Python-urllib/x.y</em>, bude třeba „podvrhnout“ jinou identifikaci úpravou HTPP-hlavičky <em>User-Agent</em>:
  </p>
  <example src="_files/cgi.3.test.py" lang="python" />
  <note>
    Stejným způsobem se přidávají i další potřebné HTTP-hlavičky.
  </note>

</slide>
<slide title="Zpracování chyb I">

  <p>
    Knihovna <em>urllib</em> se sama stará o nejtypičtější události v rámci protokolu HTTP. Tak kupříkladu přesměrování na jinou adresu je provedeno automaticky a vy jako uživatel obdržíte v odpovědi až finální výsledek.
  </p>

  <p>
    Pokud se ale dotážete na neexistující zdroj nebo se podaří vyprodukovat jakoukoliv jinou chybu v rozsazích 4xx a 5xx, výše uvedený postup použití knihovny pochopitelně ošklivě selže:
  </p>
  <example layout="vertical">
    <program src="_files/download.2.py" lang="python"/>
    <out src="_files/download.2.out" lang="python"/>
  </example>

</slide>
<slide title="Zpracování chyb II">

  <p>
    V reálném programu je proto třeba doplnit kód o zpracování takovýchto chyb, jak ukazuje dotaz na neexistující cestu na serveru:
  </p>
  <example layout="vertical">
    <program src="_files/download.3.py" lang="python"/>
    <out src="_files/download.3a.out" lang="text"/>
  </example>
  <note>
    Jelikož výjimka <em>HTTPError</em> je potomkem výjimky <em>URLError</em>, musí být uvedena jako první, jinak by ji zpracovala hned větev u <em>URLError</em> a ve starších verzích Python'u by to celé ošklivě zhavarovalo pro nedostupnost argumentu <em>reason</em>. (Alespoň v Python'u 3.4 už to ale není pravda – atribut <em>reason</em> už je prvkem až výjimky <em>HTTPError</em>.)
  </note>
  <p>
    Mimochodem kdybychom se pokusili stejným kódem zavolat neexistující server, obdrželi bychom výstup právě z druhé výjimky <em>URLError</em>:
  </p>
  <example src="_files/download.3b.out" lang="text" />

</slide>
<!--slide title="urllib.error">

  <p>
    Modul <em>urllib.error</em> združuje a definuje třídy výjimek, které využívá modul <em>urllib.request</em>. Tyto základní výjimky jsou tři, jejich základem je třída <code>URLError</code> (která je potomkem třídy <code>IOError</code>):
  </p>
  <ul>
    <li>
        <code>urllib.error.URLError</code> – Základní výjimka modulu <em>urllib.request</em>. V atributu(?) <code>reason</code> obsahuje důvod výjimky (ať už jako řetězec nebo jako jinou výjimku).
    </li>
    <li>
        <code>urllib.error.HTTPError</code> – Může fungovat jako běžná výjimka i jako návratová hodnota (jako z <em>urlopen()</em>). V atributu(?) <code>code</code> obsahuje standardní HTTP status-kód.
    </li>
    <li>
        <code>urllib.error.ContentTooShortError</code> – Vyhozena, detekuje-li funkce <em>urlretrieve()</em> menší počet stažených dat, než bylo uvedeno v hlavičce (<em>Content-Length</em>).
    </li>
  </ul>

</slide>
<slide title="">

  <p>
  </p>
  <example lang="python">
  </example>

</slide-->
  

</lecture>
