Webové API

Jak už bylo řečeno v lekci o JSON, hlavní výhoda formátu JSON je, že se na Internetu rozšířil nejvíc. Pojďme toho využít!

Spousta webových služeb poskytuje takzvané API (z application programming interface, programátorské rozhraní), přes které je možné s danou službou komunikovat programově. Místo klikání na tlačítka a čtení stránek „očima” dostaneme data ve formátu, kterým rozumí počítače – a v dnešní době to bude většinou formát JSON.

Z minulých lekcí bys měla mít založený účet na github.com, tak se zkusme zeptat Githubu, co o nás ví.

Autorizace

První, a mnohdy nejsložitější, krok k použití API je přihlášení. Počítače se totiž přihlašují jinak než lidi a problematika bezpečnosti a oprávnění by vydala na samostatný kurz. My to uděláme co nejjednodušeji, ať se rychle dostaneme k jádru věci:

  • Přihlaš se na github.com.
  • Jdi na nastavení Personal Accesss Tokens.
  • Vytvoř si nový token ("Generate new token"). Nezaškrtávej žádná oprávnění navíc.
  • Zkopíruj si heslo, které takto dostaneš, do souboru token.txt.

Pozor!

Vygenerovaný kód je heslo, které držitele opravňuje pracovat s Githubem pod tvým jménem! Drž ho v tajnosti. Kdyby se přece jen dostalo „ven”, na stránce Personal Accesss Tokens ho deaktivuj.

Requests

K práci s internetovými stránkami použijeme knihovnu Requests. V aktivovaném virtuálním prostředí si ji nainstaluj příkazem:

(env)$ python -m pip install requests

A potom v Pythonu zkus stáhnout nějakou stránku:

import requests

# stažení stránky
stranka = requests.get('https://github.com')

# ověření, že dotaz proběhl v pořádku
stranka.raise_for_status()

# vypsání obsahu
print(stranka.text)

Měl by se vypsat obsah stránky https://github.com – HTML kód, který se objeví když v prohlížeči dáš „Ukázat zdroj” (View Page Source, většinou Ctrl+U) a ze kterého prohlížeč umí vykreslit stránku.

Ale my nechceme obsah pro lidi. Podívejme se, co Github zpřístupňuje počítačům.

Uživatelský účet

Zkus, co dělá tento kód:

import requests

with open('token.txt') as soubor:
    token = soubor.read().strip()

headers = {'Authorization': 'token ' + token}

stranka = requests.get('https://api.github.com/user', headers=headers)
stranka.raise_for_status()
print(stranka.text)

Co se stalo? Tím, že jsi Githubu dala svůj token (načtený ze souboru, předaný přes slovník headers), poznal, že jde dotaz od tebe a vrátil nějaké informace ve formátu JSON.

Zkus řetězec stranka.text převést z JSON na slovník a vypsat trochu srozumitelněji:

data = json.loads(stranka.text)

print(json.dumps(data, ensure_ascii=True, indent=2))

Teď už je lépe vidět celý tvůj profil (možná včetně neveřejných informací – proto musíš svůj token udržovat v tajnosti).

S profilem, který máš v proměnné data, se dá pracovat jako s každým jiným slovníkem. Třeba adresu svého obrázku můžeš vypsat pomocí:

print(data['avatar_url'])

API Githubu

API Githubu toho umí mnohem víc. Třeba na adrese https://api.github.com/emojis na tebe čeká slovník s adresami malých obrázků. (Tenhle slovník funguje jako vyhledávací tabulka.) Celé API je zdokumentováno na adrese developer.github.com.

Interakce

Pomocí webových API se dají informace nejen číst, ale i měnit.

Na stránce github.com/pyvec/naucse.python.cz/stargazers je seznam lidí, kteří „ohvězdičkovali” tyto učební ateriály. Je jich zatím málo; pojďme se k nim pomocí webového API přidat.

Napřed svému tokenu (na Githubu v nastavení Personal Accesss Tokens) přidej právo public_repo. Od teď token střež obzvlášť pečlivě, protože se pomocí něj dají informace na Githubu i měnit.

