Nauč se Python > Kurzy > Datový kurz PyLadies > Webscraping (virtuální) > Webscraping

Web scraping

Co je web scraping?

  • Strojové čtení nestrukturovaných dat z webových stránek

Co není web scraping?

  • Stahování dat přes API
  • Stahování strukturovaných dat (JSON, CSV,...)
  • Crawling - procházení a indexování celé webové stránky pomocí jejích vnitřních hypertextových odkazů

Příklady web scrapingu

Jak hodnotila filmy Mirka Spáčilová

Když umíráš a proběhne ti celý život před očima, přijde Mirka Spáčilová a dá tomu 60 %

Opravdu hodnotí Mirka Spáčilová všechny filmy 60 %? Jde to nějak ověřit?

Michal Bláha stáhnul z iDnes celkem 1333 článků Mirky Spáčilové s hodnocením filmů a vytvořil tabulku filmů a jejich hodnocení. Šedesát procent dostal každý třetí film :-)

https://www.michalblaha.cz/2017/10/filmova-kriticka-mirka-spacilova-v-cislech/

Analýza cen na českých e-shopech

Co myslíte, můžeme věřit 80% slevám, které většina českých e-shopů nabízí během akcí typu Black Friday?

V roce 2017 začali v Apify měsíc před Black Friday denně monitorovat ceny všech produktů v největších českých e-shopech. Během Black Friday navíc 4x denně monitorovali ceny produktů v kategorii Black Friday.

Co zjistili? Průměrná uváděná sleva byla kolem 30 %, reálná kolem 20. Stačí zvýšit „původní cenu“ před slevou. Našly se i případy, kdy jste si mohli koupit zboží sice dráž, ale zato s větší slevou.

Projekt se v dalších letech rozrostl, takže dnes si můžeme stáhnout rozšíření do prohlížeče a přesvědčit se sami.


https://blog.apify.com/black-friday-po-%C4%8Desku-kouzla-se-slevami-c7c0d2e7eeaa

https://medium.com/@jakubbalada/black-friday-2019-s-hl%C3%ADda%C4%8Dem-shop%C5%AF-9a3ddd352a8c

Etika web scrapingu

Z čeho se skládá webová stránka

  • HTML (HyperText Markup Language): strukturovaný obsah stránky (text a obrázky)
  • CSS (Cascading Style Sheets): úprava vzhledu stránky
  • JavaScript: interaktivita obsahu a vzhledu stránky

HTML

  • Je tvořen HTML značkami / tagy, např. <img>
  • Většina HTML tagů je párová, např. <h2> a </h2>
  • Tagy mohou mít atributy, které dále specifikují, co a jak bude tag zobrazovat
  • Atribut class se obvykle používá k stylování stránky a často podle něj můžeme při webscrapingu odlišit různé části stránky

CSS

  • Popisuje způsob zobrazení html elementů
  • Obsahuje 2 části: selektor elementu a blok deklarace:
p.error {
  color: red;
}

Instalace

In [ ]:
#%pip install lxml beautifulsoup4 selenium

Pandas: read_html()

Pokud nám stačí stáhnout tabulky z webu, můžeme použít knihovnu Pandas:

In [1]:
import pandas as pd

Převedeme tabulky ze stránky Wikipedie Seznam států světa podle spotřeby alkoholu do dataframů pomocí funce read_html():

In [2]:
tables = pd.read_html('https://cs.wikipedia.org/wiki/Seznam_st%C3%A1t%C5%AF_sv%C4%9Bta_podle_spot%C5%99eby_alkoholu')

Výsledkem je seznam dataframů:

In [3]:
type(tables)
Out[3]:
list
In [4]:
len(tables)
Out[4]:
2
In [5]:
tables[0].head()
Out[5]:
stát evidováno neevidováno celkem pivo víno destiláty ostatní
0 Česko 14.97 1.48 16.45 8.51 2.33 3.59 0.39
1 Maďarsko 12.27 4.00 16.27 4.42 4.94 3.02 0.14
2 Rusko 11.03 4.73 15.76 3.65 0.10 6.88 0.34
3 Ukrajina 8.10 7.50 15.60 2.69 0.58 5.21 0.02
4 Estonsko 13.77 1.80 15.57 5.53 1.09 9.19 0.43

Podle WHO je Česko první! Aspoň v průměrné spotřebě alkoholu v letech 2003-2005.

