Web Scraping

Co je cílem tohoto cvičení?

Jak říká XKCD #903, pokud na Wikipedii budete klikat na první odkaz v textu článku, který není v závorkách nebo kurzívou, dříve nebo později se dostanete na článek o filosofii.

Dneska si to ověříme v praxi. Během tohoto ověřování se naučíme používat knihovnu BeautifulSoup a procvičíme si requests.

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í (na kurzu s lektorem můžete použít prostředí z minulé lekce.

Teorie do začátku

Webové stránky jsou super, pokud je čtete v prohlížečí, jako je třeba Firefox nebo Chrome. Občas ale potřebuje vytáhnout nějaké informace, a potom s nimi dále pracovat. Ruční kopírování je moc náročné.

Některé webové služby poskytují API, přes které je možné se k datům dostat v nějakém civilizovaném formátu. Toto bohužel není až tak časté, a obvykle je potřeba data vytáhnout ze samotné stránky, tak jak je určená pro prohlížeče.

Webové stránky jsou napsané v jazyku HTML. Je to značkovací jazyk, kde se míchá text určený pro lidi se značkami (tagy) určenými pro prohlížeč. Tyto tagy definují, jak se má text zobrazovat.

Instalace

(venv) $ python -m pip install beautifulsoup4 requests

Knihovna beautifulsoup existuje v několika verzích. My chceme tu poslední, verzi 4. Protože hodně existujících programů pořád používá starší verzi 3, jsou pro instalaci stále dostupné obě.

Použití

Hodně zjednodušený pohled na knihovnu beautifulsoup vypadá takto: z textu reprezentujícího HTML kód můžete vytvořit strukturu, ve které je potom možné hledat požadované značky a pracovat s nimi.

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<html><body><h1>Ahoj</h1></body></html>")
>>> soup
<html><body><h1>Ahoj</h1></body></html>
>>> soup.name
'[document]'
>>> soup.html.body.h1.text
'Ahoj'
>>>

Vyzkoušíme si nejdřív prohledávání malého dokumentu:

>>> html_doc = """
... <html><head><title>O třech prasátkách</title></head>
... <body>
... <p class="title"><b>O třech prasátkách</b></p>
... <p class="story">Byla nebyla tři prasátka. Postavila si domky ze
... <a href="http://example.com/slama" class="material">slámy</a>,
... <a href="http://example.com/drevo" class="material">dřeva</a> a
... <a href="http://example.com/cihly" class="material">cihel</a>.
... </p>
... <p class="story">...</p>
... </body>
... </html>
... """
>>>

Nejdříve ho zpracujeme. Druhý argument upřesňuje, že se jedná o HTML.

>>> soup = BeautifulSoup(html_doc, "html.parser")
>>>

Přístup přes název tagu už jsme viděli. Dostaneme tak první tag s daným jménem. Můžeme se ale zeptat na všechny.

>>> soup.a
<a class="material" href="http://example.com/slama">slámy</a>
>>> soup.find_all("a")
[<a class="material" href="http://example.com/slama">slámy</a>, <a class="material" href="http://example.com/drevo">dřeva</a>, <a class="material" href="http://example.com/cihly">cihel</a>]
>>>

Můžeme taky zkoumat obsah tagů:

>>> hlavicka = soup.head
>>> hlavicka
<head><title>O třech prasátkách</title></head>
>>> hlavicka.contents
[<title>O třech prasátkách</title>]
>>> titulek = hlavicka.contents[0]
>>> titulek
<title>O třech prasátkách</title>
>>> titulek.contents
['O třech prasátkách']
>>>

Pokud chceme iterovat v cyklu přes všechny věci uvnitř nějakého tagu, je lepší použít children než contents. Ušetříme si tak vytváření seznamu.

Pokud element obsahuje jenom text, můžeme se k němu dostat přes atribut string:

>>> titulek.string
'O třech prasátkách'
>>>

Ve stromu značek se můžeme pohybovat nejenom dolů, ale i nahoru a do stran.

  • parent – nadřazený element
  • parents – iterátor, přes který můžeme vylézt až ke kořenovému elementu
  • next_sibling, previous_sibling – skok na další nebo předchozí element, který má stejného rodiče (v reálném dokumentu soused většiny elementů bude pravděpodobně text plný mezer)

Vyhledávání

Už jsme zmínili metodu find_all. Podle čeho všecho můžeme vyhledávat? Zatím jsme viděli vyhledávání podle názvu elementu. Můžeme ale hledat i podle seznamu elementů, případně podle regulárního výrazu.

Můžeme taky udělat find_all(True). Tím dostaneme všechny elementy, ale ne text.

Taky můžeme hledat podle funkce. Tato funkce dostane jako argument jeden element, a pokud vrátí True, element bude považovaný za nalezený.

Jakýkoli pojmenovaný argument bude fungovat jako filtr na atributy elementu. Často se hodí vyhledávat podle atributu class, který ale nejde použít pro argument funkce v Pythonu. Naštěstí class_ se dá použít jako náhrada.

Nepraktický příklad z neživota

Na tomto místě je asi fajn zmínit, že na scraping neexistuje univerzální návod. Typický vývoj programu vypadá tak, že ho ladíme na datech, dokud se nechová rozumně. První aktualizace stránek ho typicky rozbije a můžeme začít ladit znovu. Je to možná ale jednodušší než se snažit vymyslet něco dokonalého.

Jak to bude celé fungovat: budeme postupně stahovat stránky z Wikipedie. Začneme náhodnou stránkou. Na každé stránce najdeme první vhodný odkaz a budeme na něj pokračovat. Program skončí, až se znovu dostane na stránku, kde už byl, nebo pokud nenajde žádný pěkný odkaz.

Není ale úplně dobrý nápad napsat velký program na první pokus. Budeme postupně přidávat malé kousky funkcionality, abychom mohli pořád opakovaně testovat, že všechno dělá to, co má.

Krok 1 – kostra programu

Začneme jednoduchou kostrou, kde si v komentářích vyznačíme, co se bude dít.

URL = "https://en.wikipedia.org"
START = "/wiki/Special:Random"


def stahuj(stranka):
    while True:
        # 1. Stáhni stránku
        # 2. Napiš titulek
        # 3. Vytáhni další odkaz
        break


if __name__ == "__main__":
    stahuj(START)

Krok 2 – stažení stránky

První úkol: nahraďte první komentář kódem, který stáhne stránku, zkontroluje připadné chyby a vypíše text odpovědi od serveru. Adresu stránky ke stažení dostanete spojením proměnných URL a stranka.

Očekávané chování po tomto kroku: program vypíše dlouhý kus HTML kódu a skončí. Při každém běhu bude výstup jiný (pracujeme s náhodnou stránkou).

Řešení

Krok 3 – hledání titulku

Úkol: doplňte tělo funkce najdi_titulek() a upravte funkci stahuj() tak, aby místo celé stránky vypsala jenom titulek stránky.

def najdi_titulek(html):
    """Najde titulek v HTML kódu. Titulek je v elementu s identifikátorem
    `firstHeading`. Budeme předpokládat, že tento element vždycky existuje.

    Funkce vrátí titulek jako řetězec.
    """

Očekávané chování: program vypíše titulek náhodné stránky a skončí.

Řešení

Krok 4 – úklid před další funkcionalitou

V dalším kroku konečně budeme hledat odkaz na další stránku. K tomu vytvoříme ještě jednu funkci: najdi_odkaz(). Ta bude velmi podobná funkci najdi_titulek().

Volání BeautifulSoup() je pomerně náročný výpočet, a asi ho nechceme dělat dvakrát.

Úkoly:

  1. Upravte program tak, aby se polévka elementů vytvořila už ve funkci stahuj(), a do najdi_titulek() se předala jako argument.
  2. Vytvořte funkci najdi_odkaz(). Bude mít stejný argument jako najdi_titulek(). Prozatím bude vždycky vracet None.
  3. Přidejte volání najdi_odkaz() do stahuj(). Vrácený odkaz uložte do proměnné stranka.
  4. Zavolejte break jenom tehdy, když stranka je None
  5. Na konec while cyklu přidejte volání time.sleep(1). Nezapomeňte naimportovat modul time.

time.sleep(1) náš program zastaví na 1 sekundu po zpracování každé stránky. Chceme si totiž procvičit programování, ne zbytečně vytěžovat cizí servery.

Očekávané chování: žádná změna oproti předchozímu kroku.

Řešení

Krok 5 – hledání odkazů

  1. Nejdříve na stránce najdeme element s atributem class s hodnotou mw-parser-output. To je box s hlavním textem článku.
  2. V cyklu projdeme přes každý odstavec (p) v tomto elementu.
  3. Vytiskneme odstavec.
  4. Pro každý odkaz (a) v tomto odstavci:
  5. vytiskneme tento odkaz,
  6. a vytáhneme z odkazu hodnotu atributu href a vrátíme ji. Tady se bude hodit metoda get().

Očekávané chování: program vytiskne titulek náhodné stránky, první odstavec na ní, potom první odkaz v tomto odstavci. Pak vytiskne další nadpis, odstavec, odkaz a tak dále. Nikdy neskončí. Ukončit ho bude třeba ručně klávesovou zkratkou Ctrl-C.

Řešení

Krok 6 – ukončení programu

Úkol: zajistíme, aby program někdy skončil. Pokud se dostaneme na stránku, kde už jsme byli, můžeme skončit.

  1. Na začátku funkce stahuj() si vytvořte proměnnou navstivene. Začne jako prázdná množina (set()).
  2. Jako první věc uvnitř while cyklu zkontrolujte, jestli stranka je v navštívených. Pokud ano, ukončete cyklus.
  3. Přidejte stránku mezi navštívené.

Očekávané chování: program bude vypisovat spoustu textu jako předtím, ale časem by měl skončit. Pořád začíná na náhodné stránce, takže to někdy může chvilku trvat.

Řešení

Krok 7 – odstranění textu v závorkách

Úkol: teď odstraníme text v závorkách. Začneme pomocnou funkci odstran_zavorky(), která by se měla chovat podle přiloženého dokumentačního komentáře.

Zavolejte tuto funkci v najdi_odkaz(). Jako argument jí dáte odstavec převedený na řetězec (pomocí str(odstavec)). Vrácený výsledek vypíšete hned potom, co se vypisuje odstavec.

Očekávané chování: stejné jako dřív, akorát odstavec bude vypsaný vždy dvakrát. Poprvé tak, jak se nachází na stránce. Podruhé bez ozávorkovaných částí.

Vzhledem k tomu, že v tomto kroku akorát pracujeme s řetězci, můžete ho přeskočit a rovnou se podívat na řešení. Ale je to relativně zajímavý problém.

def odstran_zavorky(text):
    """Odstraní uzávorkované výrazy z textu. HTML elementy mimo závorky budou
    zachované. Funkce předpokládá, že každá otevírací závorka má i uzavírací
    závorku, a že závorky a HTML elementy se nekříží.

    >>> odstran_zavorky("Ahoj (nazdar)!")
    'Ahoj !'
    >>> odstran_zavorky("<b>Ahoj</b>")
    '<b>Ahoj</b>'
    >>> odstran_zavorky("A (<i>písmeno</i>) B")
    'A  B'
    >>> odstran_zavorky("a (b (c) d) e")
    'a  e'
    """

Řešení

Krok 8 – použití nové funkce

Úkol: Místo vypisování odstavce bez závorek ho znovu převeďte na značkovou polévku. Odkazy hledejte v ní. Teď už je na čase odstranit vypisování odstavce i odkazu.

Očekávané chování: program bude vypisovat titulky stránek a následovat odkazy na nich. Až dojde na stránku, kde už byl (nebo kde není žádný odkaz), tak skončí.

Řešení

Krok 9 – filtrování pouze pěkných odkazů

Úkol: Někdy první odkaz nevede na jinou stránku (typicky odkazy na zdroje uvedené na konci stránky). Upravte funkci najdi_odkaz() tak, aby vracela adresu stránky jenom tehdy, pokud ta vracená hodnota začíná řetězecem /wiki/.

Očekávané chování: program se bude chovat stejně jako dřív, ale bude trochu chytřejší v hledání správného odkazu.

Finální řešení

Na anglické wikipedii by tento program měl poměrně spolehlivě dojít na stránku Philosophy. Pokud to zkusíte s českou verzí, až tak slavné to není. Ale i tam jsou výsledky relativně zajímavé.

Řešení

{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2019/brno-jaro-knihovny:scraping:0",
      "title": "Web Scraping",
      "html": "\n          \n    \n\n    <h1>Web Scraping</h1>\n<h2>Co je c&#xED;lem tohoto cvi&#x10D;en&#xED;?</h2>\n<p>Jak &#x159;&#xED;k&#xE1; <a href=\"https://xkcd.com/903/\">XKCD #903</a>, pokud na Wikipedii budete klikat\nna prvn&#xED; odkaz v textu &#x10D;l&#xE1;nku, kter&#xFD; nen&#xED; v z&#xE1;vork&#xE1;ch nebo kurz&#xED;vou, d&#x159;&#xED;ve nebo\npozd&#x11B;ji se dostanete na &#x10D;l&#xE1;nek o filosofii.</p>\n<p>Dneska si to ov&#x11B;&#x159;&#xED;me v praxi. B&#x11B;hem tohoto ov&#x11B;&#x159;ov&#xE1;n&#xED; se nau&#x10D;&#xED;me pou&#x17E;&#xED;vat\nknihovnu <a href=\"https://www.crummy.com/software/BeautifulSoup/\">BeautifulSoup</a> a\nprocvi&#x10D;&#xED;me si <a href=\"http://docs.python-requests.org/en/master/\">requests</a>.</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; (na kurzu s lektorem m&#x16F;&#x17E;ete pou&#x17E;&#xED;t prost&#x159;ed&#xED; z <a href=\"/2019/brno-jaro-knihovny/beginners/kurzovni-listek/\">minul&#xE9;\nlekce</a>.</p>\n<h2>Teorie do za&#x10D;&#xE1;tku</h2>\n<p>Webov&#xE9; str&#xE1;nky jsou super, pokud je &#x10D;tete v prohl&#xED;&#x17E;e&#x10D;&#xED;, jako je t&#x159;eba Firefox\nnebo Chrome. Ob&#x10D;as ale pot&#x159;ebuje vyt&#xE1;hnout n&#x11B;jak&#xE9; informace, a potom s nimi\nd&#xE1;le pracovat. Ru&#x10D;n&#xED; kop&#xED;rov&#xE1;n&#xED; je moc n&#xE1;ro&#x10D;n&#xE9;.</p>\n<p>N&#x11B;kter&#xE9; webov&#xE9; slu&#x17E;by poskytuj&#xED; API, p&#x159;es kter&#xE9; je mo&#x17E;n&#xE9; se k dat&#x16F;m dostat v\nn&#x11B;jak&#xE9;m civilizovan&#xE9;m form&#xE1;tu. Toto bohu&#x17E;el nen&#xED; a&#x17E; tak &#x10D;ast&#xE9;, a obvykle je\npot&#x159;eba data vyt&#xE1;hnout ze samotn&#xE9; str&#xE1;nky, tak jak je ur&#x10D;en&#xE1; pro prohl&#xED;&#x17E;e&#x10D;e.</p>\n<p>Webov&#xE9; str&#xE1;nky jsou napsan&#xE9; v jazyku HTML. Je to zna&#x10D;kovac&#xED; jazyk, kde se m&#xED;ch&#xE1;\ntext ur&#x10D;en&#xFD; pro lidi se zna&#x10D;kami (<em>tagy</em>) ur&#x10D;en&#xFD;mi pro prohl&#xED;&#x17E;e&#x10D;. Tyto <em>tagy</em>\ndefinuj&#xED;, jak se m&#xE1; text zobrazovat.</p>\n<h2>Instalace</h2>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(venv) $ </span>python -m pip install beautifulsoup4 requests\n</pre></div><p>Knihovna <code>beautifulsoup</code> existuje v n&#x11B;kolika verz&#xED;ch. My chceme tu posledn&#xED;,\nverzi 4. Proto&#x17E;e hodn&#x11B; existuj&#xED;c&#xED;ch program&#x16F; po&#x159;&#xE1;d pou&#x17E;&#xED;v&#xE1; star&#x161;&#xED; verzi 3, jsou\npro instalaci st&#xE1;le dostupn&#xE9; ob&#x11B;.</p>\n<h2>Pou&#x17E;it&#xED;</h2>\n<p>Hodn&#x11B; zjednodu&#x161;en&#xFD; pohled na knihovnu <code>beautifulsoup</code> vypad&#xE1; takto: z textu\nreprezentuj&#xED;c&#xED;ho HTML k&#xF3;d m&#x16F;&#x17E;ete vytvo&#x159;it strukturu, ve kter&#xE9; je potom mo&#x17E;n&#xE9;\nhledat po&#x17E;adovan&#xE9; zna&#x10D;ky a pracovat s nimi.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"s2\">&quot;&lt;html&gt;&lt;body&gt;&lt;h1&gt;Ahoj&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;&quot;</span><span class=\"p\">)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">soup</span>\n<span class=\"go\">&lt;html&gt;&lt;body&gt;&lt;h1&gt;Ahoj&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">name</span>\n<span class=\"go\">&apos;[document]&apos;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">html</span><span class=\"o\">.</span><span class=\"n\">body</span><span class=\"o\">.</span><span class=\"n\">h1</span><span class=\"o\">.</span><span class=\"n\">text</span>\n<span class=\"go\">&apos;Ahoj&apos;</span>\n<span class=\"go\">&gt;&gt;&gt;</span>\n</pre></div><p>Vyzkou&#x161;&#xED;me si nejd&#x159;&#xED;v prohled&#xE1;v&#xE1;n&#xED; mal&#xE9;ho dokumentu:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">html_doc</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;&quot;&quot;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;html&gt;&lt;head&gt;&lt;title&gt;O t&#x159;ech pras&#xE1;tk&#xE1;ch&lt;/title&gt;&lt;/head&gt;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;body&gt;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;p class=&quot;title&quot;&gt;&lt;b&gt;O t&#x159;ech pras&#xE1;tk&#xE1;ch&lt;/b&gt;&lt;/p&gt;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;p class=&quot;story&quot;&gt;Byla nebyla t&#x159;i pras&#xE1;tka. Postavila si domky ze</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;a href=&quot;http://example.com/slama&quot; class=&quot;material&quot;&gt;sl&#xE1;my&lt;/a&gt;,</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;a href=&quot;http://example.com/drevo&quot; class=&quot;material&quot;&gt;d&#x159;eva&lt;/a&gt; a</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;a href=&quot;http://example.com/cihly&quot; class=&quot;material&quot;&gt;cihel&lt;/a&gt;.</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;/p&gt;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;p class=&quot;story&quot;&gt;...&lt;/p&gt;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;/body&gt;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&lt;/html&gt;</span>\n<span class=\"gp\">... </span><span class=\"s2\">&quot;&quot;&quot;</span>\n<span class=\"go\">&gt;&gt;&gt;</span>\n</pre></div><p>Nejd&#x159;&#xED;ve ho zpracujeme. Druh&#xFD; argument up&#x159;es&#x148;uje, &#x17E;e se jedn&#xE1; o HTML.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">html_doc</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n<span class=\"go\">&gt;&gt;&gt;</span>\n</pre></div><p>P&#x159;&#xED;stup p&#x159;es n&#xE1;zev tagu u&#x17E; jsme vid&#x11B;li. Dostaneme tak prvn&#xED; tag s dan&#xFD;m jm&#xE9;nem.\nM&#x16F;&#x17E;eme se ale zeptat na v&#x161;echny.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">a</span>\n<span class=\"go\">&lt;a class=&quot;material&quot; href=&quot;http://example.com/slama&quot;&gt;sl&#xE1;my&lt;/a&gt;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;a&quot;</span><span class=\"p\">)</span>\n<span class=\"go\">[&lt;a class=&quot;material&quot; href=&quot;http://example.com/slama&quot;&gt;sl&#xE1;my&lt;/a&gt;, &lt;a class=&quot;material&quot; href=&quot;http://example.com/drevo&quot;&gt;d&#x159;eva&lt;/a&gt;, &lt;a class=&quot;material&quot; href=&quot;http://example.com/cihly&quot;&gt;cihel&lt;/a&gt;]</span>\n<span class=\"go\">&gt;&gt;&gt;</span>\n</pre></div><p>M&#x16F;&#x17E;eme taky zkoumat obsah tag&#x16F;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">hlavicka</span> <span class=\"o\">=</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">head</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">hlavicka</span>\n<span class=\"go\">&lt;head&gt;&lt;title&gt;O t&#x159;ech pras&#xE1;tk&#xE1;ch&lt;/title&gt;&lt;/head&gt;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">hlavicka</span><span class=\"o\">.</span><span class=\"n\">contents</span>\n<span class=\"go\">[&lt;title&gt;O t&#x159;ech pras&#xE1;tk&#xE1;ch&lt;/title&gt;]</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">titulek</span> <span class=\"o\">=</span> <span class=\"n\">hlavicka</span><span class=\"o\">.</span><span class=\"n\">contents</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">titulek</span>\n<span class=\"go\">&lt;title&gt;O t&#x159;ech pras&#xE1;tk&#xE1;ch&lt;/title&gt;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">titulek</span><span class=\"o\">.</span><span class=\"n\">contents</span>\n<span class=\"go\">[&apos;O t&#x159;ech pras&#xE1;tk&#xE1;ch&apos;]</span>\n<span class=\"go\">&gt;&gt;&gt;</span>\n</pre></div><p>Pokud chceme iterovat v cyklu p&#x159;es v&#x161;echny v&#x11B;ci uvnit&#x159; n&#x11B;jak&#xE9;ho tagu, je lep&#x161;&#xED;\npou&#x17E;&#xED;t <code>children</code> ne&#x17E; <code>contents</code>. U&#x161;et&#x159;&#xED;me si tak vytv&#xE1;&#x159;en&#xED; seznamu.</p>\n<p>Pokud element obsahuje jenom text, m&#x16F;&#x17E;eme se k n&#x11B;mu dostat p&#x159;es atribut <code>string</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">titulek</span><span class=\"o\">.</span><span class=\"n\">string</span>\n<span class=\"go\">&apos;O t&#x159;ech pras&#xE1;tk&#xE1;ch&apos;</span>\n<span class=\"go\">&gt;&gt;&gt;</span>\n</pre></div><p>Ve stromu zna&#x10D;ek se m&#x16F;&#x17E;eme pohybovat nejenom dol&#x16F;, ale i nahoru a do stran.</p>\n<ul>\n<li><code>parent</code> &#x2013; nad&#x159;azen&#xFD; element</li>\n<li><code>parents</code> &#x2013; iter&#xE1;tor, p&#x159;es kter&#xFD; m&#x16F;&#x17E;eme vyl&#xE9;zt a&#x17E; ke ko&#x159;enov&#xE9;mu elementu</li>\n<li><code>next_sibling</code>, <code>previous_sibling</code> &#x2013; skok na dal&#x161;&#xED; nebo p&#x159;edchoz&#xED; element,\nkter&#xFD; m&#xE1; stejn&#xE9;ho rodi&#x10D;e (v re&#xE1;ln&#xE9;m dokumentu soused v&#x11B;t&#x161;iny element&#x16F; bude\npravd&#x11B;podobn&#x11B; text pln&#xFD; mezer)</li>\n</ul>\n<h3>Vyhled&#xE1;v&#xE1;n&#xED;</h3>\n<p>U&#x17E; jsme zm&#xED;nili metodu <code>find_all</code>. Podle &#x10D;eho v&#x161;echo m&#x16F;&#x17E;eme vyhled&#xE1;vat? Zat&#xED;m\njsme vid&#x11B;li vyhled&#xE1;v&#xE1;n&#xED; podle n&#xE1;zvu elementu. M&#x16F;&#x17E;eme ale hledat i podle seznamu\nelement&#x16F;, p&#x159;&#xED;padn&#x11B; podle regul&#xE1;rn&#xED;ho v&#xFD;razu.</p>\n<p>M&#x16F;&#x17E;eme taky ud&#x11B;lat <code>find_all(True)</code>. T&#xED;m dostaneme v&#x161;echny elementy, ale ne\ntext.</p>\n<p>Taky m&#x16F;&#x17E;eme hledat podle funkce. Tato funkce dostane jako argument jeden\nelement, a pokud vr&#xE1;t&#xED; <code>True</code>, element bude pova&#x17E;ovan&#xFD; za nalezen&#xFD;.</p>\n<p>Jak&#xFD;koli pojmenovan&#xFD; argument bude fungovat jako filtr na atributy elementu.\n&#x10C;asto se hod&#xED; vyhled&#xE1;vat podle atributu <code>class</code>, kter&#xFD; ale nejde pou&#x17E;&#xED;t pro\nargument funkce v Pythonu. Na&#x161;t&#x11B;st&#xED; <code>class_</code> se d&#xE1; pou&#x17E;&#xED;t jako n&#xE1;hrada.</p>\n<h2>Nepraktick&#xFD; p&#x159;&#xED;klad z ne&#x17E;ivota</h2>\n<div class=\"admonition note\"><p>Na tomto m&#xED;st&#x11B; je asi fajn zm&#xED;nit, &#x17E;e na <em>scraping</em> neexistuje univerz&#xE1;ln&#xED;\nn&#xE1;vod. Typick&#xFD; v&#xFD;voj programu vypad&#xE1; tak, &#x17E;e ho lad&#xED;me na datech, dokud se\nnechov&#xE1; rozumn&#x11B;. Prvn&#xED; aktualizace str&#xE1;nek ho typicky rozbije a m&#x16F;&#x17E;eme za&#x10D;&#xED;t\nladit znovu. Je to mo&#x17E;n&#xE1; ale jednodu&#x161;&#x161;&#xED; ne&#x17E; se sna&#x17E;it vymyslet n&#x11B;co\ndokonal&#xE9;ho.</p>\n</div><p>Jak to bude cel&#xE9; fungovat: budeme postupn&#x11B; stahovat str&#xE1;nky z Wikipedie.\nZa&#x10D;neme n&#xE1;hodnou str&#xE1;nkou. Na ka&#x17E;d&#xE9; str&#xE1;nce najdeme prvn&#xED; vhodn&#xFD; odkaz a\nbudeme na n&#x11B;j pokra&#x10D;ovat. Program skon&#x10D;&#xED;, a&#x17E; se znovu dostane na str&#xE1;nku, kde\nu&#x17E; byl, nebo pokud nenajde &#x17E;&#xE1;dn&#xFD; p&#x11B;kn&#xFD; odkaz.</p>\n<p>Nen&#xED; ale &#xFA;pln&#x11B; dobr&#xFD; n&#xE1;pad napsat velk&#xFD; program na prvn&#xED; pokus. Budeme postupn&#x11B;\np&#x159;id&#xE1;vat mal&#xE9; kousky funkcionality, abychom mohli po&#x159;&#xE1;d opakovan&#x11B; testovat, &#x17E;e\nv&#x161;echno d&#x11B;l&#xE1; to, co m&#xE1;.</p>\n<h3>Krok 1 &#x2013; kostra programu</h3>\n<p>Za&#x10D;neme jednoduchou kostrou, kde si v koment&#xE1;&#x159;&#xED;ch vyzna&#x10D;&#xED;me, co se bude d&#xED;t.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"c1\"># 1. St&#xE1;hni str&#xE1;nku</span>\n        <span class=\"c1\"># 2. Napi&#x161; titulek</span>\n        <span class=\"c1\"># 3. Vyt&#xE1;hni dal&#x161;&#xED; odkaz</span>\n        <span class=\"k\">break</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div><h3>Krok 2 &#x2013; sta&#x17E;en&#xED; str&#xE1;nky</h3>\n<p>Prvn&#xED; &#xFA;kol: nahra&#x10F;te prvn&#xED; koment&#xE1;&#x159; k&#xF3;dem, kter&#xFD; st&#xE1;hne str&#xE1;nku, zkontroluje\np&#x159;ipadn&#xE9; chyby a vyp&#xED;&#x161;e text odpov&#x11B;di od serveru. Adresu str&#xE1;nky ke sta&#x17E;en&#xED;\ndostanete spojen&#xED;m prom&#x11B;nn&#xFD;ch <code>URL</code> a <code>stranka</code>.</p>\n<p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED; po tomto kroku: program vyp&#xED;&#x161;e dlouh&#xFD; kus HTML k&#xF3;du a skon&#x10D;&#xED;.\nP&#x159;i ka&#x17E;d&#xE9;m b&#x11B;hu bude v&#xFD;stup jin&#xFD; (pracujeme s n&#xE1;hodnou str&#xE1;nkou).</p>\n<div class=\"solution\" id=\"solution-0\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/0/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"n\">odpoved</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=\"n\">URL</span> <span class=\"o\">+</span> <span class=\"n\">stranka</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">)</span>\n        <span class=\"c1\"># 2. Napi&#x161; titulek</span>\n        <span class=\"c1\"># 3. Vyt&#xE1;hni dal&#x161;&#xED; odkaz</span>\n        <span class=\"k\">break</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Krok 3 &#x2013; hled&#xE1;n&#xED; titulku</h3>\n<p>&#xDA;kol: dopl&#x148;te t&#x11B;lo funkce <code>najdi_titulek()</code> a upravte funkci <code>stahuj()</code> tak,\naby m&#xED;sto cel&#xE9; str&#xE1;nky vypsala jenom titulek str&#xE1;nky.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">html</span><span class=\"p\">):</span>\n    <span class=\"sd\">&quot;&quot;&quot;Najde titulek v HTML k&#xF3;du. Titulek je v elementu s identifik&#xE1;torem</span>\n<span class=\"sd\">    `firstHeading`. Budeme p&#x159;edpokl&#xE1;dat, &#x17E;e tento element v&#x17E;dycky existuje.</span>\n\n<span class=\"sd\">    Funkce vr&#xE1;t&#xED; titulek jako &#x159;et&#x11B;zec.</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n</pre></div><p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED;: program vyp&#xED;&#x161;e titulek n&#xE1;hodn&#xE9; str&#xE1;nky a skon&#x10D;&#xED;.</p>\n<div class=\"solution\" id=\"solution-1\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/1/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">html</span><span class=\"p\">):</span>\n    <span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">html</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"o\">=</span><span class=\"s2\">&quot;firstHeading&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">text</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"n\">odpoved</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=\"n\">URL</span> <span class=\"o\">+</span> <span class=\"n\">stranka</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">))</span>\n        <span class=\"c1\"># 3. Vyt&#xE1;hni dal&#x161;&#xED; odkaz</span>\n        <span class=\"k\">break</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Krok 4 &#x2013; &#xFA;klid p&#x159;ed dal&#x161;&#xED; funkcionalitou</h3>\n<p>V dal&#x161;&#xED;m kroku kone&#x10D;n&#x11B; budeme hledat odkaz na dal&#x161;&#xED; str&#xE1;nku. K tomu vytvo&#x159;&#xED;me\nje&#x161;t&#x11B; jednu funkci: <code>najdi_odkaz()</code>. Ta bude velmi podobn&#xE1; funkci\n<code>najdi_titulek()</code>.</p>\n<p>Vol&#xE1;n&#xED; <code>BeautifulSoup()</code> je pomern&#x11B; n&#xE1;ro&#x10D;n&#xFD; v&#xFD;po&#x10D;et, a asi ho nechceme d&#x11B;lat\ndvakr&#xE1;t.</p>\n<p>&#xDA;koly:</p>\n<ol>\n<li>Upravte program tak, aby se pol&#xE9;vka element&#x16F; vytvo&#x159;ila u&#x17E; ve funkci\n<code>stahuj()</code>, a do <code>najdi_titulek()</code> se p&#x159;edala jako argument.</li>\n<li>Vytvo&#x159;te funkci <code>najdi_odkaz()</code>. Bude m&#xED;t stejn&#xFD; argument jako\n<code>najdi_titulek()</code>. Prozat&#xED;m bude v&#x17E;dycky vracet <code>None</code>.</li>\n<li>P&#x159;idejte vol&#xE1;n&#xED; <code>najdi_odkaz()</code> do <code>stahuj()</code>. Vr&#xE1;cen&#xFD; odkaz ulo&#x17E;te do\n prom&#x11B;nn&#xE9; <code>stranka</code>.</li>\n<li>Zavolejte <code>break</code> jenom tehdy, kdy&#x17E; <code>stranka</code> je <code>None</code></li>\n<li>Na konec <code>while</code> cyklu p&#x159;idejte vol&#xE1;n&#xED; <code>time.sleep(1)</code>. Nezapome&#x148;te\n naimportovat modul <code>time</code>.</li>\n</ol>\n<div class=\"admonition note\"><p><code>time.sleep(1)</code> n&#xE1;&#x161; program zastav&#xED; na 1 sekundu po zpracov&#xE1;n&#xED; ka&#x17E;d&#xE9; str&#xE1;nky.\nChceme si toti&#x17E; procvi&#x10D;it programov&#xE1;n&#xED;, ne zbyte&#x10D;n&#x11B; vyt&#x11B;&#x17E;ovat ciz&#xED; servery.</p>\n</div><p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED;: &#x17E;&#xE1;dn&#xE1; zm&#x11B;na oproti p&#x159;edchoz&#xED;mu kroku.</p>\n<div class=\"solution\" id=\"solution-2\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/2/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">time</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"o\">=</span><span class=\"s2\">&quot;firstHeading&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">text</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"k\">pass</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"n\">odpoved</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=\"n\">URL</span> <span class=\"o\">+</span> <span class=\"n\">stranka</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n\n        <span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">))</span>\n\n        <span class=\"n\">stranka</span> <span class=\"o\">=</span> <span class=\"n\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">stranka</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n\n        <span class=\"n\">time</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Krok 5 &#x2013; hled&#xE1;n&#xED; odkaz&#x16F;</h3>\n<ol>\n<li>Nejd&#x159;&#xED;ve na str&#xE1;nce najdeme element s atributem <code>class</code> s hodnotou\n<code>mw-parser-output</code>. To je box s hlavn&#xED;m textem &#x10D;l&#xE1;nku.</li>\n<li>V cyklu projdeme p&#x159;es ka&#x17E;d&#xFD; odstavec (<code>p</code>) v tomto elementu.</li>\n<li>Vytiskneme odstavec.</li>\n<li>Pro ka&#x17E;d&#xFD; odkaz (<code>a</code>) v tomto odstavci:</li>\n<li>vytiskneme tento odkaz,</li>\n<li>a vyt&#xE1;hneme z odkazu hodnotu atributu <code>href</code> a vr&#xE1;t&#xED;me ji. Tady se bude\nhodit metoda <code>get()</code>.</li>\n</ol>\n<p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED;: program vytiskne titulek n&#xE1;hodn&#xE9; str&#xE1;nky, prvn&#xED; odstavec na\nn&#xED;, potom prvn&#xED; odkaz v tomto odstavci. Pak vytiskne dal&#x161;&#xED; nadpis, odstavec,\nodkaz a tak d&#xE1;le. Nikdy neskon&#x10D;&#xED;. Ukon&#x10D;it ho bude t&#x159;eba ru&#x10D;n&#x11B; kl&#xE1;vesovou\nzkratkou Ctrl-C.</p>\n<div class=\"solution\" id=\"solution-3\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/3/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">time</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"o\">=</span><span class=\"s2\">&quot;firstHeading&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">text</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"n\">hlavni_text</span> <span class=\"o\">=</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"n\">class_</span><span class=\"o\">=</span><span class=\"s2\">&quot;mw-parser-output&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">for</span> <span class=\"n\">odstavec</span> <span class=\"ow\">in</span> <span class=\"n\">hlavni_text</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;p&quot;</span><span class=\"p\">):</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odstavec</span><span class=\"p\">)</span>\n        <span class=\"k\">for</span> <span class=\"n\">odkaz</span> <span class=\"ow\">in</span> <span class=\"n\">odstavec</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;a&quot;</span><span class=\"p\">):</span>\n            <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odkaz</span><span class=\"p\">)</span>\n            <span class=\"k\">return</span> <span class=\"n\">odkaz</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">&quot;href&quot;</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"n\">odpoved</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=\"n\">URL</span> <span class=\"o\">+</span> <span class=\"n\">stranka</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n\n        <span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">))</span>\n\n        <span class=\"n\">stranka</span> <span class=\"o\">=</span> <span class=\"n\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">stranka</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n\n        <span class=\"n\">time</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Krok 6 &#x2013; ukon&#x10D;en&#xED; programu</h3>\n<p>&#xDA;kol: zajist&#xED;me, aby program n&#x11B;kdy skon&#x10D;il. Pokud se dostaneme na str&#xE1;nku, kde\nu&#x17E; jsme byli, m&#x16F;&#x17E;eme skon&#x10D;it.</p>\n<ol>\n<li>Na za&#x10D;&#xE1;tku funkce <code>stahuj()</code> si vytvo&#x159;te prom&#x11B;nnou <code>navstivene</code>. Za&#x10D;ne\njako pr&#xE1;zdn&#xE1; mno&#x17E;ina (<code>set()</code>).</li>\n<li>Jako prvn&#xED; v&#x11B;c uvnit&#x159; <code>while</code> cyklu zkontrolujte, jestli <code>stranka</code> je v\nnav&#x161;t&#xED;ven&#xFD;ch. Pokud ano, ukon&#x10D;ete cyklus.</li>\n<li>P&#x159;idejte str&#xE1;nku mezi nav&#x161;t&#xED;ven&#xE9;.</li>\n</ol>\n<p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED;: program bude vypisovat spoustu textu jako p&#x159;edt&#xED;m, ale\n&#x10D;asem by m&#x11B;l skon&#x10D;it. Po&#x159;&#xE1;d za&#x10D;&#xED;n&#xE1; na n&#xE1;hodn&#xE9; str&#xE1;nce, tak&#x17E;e to n&#x11B;kdy m&#x16F;&#x17E;e\nchvilku trvat.</p>\n<div class=\"solution\" id=\"solution-4\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/4/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">time</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"o\">=</span><span class=\"s2\">&quot;firstHeading&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">text</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"n\">hlavni_text</span> <span class=\"o\">=</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"n\">class_</span><span class=\"o\">=</span><span class=\"s2\">&quot;mw-parser-output&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">for</span> <span class=\"n\">odstavec</span> <span class=\"ow\">in</span> <span class=\"n\">hlavni_text</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;p&quot;</span><span class=\"p\">):</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odstavec</span><span class=\"p\">)</span>\n        <span class=\"k\">for</span> <span class=\"n\">odkaz</span> <span class=\"ow\">in</span> <span class=\"n\">odstavec</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;a&quot;</span><span class=\"p\">):</span>\n            <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odkaz</span><span class=\"p\">)</span>\n            <span class=\"k\">return</span> <span class=\"n\">odkaz</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">&quot;href&quot;</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"n\">navstivene</span> <span class=\"o\">=</span> <span class=\"nb\">set</span><span class=\"p\">()</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"k\">if</span> <span class=\"n\">stranka</span> <span class=\"ow\">in</span> <span class=\"n\">navstivene</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n        <span class=\"n\">navstivene</span><span class=\"o\">.</span><span class=\"n\">add</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">)</span>\n\n        <span class=\"n\">odpoved</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=\"n\">URL</span> <span class=\"o\">+</span> <span class=\"n\">stranka</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n\n        <span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">))</span>\n\n        <span class=\"n\">stranka</span> <span class=\"o\">=</span> <span class=\"n\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">stranka</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n\n        <span class=\"n\">time</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Krok 7 &#x2013; odstran&#x11B;n&#xED; textu v z&#xE1;vork&#xE1;ch</h3>\n<p>&#xDA;kol: te&#x10F; odstran&#xED;me text v z&#xE1;vork&#xE1;ch. Za&#x10D;neme pomocnou funkci\n<code>odstran_zavorky()</code>, kter&#xE1; by se m&#x11B;la chovat podle p&#x159;ilo&#x17E;en&#xE9;ho dokumenta&#x10D;n&#xED;ho\nkoment&#xE1;&#x159;e.</p>\n<p>Zavolejte tuto funkci v <code>najdi_odkaz()</code>. Jako argument j&#xED; d&#xE1;te odstavec\np&#x159;eveden&#xFD; na &#x159;et&#x11B;zec (pomoc&#xED; <code>str(odstavec)</code>). Vr&#xE1;cen&#xFD; v&#xFD;sledek vyp&#xED;&#x161;ete hned\npotom, co se vypisuje odstavec.</p>\n<p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED;: stejn&#xE9; jako d&#x159;&#xED;v, akor&#xE1;t odstavec bude vypsan&#xFD; v&#x17E;dy dvakr&#xE1;t.\nPoprv&#xE9; tak, jak se nach&#xE1;z&#xED; na str&#xE1;nce. Podruh&#xE9; bez oz&#xE1;vorkovan&#xFD;ch &#x10D;&#xE1;st&#xED;.</p>\n<div class=\"admonition note\"><p>Vzhledem k tomu, &#x17E;e v tomto kroku akor&#xE1;t pracujeme s &#x159;et&#x11B;zci, m&#x16F;&#x17E;ete ho\np&#x159;esko&#x10D;it a rovnou se pod&#xED;vat na &#x159;e&#x161;en&#xED;. Ale je to relativn&#x11B; zaj&#xED;mav&#xFD;\nprobl&#xE9;m.</p>\n</div><div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">odstran_zavorky</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"sd\">&quot;&quot;&quot;Odstran&#xED; uz&#xE1;vorkovan&#xE9; v&#xFD;razy z textu. HTML elementy mimo z&#xE1;vorky budou</span>\n<span class=\"sd\">    zachovan&#xE9;. Funkce p&#x159;edpokl&#xE1;d&#xE1;, &#x17E;e ka&#x17E;d&#xE1; otev&#xED;rac&#xED; z&#xE1;vorka m&#xE1; i uzav&#xED;rac&#xED;</span>\n<span class=\"sd\">    z&#xE1;vorku, a &#x17E;e z&#xE1;vorky a HTML elementy se nek&#x159;&#xED;&#x17E;&#xED;.</span>\n\n<span class=\"sd\">    &gt;&gt;&gt; odstran_zavorky(&quot;Ahoj (nazdar)!&quot;)</span>\n<span class=\"sd\">    &apos;Ahoj !&apos;</span>\n<span class=\"sd\">    &gt;&gt;&gt; odstran_zavorky(&quot;&lt;b&gt;Ahoj&lt;/b&gt;&quot;)</span>\n<span class=\"sd\">    &apos;&lt;b&gt;Ahoj&lt;/b&gt;&apos;</span>\n<span class=\"sd\">    &gt;&gt;&gt; odstran_zavorky(&quot;A (&lt;i&gt;p&#xED;smeno&lt;/i&gt;) B&quot;)</span>\n<span class=\"sd\">    &apos;A  B&apos;</span>\n<span class=\"sd\">    &gt;&gt;&gt; odstran_zavorky(&quot;a (b (c) d) e&quot;)</span>\n<span class=\"sd\">    &apos;a  e&apos;</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n</pre></div><div class=\"solution\" id=\"solution-5\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/5/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">time</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">odstran_zavorky</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"c1\"># V kolika z&#xE1;vork&#xE1;ch jsme vno&#x159;en&#xED;. 0 = mimo z&#xE1;vorky</span>\n    <span class=\"n\">hloubka</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n    <span class=\"c1\"># Jsme zrovna uvnit&#x159; n&#x11B;jak&#xE9;ho HTML elementu?</span>\n    <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n    <span class=\"n\">vysledek</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;&quot;</span>\n    <span class=\"k\">for</span> <span class=\"n\">znak</span> <span class=\"ow\">in</span> <span class=\"n\">text</span><span class=\"p\">:</span>\n        <span class=\"c1\"># Pokud jsme v n&#x11B;jak&#xE9;m elementu&#x2026;</span>\n        <span class=\"k\">if</span> <span class=\"n\">v_tagu</span><span class=\"p\">:</span>\n            <span class=\"c1\"># &#x2026;chceme zachovat ve&#x161;ker&#xFD; text.</span>\n            <span class=\"n\">vysledek</span> <span class=\"o\">+=</span> <span class=\"n\">znak</span>\n            <span class=\"c1\"># Kon&#x10D;&#xED; tady zna&#x10D;ka?</span>\n            <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;&gt;&quot;</span><span class=\"p\">:</span>\n                <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n        <span class=\"k\">else</span><span class=\"p\">:</span>\n            <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;(&quot;</span><span class=\"p\">:</span>\n                <span class=\"c1\"># Pokud vstupujeme do uz&#xE1;vorkovan&#xE9;ho v&#xFD;razu, jsme o jednu</span>\n                <span class=\"c1\"># &#xFA;rove&#x148; hloub&#x11B;ji.</span>\n                <span class=\"n\">hloubka</span> <span class=\"o\">+=</span> <span class=\"mi\">1</span>\n            <span class=\"k\">elif</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;)&quot;</span><span class=\"p\">:</span>\n                <span class=\"c1\"># Pokud vystupujeme, &#xFA;rove&#x148; o jedna zmen&#x161;&#xED;me.</span>\n                <span class=\"n\">hloubka</span> <span class=\"o\">-=</span> <span class=\"mi\">1</span>\n            <span class=\"k\">elif</span> <span class=\"n\">hloubka</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n                <span class=\"c1\"># Jsme mimo z&#xE1;vorky, chceme si znak nechat.</span>\n                <span class=\"n\">vysledek</span> <span class=\"o\">+=</span> <span class=\"n\">znak</span>\n                <span class=\"c1\"># Ale mus&#xED;me zkontrolovat, jestli nevstupujeme do n&#x11B;jak&#xE9;ho HTML</span>\n                <span class=\"c1\"># elementu.</span>\n                <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;&lt;&quot;</span><span class=\"p\">:</span>\n                    <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">True</span>\n\n    <span class=\"k\">return</span> <span class=\"n\">vysledek</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"o\">=</span><span class=\"s2\">&quot;firstHeading&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">text</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"n\">hlavni_text</span> <span class=\"o\">=</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"n\">class_</span><span class=\"o\">=</span><span class=\"s2\">&quot;mw-parser-output&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">for</span> <span class=\"n\">odstavec</span> <span class=\"ow\">in</span> <span class=\"n\">hlavni_text</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;p&quot;</span><span class=\"p\">):</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odstavec</span><span class=\"p\">)</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odstran_zavorky</span><span class=\"p\">(</span><span class=\"nb\">str</span><span class=\"p\">(</span><span class=\"n\">odstavec</span><span class=\"p\">)))</span>\n        <span class=\"k\">for</span> <span class=\"n\">odkaz</span> <span class=\"ow\">in</span> <span class=\"n\">odstavec</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;a&quot;</span><span class=\"p\">):</span>\n            <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">odkaz</span><span class=\"p\">)</span>\n            <span class=\"k\">return</span> <span class=\"n\">odkaz</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">&quot;href&quot;</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"n\">navstivene</span> <span class=\"o\">=</span> <span class=\"nb\">set</span><span class=\"p\">()</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"k\">if</span> <span class=\"n\">stranka</span> <span class=\"ow\">in</span> <span class=\"n\">navstivene</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n        <span class=\"n\">navstivene</span><span class=\"o\">.</span><span class=\"n\">add</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">)</span>\n\n        <span class=\"n\">odpoved</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=\"n\">URL</span> <span class=\"o\">+</span> <span class=\"n\">stranka</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n\n        <span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">))</span>\n\n        <span class=\"n\">stranka</span> <span class=\"o\">=</span> <span class=\"n\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">stranka</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n\n        <span class=\"n\">time</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Krok 8 &#x2013; pou&#x17E;it&#xED; nov&#xE9; funkce</h3>\n<p>&#xDA;kol: M&#xED;sto vypisov&#xE1;n&#xED; odstavce bez z&#xE1;vorek ho znovu p&#x159;eve&#x10F;te na zna&#x10D;kovou\npol&#xE9;vku. Odkazy hledejte v n&#xED;. Te&#x10F; u&#x17E; je na &#x10D;ase odstranit vypisov&#xE1;n&#xED; odstavce\ni odkazu.</p>\n<p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED;: program bude vypisovat titulky str&#xE1;nek a n&#xE1;sledovat odkazy\nna nich. A&#x17E; dojde na str&#xE1;nku, kde u&#x17E; byl (nebo kde nen&#xED; &#x17E;&#xE1;dn&#xFD; odkaz), tak\nskon&#x10D;&#xED;.</p>\n<div class=\"solution\" id=\"solution-6\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/6/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">time</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">odstran_zavorky</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"n\">hloubka</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n    <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n    <span class=\"n\">vysledek</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;&quot;</span>\n    <span class=\"k\">for</span> <span class=\"n\">znak</span> <span class=\"ow\">in</span> <span class=\"n\">text</span><span class=\"p\">:</span>\n        <span class=\"k\">if</span> <span class=\"n\">v_tagu</span><span class=\"p\">:</span>\n            <span class=\"n\">vysledek</span> <span class=\"o\">+=</span> <span class=\"n\">znak</span>\n            <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;&gt;&quot;</span><span class=\"p\">:</span>\n                <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n        <span class=\"k\">else</span><span class=\"p\">:</span>\n            <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;(&quot;</span><span class=\"p\">:</span>\n                <span class=\"n\">hloubka</span> <span class=\"o\">+=</span> <span class=\"mi\">1</span>\n            <span class=\"k\">elif</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;)&quot;</span><span class=\"p\">:</span>\n                <span class=\"n\">hloubka</span> <span class=\"o\">-=</span> <span class=\"mi\">1</span>\n            <span class=\"k\">elif</span> <span class=\"n\">hloubka</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n                <span class=\"n\">vysledek</span> <span class=\"o\">+=</span> <span class=\"n\">znak</span>\n                <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;&lt;&quot;</span><span class=\"p\">:</span>\n                    <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">True</span>\n\n    <span class=\"k\">return</span> <span class=\"n\">vysledek</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"o\">=</span><span class=\"s2\">&quot;firstHeading&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">text</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"n\">hlavni_text</span> <span class=\"o\">=</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"n\">class_</span><span class=\"o\">=</span><span class=\"s2\">&quot;mw-parser-output&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">for</span> <span class=\"n\">odstavec</span> <span class=\"ow\">in</span> <span class=\"n\">hlavni_text</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;p&quot;</span><span class=\"p\">):</span>\n        <span class=\"n\">html</span> <span class=\"o\">=</span> <span class=\"n\">odstran_zavorky</span><span class=\"p\">(</span><span class=\"nb\">str</span><span class=\"p\">(</span><span class=\"n\">odstavec</span><span class=\"p\">))</span>\n        <span class=\"n\">odstavec</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">html</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">for</span> <span class=\"n\">odkaz</span> <span class=\"ow\">in</span> <span class=\"n\">odstavec</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;a&quot;</span><span class=\"p\">):</span>\n            <span class=\"k\">return</span> <span class=\"n\">odkaz</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">&quot;href&quot;</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"n\">navstivene</span> <span class=\"o\">=</span> <span class=\"nb\">set</span><span class=\"p\">()</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"k\">if</span> <span class=\"n\">stranka</span> <span class=\"ow\">in</span> <span class=\"n\">navstivene</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n        <span class=\"n\">navstivene</span><span class=\"o\">.</span><span class=\"n\">add</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">)</span>\n\n        <span class=\"n\">odpoved</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=\"n\">URL</span> <span class=\"o\">+</span> <span class=\"n\">stranka</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n\n        <span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">))</span>\n\n        <span class=\"n\">stranka</span> <span class=\"o\">=</span> <span class=\"n\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">stranka</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n\n        <span class=\"n\">time</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">1</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\">stahuj</span><span class=\"p\">(</span><span class=\"n\">START</span><span class=\"p\">)</span>\n</pre></div>\n    </div>\n</div><h3>Krok 9 &#x2013; filtrov&#xE1;n&#xED; pouze p&#x11B;kn&#xFD;ch odkaz&#x16F;</h3>\n<p>&#xDA;kol: N&#x11B;kdy prvn&#xED; odkaz nevede na jinou str&#xE1;nku (typicky odkazy na zdroje\nuveden&#xE9; na konci str&#xE1;nky). Upravte funkci <code>najdi_odkaz()</code> tak, aby vracela\nadresu str&#xE1;nky jenom tehdy, pokud ta vracen&#xE1; hodnota za&#x10D;&#xED;n&#xE1; &#x159;et&#x11B;zecem <code>/wiki/</code>.</p>\n<p>O&#x10D;ek&#xE1;van&#xE9; chov&#xE1;n&#xED;: program se bude chovat stejn&#x11B; jako d&#x159;&#xED;v, ale bude trochu\nchyt&#x159;ej&#x161;&#xED; v hled&#xE1;n&#xED; spr&#xE1;vn&#xE9;ho odkazu.</p>\n<h3>Fin&#xE1;ln&#xED; &#x159;e&#x161;en&#xED;</h3>\n<p>Na anglick&#xE9; wikipedii by tento program m&#x11B;l pom&#x11B;rn&#x11B; spolehliv&#x11B; doj&#xED;t na str&#xE1;nku\n<em>Philosophy</em>. Pokud to zkus&#xED;te s &#x10D;eskou verz&#xED;, a&#x17E; tak slavn&#xE9; to nen&#xED;. Ale i tam\njsou v&#xFD;sledky relativn&#x11B; zaj&#xED;mav&#xE9;.</p>\n<div class=\"solution\" id=\"solution-7\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-knihovny/beginners/scraping/index/solutions/7/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">time</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n<span class=\"kn\">from</span> <span class=\"nn\">bs4</span> <span class=\"kn\">import</span> <span class=\"n\">BeautifulSoup</span>\n\n\n<span class=\"n\">URL</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;https://en.wikipedia.org&quot;</span>\n<span class=\"n\">START</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;/wiki/Special:Random&quot;</span>\n<span class=\"c1\"># &#x10C;esk&#xE1; wikipedie:</span>\n<span class=\"c1\"># URL = &quot;https://cs.wikipedia.org&quot;</span>\n<span class=\"c1\"># START = &quot;/wiki/Speci&#xE1;ln&#xED;:N&#xE1;hodn&#xE1;_str&#xE1;nka&quot;</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">odstran_zavorky</span><span class=\"p\">(</span><span class=\"n\">text</span><span class=\"p\">):</span>\n    <span class=\"n\">hloubka</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n    <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n    <span class=\"n\">vysledek</span> <span class=\"o\">=</span> <span class=\"s2\">&quot;&quot;</span>\n    <span class=\"k\">for</span> <span class=\"n\">znak</span> <span class=\"ow\">in</span> <span class=\"n\">text</span><span class=\"p\">:</span>\n        <span class=\"k\">if</span> <span class=\"n\">v_tagu</span><span class=\"p\">:</span>\n            <span class=\"n\">vysledek</span> <span class=\"o\">+=</span> <span class=\"n\">znak</span>\n            <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;&gt;&quot;</span><span class=\"p\">:</span>\n                <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n        <span class=\"k\">else</span><span class=\"p\">:</span>\n            <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;(&quot;</span><span class=\"p\">:</span>\n                <span class=\"n\">hloubka</span> <span class=\"o\">+=</span> <span class=\"mi\">1</span>\n            <span class=\"k\">elif</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;)&quot;</span><span class=\"p\">:</span>\n                <span class=\"n\">hloubka</span> <span class=\"o\">-=</span> <span class=\"mi\">1</span>\n            <span class=\"k\">elif</span> <span class=\"n\">hloubka</span> <span class=\"o\">==</span> <span class=\"mi\">0</span><span class=\"p\">:</span>\n                <span class=\"n\">vysledek</span> <span class=\"o\">+=</span> <span class=\"n\">znak</span>\n                <span class=\"k\">if</span> <span class=\"n\">znak</span> <span class=\"o\">==</span> <span class=\"s2\">&quot;&lt;&quot;</span><span class=\"p\">:</span>\n                    <span class=\"n\">v_tagu</span> <span class=\"o\">=</span> <span class=\"bp\">True</span>\n\n    <span class=\"k\">return</span> <span class=\"n\">vysledek</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"nb\">id</span><span class=\"o\">=</span><span class=\"s2\">&quot;firstHeading&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">text</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">):</span>\n    <span class=\"n\">hlavni_text</span> <span class=\"o\">=</span> <span class=\"n\">soup</span><span class=\"o\">.</span><span class=\"n\">find</span><span class=\"p\">(</span><span class=\"n\">class_</span><span class=\"o\">=</span><span class=\"s2\">&quot;mw-parser-output&quot;</span><span class=\"p\">)</span>\n    <span class=\"k\">for</span> <span class=\"n\">odstavec</span> <span class=\"ow\">in</span> <span class=\"n\">hlavni_text</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;p&quot;</span><span class=\"p\">):</span>\n        <span class=\"n\">html</span> <span class=\"o\">=</span> <span class=\"n\">odstran_zavorky</span><span class=\"p\">(</span><span class=\"nb\">str</span><span class=\"p\">(</span><span class=\"n\">odstavec</span><span class=\"p\">))</span>\n        <span class=\"n\">odstavec</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">html</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">for</span> <span class=\"n\">odkaz</span> <span class=\"ow\">in</span> <span class=\"n\">odstavec</span><span class=\"o\">.</span><span class=\"n\">find_all</span><span class=\"p\">(</span><span class=\"s2\">&quot;a&quot;</span><span class=\"p\">):</span>\n            <span class=\"n\">href</span> <span class=\"o\">=</span> <span class=\"n\">odkaz</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s2\">&quot;href&quot;</span><span class=\"p\">)</span>\n            <span class=\"k\">if</span> <span class=\"n\">href</span><span class=\"o\">.</span><span class=\"n\">startswith</span><span class=\"p\">(</span><span class=\"s2\">&quot;/wiki/&quot;</span><span class=\"p\">):</span>\n                <span class=\"k\">return</span> <span class=\"n\">href</span>\n\n\n<span class=\"k\">def</span> <span class=\"nf\">stahuj</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">):</span>\n    <span class=\"n\">navstivene</span> <span class=\"o\">=</span> <span class=\"nb\">set</span><span class=\"p\">()</span>\n    <span class=\"k\">while</span> <span class=\"bp\">True</span><span class=\"p\">:</span>\n        <span class=\"k\">if</span> <span class=\"n\">stranka</span> <span class=\"ow\">in</span> <span class=\"n\">navstivene</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n        <span class=\"n\">navstivene</span><span class=\"o\">.</span><span class=\"n\">add</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"p\">)</span>\n\n        <span class=\"n\">odpoved</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=\"n\">f</span><span class=\"s2\">&quot;{URL}{stranka}&quot;</span><span class=\"p\">)</span>\n        <span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n\n        <span class=\"n\">soup</span> <span class=\"o\">=</span> <span class=\"n\">BeautifulSoup</span><span class=\"p\">(</span><span class=\"n\">odpoved</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">,</span> <span class=\"s2\">&quot;html.parser&quot;</span><span class=\"p\">)</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">najdi_titulek</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">))</span>\n\n        <span class=\"n\">stranka</span> <span class=\"o\">=</span> <span class=\"n\">najdi_odkaz</span><span class=\"p\">(</span><span class=\"n\">soup</span><span class=\"p\">)</span>\n        <span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"n\">stranka</span><span class=\"p\">:</span>\n            <span class=\"k\">break</span>\n</pre></div>\n    </div>\n</div>\n\n\n        "
    }
  }
}