Chceme-li měnit informace, musíme knihovně Requests říct, aby použila jinou „HTTP metodu” než GET. Co to přesně jsou HTTP metody je na trochu delší povídání (viz Wikipedia), ale stručně řečeno, pomocí GET se většinou stahuje obsah, pomocí POST se přidává nový, PUT mění něco, co už na webu existuje a DELETE něco maže. Jakou metodu poujeme závisí na tom, co chceme udělat; většinou to bude POST, PUT nebo DELETE.

Podle dokumentace Githubu se přidání hvězdičky dělá pomocí PUT dotazu na adresu /user/starred/:owner/:repo. Za :owner a :repo dosadíš vlastníka a jméno repozitáře (v našem případě pyvec a naucse.python.cz) a PUT metodu zvolíš tak, že zavoláš místo get funkci put:

import requests

with open('token.txt') as soubor:
    token = soubor.read().strip()

headers = {'Authorization': 'token ' + token}

stranka = requests.put('https://api.github.com/user/starred/pyvec/naucse.python.cz', headers=headers)
stranka.raise_for_status()

Tenhle dotaz nevrátí žádný text, ale na github.com/pyvec/naucse.python.cz/stargazers se můžeš přesvědčit, že to funguje.

Chceš-li hvězdičku zase odstranit, použij metodu DELETE na stejnou adresu. (Ale nezapomeň tam pak ★ zase vrátit! ☺)