In [6]:
tables[1].head()
Out[6]:
Pořadí Stát Spotřeba v litrech Rok
0 1 Francie 12.6 2011
1 2 Rakousko 12.2 2009
2 3 Estonsko 12.0 2011
3 4 Německo 11.7 2009
4 5 Irsko 11.6 2011

Podle OECD to vypadá trochu jinak 🧐

Varování: Svět není ideální (proto se v něm taky tolik pije) a ne vždy se podaří tabulky pomocí read_html() získat. Příkaz nemusí tabulku vůbec „vidět“, nebo si nedokáže poradit s její strukturou.

Cvičení

Získejte tabulku aktuálních ekonomických údajů ze stránek Českého statistického úřadu, https://www.czso.cz/csu/czso/aktualniinformace.

Nápověda: Pokud uvidíš v dataframu ošklivé znaky, zkus specifikovat encoding='utf-8'

In [ ]:

Pokud data, která hledáme, nejsou na webu ve formě tabulky, musíme přikročit k „drastičtějším“ nástrojům :-)

BeautifulSoup

Knihovna BeautifulSoup se používá k získávání dat z HTML a XML souborů. Pracuje s různými parsery, které analyzují HTML soubory, a umožnuje vybrat požadované HTML elementy a pracovat s nimi.

Pod čarou: Kdybys měl/a větší projekt a potřeboval/a projít třeba několik desítek e-shopů, podívej se na knihovnu Scrapy, která je pro podobné úkoly vhodnější.

In [7]:
from bs4 import BeautifulSoup

Nejprve si načteme vzorový html dokument ze souboru:

In [8]:
with open("static/html_doc.html", "r") as f:
    html_doc = f.read()

Vytvoříme objekt typu BeautifulSoup. První argument jsou HTML data, druhý slouží k specifikaci parseru.

In [9]:
soup = BeautifulSoup(html_doc, 'html.parser')

Pomocí metody prettify() můžeme vytisknout hezky zformátované html:

In [10]:
#print(soup.prettify())

Po objektu typu BeautifulSoup se můžeme pohybovat pomocí tagů.
První element typu title:

In [11]:
soup.title
Out[11]:
<title>PyData Prague | pydata.cz</title>

První element typu h1:

In [12]:
soup.h1
Out[12]:
<h1><a href="https://pydata.cz/">pydata.cz</a></h1>

Jméno elementu typu h1:

In [13]:
soup.h1.name
Out[13]:
'h1'

Jméno nadřazeného elementu (atributy parent a name):

In [14]:
soup.h1.parent.name
Out[14]:
'div'

Data uvnitř elementu h1 (atribut string)

In [15]:
soup.h1.string
Out[15]:
'pydata.cz'

První element typu h2:

In [16]:
soup.h2
Out[16]:
<h2 id="code-of-conduct">Code of Conduct</h2>

Id elementu h2:

In [17]:
soup.h2['id']
Out[17]:
'code-of-conduct'

Můžeme najít i všechny tagy daného typu, např find_all('a') najde všechny odkazy:

In [18]:
links = soup.find_all('a')
links
Out[18]:
[<a href="https://pydata.cz/">pydata.cz</a>,
 <a class="pydata" href="https://pydata.org/">PyData</a>,
 <a href="https://numfocus.org/">NumFOCUS</a>,
 <a class="pydata" href="https://pydata.org/code-of-conduct/">pydata.org/code-of-conduct/</a>]

Výsledky hledání můžeme procházet:

In [19]:
links[0]['href']
Out[19]:
'https://pydata.cz/'
In [20]:
links[0].text
Out[20]:
'pydata.cz'

Nemusíme hledat jen podle jména tagu, ale i podle jejich atributů:

In [21]:
soup.find(id="pydata-prague")
Out[21]:
<h1 id="pydata-prague">PyData Prague</h1>

Pokud hledáme podle třídy, memůžeme použít soup.find(class="pydata"), protože class je v Pythonu klíčové slovo. Musíme použít class_

In [22]:
soup.find(class_="pydata")
Out[22]:
<a class="pydata" href="https://pydata.org/">PyData</a>

class můžeme taky napsat jako klíč v parametru attrs:

In [23]:
soup.find(attrs={'class':'pydata'})
Out[23]:
<a class="pydata" href="https://pydata.org/">PyData</a>

Cvičení

Vyberte odstavec s id description.

In [ ]:

Vyberte všechny odkazy s třídou pydata

In [ ]:

Requests

Knihovna requests je určená pro HTTP dotazy. V našem případě ji budeme používat pro získání textu webové stránky.