{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2018/pyladies-praha-podzim-ntk:dict:2",
      "title": "Github API",
      "html": "\n          \n    \n\n    <h1>Webov&#xE9; API</h1>\n<p>Jak u&#x17E; bylo &#x159;e&#x10D;eno v&#xA0;<a href=\"/2018/pyladies-praha-podzim-ntk/intro/json/\">lekci o JSON</a>,\nhlavn&#xED; v&#xFD;hoda form&#xE1;tu JSON je, &#x17E;e se na Internetu roz&#x161;&#xED;&#x159;il nejv&#xED;c.\nPoj&#x10F;me toho vyu&#x17E;&#xED;t!</p>\n<p>Spousta webov&#xFD;ch slu&#x17E;eb poskytuje takzvan&#xE9;\n<em>API</em> (z&#xA0;<em>application programming interface</em>,\nprogram&#xE1;torsk&#xE9; rozhran&#xED;), p&#x159;es kter&#xE9; je mo&#x17E;n&#xE9; s danou\nslu&#x17E;bou komunikovat programov&#x11B;.\nM&#xED;sto klik&#xE1;n&#xED; na tla&#x10D;&#xED;tka a &#x10D;ten&#xED; str&#xE1;nek &#x201E;o&#x10D;ima&#x201D;\ndostaneme data ve form&#xE1;tu, kter&#xFD;m rozum&#xED; po&#x10D;&#xED;ta&#x10D;e &#x2013;\na v&#xA0;dne&#x161;n&#xED; dob&#x11B; to bude v&#x11B;t&#x161;inou form&#xE1;t JSON.</p>\n<p>Z&#xA0;minul&#xFD;ch lekc&#xED; bys m&#x11B;la m&#xED;t zalo&#x17E;en&#xFD; &#xFA;&#x10D;et na github.com,\ntak se zkusme zeptat Githubu, co o n&#xE1;s v&#xED;.</p>\n<h2>Autorizace</h2>\n<p>Prvn&#xED;, a mnohdy nejslo&#x17E;it&#x11B;j&#x161;&#xED;, krok k pou&#x17E;it&#xED; API\nje p&#x159;ihl&#xE1;&#x161;en&#xED;. Po&#x10D;&#xED;ta&#x10D;e se toti&#x17E; p&#x159;ihla&#x161;uj&#xED; jinak\nne&#x17E; lidi a problematika bezpe&#x10D;nosti a opr&#xE1;vn&#x11B;n&#xED; by vydala na samostatn&#xFD; kurz.\nMy to ud&#x11B;l&#xE1;me co nejjednodu&#x161;eji, a&#x165; se rychle dostaneme k&#xA0;j&#xE1;dru v&#x11B;ci:</p>\n<ul>\n<li>P&#x159;ihla&#x161; se na <a href=\"https://github.com\">github.com</a>.</li>\n<li>Jdi na <a href=\"https://github.com/settings/tokens\">nastaven&#xED; Personal Accesss Tokens</a>.</li>\n<li>Vytvo&#x159; si nov&#xFD; token (&quot;Generate new token&quot;). Neza&#x161;krt&#xE1;vej &#x17E;&#xE1;dn&#xE1; opr&#xE1;vn&#x11B;n&#xED; nav&#xED;c.</li>\n<li>Zkop&#xED;ruj si heslo, kter&#xE9; takto dostane&#x161;, do souboru <code>token.txt</code>.</li>\n</ul>\n<div class=\"admonition warning\"><p class=\"admonition-title\">Pozor!</p>\n<p>Vygenerovan&#xFD; k&#xF3;d je heslo, kter&#xE9; dr&#x17E;itele\noprav&#x148;uje pracovat s Githubem pod tv&#xFD;m jm&#xE9;nem!\nDr&#x17E; ho v tajnosti. Kdyby se p&#x159;ece jen dostalo &#x201E;ven&#x201D;, na str&#xE1;nce\n<a href=\"https://github.com/settings/tokens\">Personal Accesss Tokens</a> ho deaktivuj.</p>\n</div><h2>Requests</h2>\n<p>K pr&#xE1;ci s internetov&#xFD;mi str&#xE1;nkami pou&#x17E;ijeme knihovnu Requests.\nV aktivovan&#xE9;m virtu&#xE1;ln&#xED;m prost&#x159;ed&#xED; si ji nainstaluj p&#x159;&#xED;kazem:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(env)$ </span>python -m pip install requests\n</pre></div><p>A potom v Pythonu zkus st&#xE1;hnout n&#x11B;jakou str&#xE1;nku:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n\n<span class=\"c1\"># sta&#x17E;en&#xED; str&#xE1;nky</span>\n<span class=\"n\">stranka</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=\"s1\">&apos;https://github.com&apos;</span><span class=\"p\">)</span>\n\n<span class=\"c1\"># ov&#x11B;&#x159;en&#xED;, &#x17E;e dotaz prob&#x11B;hl v po&#x159;&#xE1;dku</span>\n<span class=\"n\">stranka</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n\n<span class=\"c1\"># vyps&#xE1;n&#xED; obsahu</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">)</span>\n</pre></div><p>M&#x11B;l by se vypsat obsah str&#xE1;nky\n<a href=\"https://github.com\">https://github.com</a> &#x2013;\nHTML k&#xF3;d, kter&#xFD; se objev&#xED; kdy&#x17E; v prohl&#xED;&#x17E;e&#x10D;i d&#xE1;&#x161;\n&#x201E;Uk&#xE1;zat zdroj&#x201D; (<em>View Page Source</em>, v&#x11B;t&#x161;inou <kbd>Ctrl</kbd>+<kbd>U</kbd>)\na ze kter&#xE9;ho prohl&#xED;&#x17E;e&#x10D; um&#xED; vykreslit str&#xE1;nku.</p>\n<p>Ale my nechceme obsah pro lidi.\nPod&#xED;vejme se, co Github zp&#x159;&#xED;stup&#x148;uje po&#x10D;&#xED;ta&#x10D;&#x16F;m.</p>\n<h2>U&#x17E;ivatelsk&#xFD; &#xFA;&#x10D;et</h2>\n<p>Zkus, co d&#x11B;l&#xE1; tento k&#xF3;d:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n\n<span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">&apos;token.txt&apos;</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">soubor</span><span class=\"p\">:</span>\n    <span class=\"n\">token</span> <span class=\"o\">=</span> <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">read</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">strip</span><span class=\"p\">()</span>\n\n<span class=\"n\">headers</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s1\">&apos;Authorization&apos;</span><span class=\"p\">:</span> <span class=\"s1\">&apos;token &apos;</span> <span class=\"o\">+</span> <span class=\"n\">token</span><span class=\"p\">}</span>\n\n<span class=\"n\">stranka</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=\"s1\">&apos;https://api.github.com/user&apos;</span><span class=\"p\">,</span> <span class=\"n\">headers</span><span class=\"o\">=</span><span class=\"n\">headers</span><span class=\"p\">)</span>\n<span class=\"n\">stranka</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\">stranka</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">)</span>\n</pre></div><p>Co se stalo? T&#xED;m, &#x17E;e jsi Githubu dala sv&#x16F;j token\n(na&#x10D;ten&#xFD; ze souboru, p&#x159;edan&#xFD; p&#x159;es slovn&#xED;k <code>headers</code>),\npoznal, &#x17E;e jde dotaz od tebe a vr&#xE1;til n&#x11B;jak&#xE9; informace\nve form&#xE1;tu JSON.</p>\n<p>Zkus &#x159;et&#x11B;zec <code>stranka.text</code> p&#x159;ev&#xE9;st z&#xA0;JSON na slovn&#xED;k\na vypsat trochu srozumiteln&#x11B;ji:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">data</span> <span class=\"o\">=</span> <span class=\"n\">json</span><span class=\"o\">.</span><span class=\"n\">loads</span><span class=\"p\">(</span><span class=\"n\">stranka</span><span class=\"o\">.</span><span class=\"n\">text</span><span class=\"p\">)</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">json</span><span class=\"o\">.</span><span class=\"n\">dumps</span><span class=\"p\">(</span><span class=\"n\">data</span><span class=\"p\">,</span> <span class=\"n\">ensure_ascii</span><span class=\"o\">=</span><span class=\"bp\">True</span><span class=\"p\">,</span> <span class=\"n\">indent</span><span class=\"o\">=</span><span class=\"mi\">2</span><span class=\"p\">))</span>\n</pre></div><p>Te&#x10F; u&#x17E; je l&#xE9;pe vid&#x11B;t cel&#xFD; tv&#x16F;j profil\n(mo&#x17E;n&#xE1; v&#x10D;etn&#x11B; neve&#x159;ejn&#xFD;ch informac&#xED; &#x2013; proto mus&#xED;&#x161; sv&#x16F;j token\nudr&#x17E;ovat v tajnosti).</p>\n<p>S&#xA0;profilem, kter&#xFD; m&#xE1;&#x161; v&#xA0;prom&#x11B;nn&#xE9; <code>data</code>,\nse d&#xE1; pracovat jako s ka&#x17E;d&#xFD;m jin&#xFD;m slovn&#xED;kem.\nT&#x159;eba adresu sv&#xE9;ho obr&#xE1;zku m&#x16F;&#x17E;e&#x161; vypsat pomoc&#xED;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">data</span><span class=\"p\">[</span><span class=\"s1\">&apos;avatar_url&apos;</span><span class=\"p\">])</span>\n</pre></div><h2>API Githubu</h2>\n<p>API Githubu toho um&#xED; mnohem v&#xED;c. T&#x159;eba na adrese\n<a href=\"https://api.github.com/emojis\">https://api.github.com/emojis</a> na tebe &#x10D;ek&#xE1;\nslovn&#xED;k s adresami mal&#xFD;ch obr&#xE1;zk&#x16F;.\n(Tenhle slovn&#xED;k funguje jako vyhled&#xE1;vac&#xED; tabulka.)\nCel&#xE9; API je zdokumentov&#xE1;no na adrese\n<a href=\"https://developer.github.com/v3/\">developer.github.com</a>.</p>\n<h2>Interakce</h2>\n<p>Pomoc&#xED; webov&#xFD;ch API se daj&#xED; informace nejen &#x10D;&#xED;st, ale i m&#x11B;nit.</p>\n<p>Na str&#xE1;nce\n<a href=\"https://github.com/pyvec/naucse.python.cz/stargazers\">github.com/pyvec/naucse.python.cz/stargazers</a>\nje seznam lid&#xED;, kte&#x159;&#xED; &#x201E;ohv&#x11B;zdi&#x10D;kovali&#x201D; tyto u&#x10D;ebn&#xED; ateri&#xE1;ly.\nJe jich zat&#xED;m m&#xE1;lo; poj&#x10F;me se k nim pomoc&#xED; webov&#xE9;ho API p&#x159;idat.</p>\n<p>Nap&#x159;ed sv&#xE9;mu tokenu (na Githubu v nastaven&#xED;\n<a href=\"https://github.com/settings/tokens\">Personal Accesss Tokens</a>)\np&#x159;idej pr&#xE1;vo <code>public_repo</code>.\nOd te&#x10F; token st&#x159;e&#x17E; obzvl&#xE1;&#x161;&#x165; pe&#x10D;liv&#x11B;, proto&#x17E;e se pomoc&#xED;\nn&#x11B;j daj&#xED; informace na Githubu i m&#x11B;nit.</p>\n<p>Chceme-li m&#x11B;nit informace, mus&#xED;me knihovn&#x11B; Requests\n&#x159;&#xED;ct, aby pou&#x17E;ila jinou &#x201E;HTTP metodu&#x201D; ne&#x17E; <code>GET</code>.\nCo to p&#x159;esn&#x11B; jsou HTTP metody je na trochu del&#x161;&#xED; pov&#xED;d&#xE1;n&#xED;\n(viz <a href=\"https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\">Wikipedia</a>),\nale stru&#x10D;n&#x11B; &#x159;e&#x10D;eno, pomoc&#xED; <code>GET</code> se v&#x11B;t&#x161;inou stahuje\nobsah, pomoc&#xED; <code>POST</code> se p&#x159;id&#xE1;v&#xE1; nov&#xFD;,\n<code>PUT</code> m&#x11B;n&#xED; n&#x11B;co, co u&#x17E; na webu existuje\na <code>DELETE</code> n&#x11B;co ma&#x17E;e.\nJakou metodu poujeme z&#xE1;vis&#xED; na tom, co chceme ud&#x11B;lat;\nv&#x11B;t&#x161;inou to bude <code>POST</code>, <code>PUT</code> nebo <code>DELETE</code>.</p>\n<p>Podle <a href=\"https://developer.github.com/v3/activity/starring/#star-a-repository\">dokumentace Githubu</a>\nse p&#x159;id&#xE1;n&#xED; hv&#x11B;zdi&#x10D;ky d&#x11B;l&#xE1; pomoc&#xED; <code>PUT</code>\ndotazu na adresu <code>/user/starred/:owner/:repo</code>.\nZa <code>:owner</code> a <code>:repo</code>\ndosad&#xED;&#x161; vlastn&#xED;ka a jm&#xE9;no repozit&#xE1;&#x159;e\n(v na&#x161;em p&#x159;&#xED;pad&#x11B; <code>pyvec</code> a <code>naucse.python.cz</code>)\na <code>PUT</code> metodu zvol&#xED;&#x161; tak, &#x17E;e zavol&#xE1;&#x161; m&#xED;sto <code>get</code> funkci <code>put</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">requests</span>\n\n<span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">&apos;token.txt&apos;</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">soubor</span><span class=\"p\">:</span>\n    <span class=\"n\">token</span> <span class=\"o\">=</span> <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">read</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">strip</span><span class=\"p\">()</span>\n\n<span class=\"n\">headers</span> <span class=\"o\">=</span> <span class=\"p\">{</span><span class=\"s1\">&apos;Authorization&apos;</span><span class=\"p\">:</span> <span class=\"s1\">&apos;token &apos;</span> <span class=\"o\">+</span> <span class=\"n\">token</span><span class=\"p\">}</span>\n\n<span class=\"n\">stranka</span> <span class=\"o\">=</span> <span class=\"n\">requests</span><span class=\"o\">.</span><span class=\"n\">put</span><span class=\"p\">(</span><span class=\"s1\">&apos;https://api.github.com/user/starred/pyvec/naucse.python.cz&apos;</span><span class=\"p\">,</span> <span class=\"n\">headers</span><span class=\"o\">=</span><span class=\"n\">headers</span><span class=\"p\">)</span>\n<span class=\"n\">stranka</span><span class=\"o\">.</span><span class=\"n\">raise_for_status</span><span class=\"p\">()</span>\n</pre></div><p>Tenhle dotaz nevr&#xE1;t&#xED; &#x17E;&#xE1;dn&#xFD; text, ale na\n<a href=\"https://github.com/pyvec/naucse.python.cz/stargazers\">github.com/pyvec/naucse.python.cz/stargazers</a>\nse m&#x16F;&#x17E;e&#x161; p&#x159;esv&#x11B;d&#x10D;it, &#x17E;e to funguje.</p>\n<p>Chce&#x161;-li hv&#x11B;zdi&#x10D;ku zase odstranit, pou&#x17E;ij metodu\n<code>DELETE</code> na stejnou adresu.\n(Ale nezapome&#x148; tam pak &#x2605; zase vr&#xE1;tit! &#x263A;)</p>\n\n\n        "
    }
  }
}