HTTP požadavky lze vytvářet i pomocí standardní knihovny Pythonu, ale requests mají mnohem lidštější rozhraní.

In [24]:
import requests
In [25]:
r = requests.get('https://pydata.cz/')

Můžeme zkontrolovat návratový stav:

In [26]:
r.status_code
Out[26]:
200

Stavové kódy se dělí do 5 skupin:

  • 1xx – informační odpověď
  • 2xx – úspěch
  • 3xx – přesměrování
  • 4xx – chyba klienta
  • 5xx – chyba serveru

Metoda text ukáže zdrojový kód stránky:

In [27]:
#print(r.text)

BeautifulSoup podruhé: CSS selektory

S knihovnou BeautifulSoup můžeme vyhledávat CSS selektory pomocí funkcí select (ukáže všechny prvky) a select_one (najde první prvek).

Otevřeme si html dokument s programem kin. Je stažený z https://dokina.tiscali.cz/program-kin, ale protože by se mohlo stát, že dnes na programu nic není, použijeme radši staženou verzi. Pokud na stránkách něco je, můžeš samozřejmě použít requests.

In [28]:
with open("static/kina.html", "r") as f:
    html_doc = f.read()
soup = BeautifulSoup(html_doc)

Zkusíme najít všechny tagu s třídou title a několik z nich si vypíšeme:
(Kvůli přehlednosti nevypisujeme úplně všechny)

In [29]:
titles = soup.select('.title') 
titles[:6]
Out[29]:
[<h4 class="title mb-0">3Bobule</h4>,
 <h4 class="title mb-0">Tiché místo: Část II</h4>,
 <h4 class="title mb-0">V síti</h4>,
 <h4 class="title mb-0">Princezna zakletá v čase</h4>,
 <h4 class="title mb-0">Bloodshot</h4>,
 <h3 class="title mb-0">
 <a data-ga-action="cinema-detail" data-ga-category="program-kin" href="https://dokina.tiscali.cz/evropsky-dum-7435" title="Profil kina">Evropský dům</a>
 <a class="favourite-toggler" data-ga-action="toggle-favorite-cinema" data-ga-category="program-kin" data-id="7435" href="https://dokina.tiscali.cz/program-kin?movie_id=&amp;place_id=&amp;cinema_type=&amp;town_id=&amp;start=25-03-2020#" title="Přidat do oblíbených"></a>
 </h3>]

Najdeme všechny tagy typu <h3 class="title mb-0"> a podíváme se na první z nich:

In [30]:
mb0_titles = soup.select('h3.title.mb-0')
mb0_titles[0]
Out[30]:
<h3 class="title mb-0">
<a data-ga-action="cinema-detail" data-ga-category="program-kin" href="https://dokina.tiscali.cz/evropsky-dum-7435" title="Profil kina">Evropský dům</a>
<a class="favourite-toggler" data-ga-action="toggle-favorite-cinema" data-ga-category="program-kin" data-id="7435" href="https://dokina.tiscali.cz/program-kin?movie_id=&amp;place_id=&amp;cinema_type=&amp;town_id=&amp;start=25-03-2020#" title="Přidat do oblíbených"></a>
</h3>

Najdeme všechny tagy s třídou title uvnitř tagů s třídou movie-item

In [31]:
soup.select('.movie-item .title ')
Out[31]:
[<h4 class="title mb-0">3Bobule</h4>,
 <h4 class="title mb-0">Tiché místo: Část II</h4>,
 <h4 class="title mb-0">V síti</h4>,
 <h4 class="title mb-0">Princezna zakletá v čase</h4>,
 <h4 class="title mb-0">Bloodshot</h4>,
 <h4 class="title mb-0">A dýchejte klidně</h4>,
 <h4 class="title mb-0">Sviňa</h4>,
 <h4 class="title mb-0">Králíček Jojo</h4>,
 <h4 class="title mb-0">La belle saison</h4>,
 <h4 class="title mb-0">Výjimeční</h4>,
 <h4 class="title mb-0">La Llorona</h4>,
 <h4 class="title mb-0">Morgiana</h4>,
 <h4 class="title mb-0">Noční ostraha</h4>,
 <h4 class="title mb-0">Vita and Virginia</h4>,
 <h4 class="title mb-0">Volej mámu</h4>,
 <h4 class="title mb-0">Čas beznaděje</h4>,
 <h4 class="title mb-0">Šarlatán</h4>,
 <h4 class="title mb-0">Žaluji!</h4>]

Nástroje v prohlížeči

Než začneme procházet stránku pomocí Pythonu, je dobré podívat se na její strukturu přímo v prohlížeči.

Zdrojový kód stránky


Prozkoumat prvek / Inspect




Pozor: Zrojový kód vybraného prvku nemusí odpovídat kódu, který stáhneš pomocí Pythonu. Během zobrazení v prohlížeči mohl být změněn JavaScriptem.

Nástroje pro vývojáře



Poznámka: Pokud v záložce Network nic nevidíš, zkus obnovit stránku.

Příklad z (ne)života

Extrakce dat o 20 nejlepších filmech podle ČSFD

Chceme vytvořit tabulku s následujícími sloupci:

  • Název filmu
  • Průměrné hodnocení
  • Země původu
  • Rok uvedení
  • Délka filmu
  • Režisér

Na scraping není univerzální návod. Obvykle metodou pokus-omyl zkoušíme, co bude fungovat. Pak přijde aktualizace stránek a můžeme začít znovu...

In [32]:
url = 'https://www.csfd.cz/zebricky/nejlepsi-filmy/'
# stránka odmítá GET requesty bez identifikace User-Agenta:
headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36'}
r = requests.get(url, headers=headers)
soup = BeautifulSoup(r.text, 'html.parser')

Úkol:

Podívej se do zdrojového kódu stránky. Jaké prvky budeme na stránce hledat? A kde najdeme informace o režisérovi?

Podíváme se, jak vypadají prvky třídy film:

In [33]:
soup.select('.film')[0]
Out[33]:
<td class="film" id="chart-2294"><a href="/film/2294-vykoupeni-z-veznice-shawshank/">Vykoupení z věznice Shawshank</a> <span class="film-year" dir="ltr">(1994)</span></td>

Abychom získali další informace o filmech, musíme se dostat na stránku filmu. K tomu budeme potřebovat odkazy:

In [34]:
film_odkazy = soup.select('.film a')
film_odkazy[0]
Out[34]:
<a href="/film/2294-vykoupeni-z-veznice-shawshank/">Vykoupení z věznice Shawshank</a>
In [35]:
odkazy = ['https://www.csfd.cz' + film['href'] for film in film_odkazy]
odkazy[0]
Out[35]:
'https://www.csfd.cz/film/2294-vykoupeni-z-veznice-shawshank/'

Rovnou si uložíme i názvy filmů:

In [36]:
nazvy = [film.text for film in film_odkazy]
nazvy[0]
Out[36]:
'Vykoupení z věznice Shawshank'

Průměrné hodnocení najdeme v prvcích s třídou average:

In [37]:
soup.select('.average')[0]
Out[37]:
<td class="average">95,3%</td>
In [38]:
hodnoceni = [aver.text for aver in soup.select('.average')]
hodnoceni[0]
Out[38]:
'95,3%'

Informace o jednotlivých filmech

Nejdřív si zkusíme najít informace o prvním filmu:

In [39]:
r = requests.get(odkazy[0], headers=headers)
film_soup = BeautifulSoup(r.text, 'html.parser')
In [40]:
film_soup.select_one('.origin').text
Out[40]:
'USA, 1994, 142 min'
In [41]:
film_soup.find('span', {'itemprop': 'director'})
Out[41]:
<span data-truncate="60" itemprop="director">
<a href="/tvurce/2869-frank-darabont/">Frank Darabont</a>
</span>
In [42]:
film_soup.find('span', {'itemprop': 'director'}).a.text
Out[42]:
'Frank Darabont'

Hurá! Funguje to. Můžeme to udělat se všemi filmy.

In [43]:
from time import sleep
import re

Poznámka pod čarou: re je modul standardní knihovny pro práci s řetězci pomocí regulárních výrazů. V následujícím kódu ji použijeme pro rozdělení řetězce podle dvou různých znaků.

Potřebujeme vyřešit případy typu 166 min (Director's cut: 175 min, Alternativní: 152 min), proto dělíme text podle znaků "," a "("

In [44]:
zeme = []
roky = []
delky = []
reziseri = []
for odkaz in odkazy:
    r = requests.get(odkaz, headers=headers)
    film_soup = BeautifulSoup(r.text, 'html.parser')
    txt = film_soup.select_one('.origin').text
    puvod, rok, cas, *zbytek = re.split(',|\(', txt) 
    reziser = film_soup.find('span', {'itemprop': 'director'}).a.text
    zeme.append(puvod)
    roky.append(rok)
    delky.append(cas)
    reziseri.append(reziser)
    sleep(1) # chováme se lidsky :-)
In [45]:
filmy_df = pd.DataFrame(zip(nazvy, zeme, roky, reziseri, delky, hodnoceni), 
                        columns=['Název', 
                                 'Původ', 
                                 'Rok', 
                                 'Režie', 
                                 'Délka', 
                                 'ČSFD'])
filmy_df.head()
Out[45]:
Název Původ Rok Režie Délka ČSFD
0 Vykoupení z věznice Shawshank USA 1994 Frank Darabont 142 min 95,3%
1 Forrest Gump USA 1994 Robert Zemeckis 142 min 94,5%
2 Zelená míle USA 1999 Frank Darabont 188 min 92,8%
3 Přelet nad kukaččím hnízdem USA 1975 Miloš Forman 133 min 92,5%
4 Sedm USA 1995 David Fincher 127 min 92,4%

Jak na JavaScript

Pokud webová stránka používá JavaScript k generování obsahu, máme dvě možnosti:

  1. Pochopit, co JavaScript dělá, a zařídit se podle toho
  2. Chovat se jako browser

Příklad

Výsledky závodu Big's Backyard Ultra, https://my.raceresult.com/139372/#0_2C3B48

In [46]:
r = requests.get('https://my.raceresult.com/139372/#0_2C3B48')

Zkusíme najít v textu stránky slovo Gavin.

In [47]:
r.text.find("Gavin")
Out[47]:
-1

Nic. Kde se stala chyba? Vytiskneme si zdrojový kód stránky... a tabulka nikde!

In [48]:
#print(r.text)

Možnost 1: Developer tools - Network

Pokud nám jde o data jen z jedné webové stránky, můžeme pomocí nástrojů pro vývojáře zjistit, co javascript na stránce dělá. Zajímá nás, kde bere stránka data.

Otevřeme si nástroje pro vývojáře a rozklikneme záložku Network. Pokud na ní nic není, stránku refreshujeme. Typicky nás zajímají dotazy typu XMLHttpRequest (XHR), které stránce umožňují získávat data z nějaké URL.


V našem případě vidíme dva dotazy typu XHR, podíváme se na ně blíž, abychom zjistili, který z nich obsahuje hledaná data. Zkopírujeme si adresu odkazu.



In [49]:
r = requests.get('https://my2.raceresult.com/RRPublish/data/list.php?eventid=139372&key=3a52cf488ad3dfe5c994f6b203e7c2e8&listname=Result+Lists%7CLap+Details&page=results&contest=0&r=all&l=0')
In [50]:
#r.text
In [51]:
#r.json()['data']
In [52]:
r.json()['data']['#1_1///Gavin Woody///40Laps']
Out[52]:
[['1', '1', '7:35:18', '55:19', '04:40'],
 ['1', '2', '8:35:23', '55:24', '04:35'],
 ['1', '3', '9:34:25', '54:26', '05:33'],
 ['1', '4', '10:35:09', '55:10', '04:49'],
 ['1', '5', '11:33:55', '53:56', '06:03'],
 ['1', '6', '12:34:23', '54:24', '05:35'],
 ['1', '7', '13:33:35', '53:36', '06:23'],
 ['1', '8', '14:33:57', '53:58', '06:01'],
 ['1', '9', '15:34:07', '54:08', '05:51'],
 ['1', '10', '16:34:39', '54:40', '05:19'],
 ['1', '11', '17:35:17', '55:18', '04:41'],
 ['1', '12', '18:34:22', '54:23', '05:36'],
 ['1', '13', '19:25:29', '45:30', '14:29'],
 ['1', '14', '20:28:55', '48:56', '11:03'],
 ['1', '15', '21:30:22', '50:23', '09:36'],
 ['1', '16', '22:27:05', '47:06', '12:53'],
 ['1', '17', '23:28:05', '48:06', '11:53'],
 ['1', '18', '24:28:35', '48:36', '11:23'],
 ['1', '19', '25:27:17', '47:18', '12:41'],
 ['1', '20', '26:28:12', '48:13', '11:46'],
 ['1', '21', '27:28:15', '48:16', '11:43'],
 ['1', '22', '28:26:35', '46:36', '13:23'],
 ['1', '23', '29:26:29', '46:30', '13:29'],
 ['1', '24', '30:27:35', '47:36', '12:23'],
 ['1', '25', '31:34:26', '54:27', '05:32'],
 ['1', '26', '32:32:42', '52:43', '07:16'],
 ['1', '27', '33:32:27', '52:28', '07:31'],
 ['1', '28', '34:34:26', '54:27', '05:32'],
 ['1', '29', '35:33:34', '53:35', '06:24'],
 ['1', '30', '36:33:37', '53:38', '06:21'],
 ['1', '31', '37:33:08', '53:09', '06:50'],
 ['1', '32', '38:34:02', '54:03', '05:56'],
 ['1', '33', '39:33:41', '53:42', '06:17'],
 ['1', '34', '40:33:59', '54:00', '05:59'],
 ['1', '35', '41:33:41', '53:42', '06:17'],
 ['1', '36', '42:36:38', '56:39', '03:20'],
 ['1', '37', '43:34:18', '54:19', '05:40'],
 ['1', '38', '44:33:52', '53:53', '06:06'],
 ['1', '39', '45:30:33', '50:34', '09:25'],
 ['1', '40', '46:31:21', '51:22', '08:37']]

Možnost 2: Headless browsers

Selenium je knihovna určená na automatizaci testování webových aplikací. Umožňuje spustit a ovládat prohlížeč, takže s ní můžete dělat prakticky všechno, co obvykle děláte na webu. Třeba automaticky vyplňovat formuláře nebo stahovat data.

In [53]:
from selenium import webdriver
from selenium.webdriver.firefox.options import Options  
#from selenium.webdriver.chrome.options import Options  
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
In [54]:
options = Options() 
options.headless = True

Nemáš-li chromedriver nebo geckodriver (pro Firefox), stáhneš je tady:

In [55]:
driver = webdriver.Firefox(options=options)
#driver = webdriver.Chrome(options=options)
driver.get('https://my3.raceresult.com/139372/#0_2C3B48')

Stáhneme tabulku s třídou MainTable:

In [56]:
try: # čekáme 5 s, jestli se tabulka natáhne 
    table = WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CLASS_NAME, 'MainTable'))) 
except TimeoutException: 
    print("Time out!")
In [57]:
table
Out[57]:
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="a900b51a-9168-024d-ae19-5291d48f166d", element="8744ffeb-6ae3-0b43-983b-24ebdf28152c")>
In [58]:
txt = table.text
In [59]:
print(txt[:1000])
  #
Measurement
Lap Split
Rest Time
  1
Gavin Woody
40Laps
1 7:35:18 55:19 04:40  
2 8:35:23 55:24 04:35  
3 9:34:25 54:26 05:33  
4 10:35:09 55:10 04:49  
5 11:33:55 53:56 06:03  
6 12:34:23 54:24 05:35  
7 13:33:35 53:36 06:23  
8 14:33:57 53:58 06:01  
9 15:34:07 54:08 05:51  
10 16:34:39 54:40 05:19  
11 17:35:17 55:18 04:41  
12 18:34:22 54:23 05:36  
13 19:25:29 45:30 14:29  
14 20:28:55 48:56 11:03  
15 21:30:22 50:23 09:36  
16 22:27:05 47:06 12:53  
17 23:28:05 48:06 11:53  
18 24:28:35 48:36 11:23  
19 25:27:17 47:18 12:41  
20 26:28:12 48:13 11:46  
21 27:28:15 48:16 11:43  
22 28:26:35 46:36 13:23  
23 29:26:29 46:30 13:29  
24 30:27:35 47:36 12:23  
25 31:34:26 54:27 05:32  
26 32:32:42 52:43 07:16  
27 33:32:27 52:28 07:31  
28 34:34:26 54:27 05:32  
29 35:33:34 53:35 06:24  
30 36:33:37 53:38 06:21  
31 37:33:08 53:09 06:50  
32 38:34:02 54:03 05:56  
33 39:33:41 53:42 06:17  
34 40:33:59 54:00 05:59  
35 41:33:41 53:42 06:17  
36 42:36:38 56:39 03:20  
37 43:34:18 54:19
In [60]:
driver.close()
driver.quit()

Závěrem

Naučili jsme se jak získat data i bez API nebo CSV soubourů. Nesmíme u toho ale zapomínat na dodržování zákonů a etiky. Webscraping zkoušej vždy až jako poslední možnost, stačí sebemenší změna na stránce a tvůj postup přestane fungovat.


Toto je stránka lekce z kurzu, který probíhá nebo proběhl naživo s instruktorem.