18  Duomenų gavimas naudojant API

Internete duomenys pateikiami ne tik kaip atsisiunčiami .csv, .xlsx ar kitų formatų failai. Dalis duomenų yra pasiekiami per programų sąsajas (angl. Application Programming Interface, API). API leidžia programai automatiškai paprašyti konkrečių duomenų ir gauti juos kompiuteriui patogiu formatu.

Naudojant API nebereikia kiekvieną kartą rankiniu būdu atidaryti interneto svetainės, pasirinkti filtrų ir atsisiųsti failo. Pavyzdžiui, R programa gali pati suformuoti užklausą, ją išsiųsti, patikrinti gautą atsakymą ir paversti duomenis į vektorius, sąrašus ar duomenų lenteles.

Šiame skyriuje aptarsime:

18.1 Kas yra API?

API galima suprasti kaip iš anksto apibrėžtą taisyklių rinkinį, pagal kurį viena programa gali bendrauti su kita programa. API nurodo:

  • kokių duomenų arba veiksmų galima prašyti,
  • kokiu adresu siųsti užklausą,
  • kokius parametrus galima ar būtina pateikti,
  • kaip turi būti patvirtinta naudotojo tapatybė,
  • kokio formato atsakymas bus grąžintas,
  • kokios klaidos gali atsirasti.

Nepainiokite API su duomenų baze! API paprasčiausiai yra sąsaja, per kurią klientui (užklausėjui) suteikiama kontroliuojama prieiga prie norimo serverio saugomų duomenų ar funkcijų.

Klientas ir serveris

API komunikacija paprastai vyksta tarp dviejų pusių:

  • kliento – programa, kuri siunčia užklausą (mūsų atveju klientas bus R),
  • serverio – kompiuteris arba sistema, kuri priima užklausą, ją apdoroja ir grąžina atsakymą.

Bendra komunikacijos eiga yra tokia:

  1. klientas suformuoja užklausą su kreipimuosi dėl norimo ištekliaus,
  2. užklausa internetu nusiunčiama serveriui,
  3. serveris patikrina užklausą ir atlieka prašomą veiksmą,
  4. serveris grąžina atsakymą,
  5. klientas patikrina atsakymą ir toliau naudoja jo turinį.

Šaltinis: https://www.postman.com/what-is-an-api/

Šį procesą galima palyginti su užsakymu restorane. Klientas pasirenka patiekalą ir pateikia užsakymą padavėjui. Padavėjas perduoda tiksliai suformuluotą užsakymą virtuvei, jį įvykdo ir grąžina rezultatą. Klientui nebūtina žinoti, kaip organizuojamas virtuvės darbas. Panašiai API naudotojui nebūtina žinoti, kaip serverio viduje saugomi ar apskaičiuojami duomenys ir kiti ištekliai.

Toliau aptarsime pagrindinius API objektus.

Išteklius

Išteklius (angl. resource) yra objektas arba duomenų rinkinys, su kuriuo norime atlikti veiksmą. Ištekliais gali būti įvairūs duomenys, pavyzdžiui, naudotojų sąrašas, vieno vartotojo duomenys, įvairūs ekonominiai duomenys, dokumentai ir t. t.

Kiekvienas išteklius turi adresą. API kontekste toks adresas dažnai vadinamas galiniu tašku (angl. endpoint). Pavyzdžiui, viešoje mokomojoje API naudojamas toks adresas:

https://jsonplaceholder.typicode.com/users

Šiuo adresu pasiekiamas visų naudotojų išteklius (/users). Pridėjus konkretų identifikatorių, galima kreiptis į vieno naudotojo išteklių:

https://jsonplaceholder.typicode.com/users/1

API adresą galima išskaidyti į kelias dalis:

https://jsonplaceholder.typicode.com/users/1
\______/\__________________________/\______/
 schema         domenas              kelias 
  • https nurodo komunikacijos protokolą,
  • jsonplaceholder.typicode.com yra serverio domenas,
  • /users/1 yra kelias iki konkretaus ištekliaus.

Bendroji, visiems tos pačios API ištekliams pasikartojanti adreso dalis dar vadinama baziniu adresu (angl. base URL), o konkretų išteklių nusakanti dalis – galiniu tašku.

PastabaURI ir URL

API dokumentacijose galima sutikti terminus URL (angl. Uniform Resource Locator) ir URI (angl. Uniform Resource Identifier). Praktiniuose šio skyriaus pavyzdžiuose vartosime paprastesnį terminą adresas arba URL.

Užklausa

Užklausa (angl. request) yra kliento siunčiamas pranešimas serveriui. Užklausoje nurodoma:

  • į kokį išteklių kreipiamasi,
  • koks veiksmas turi būti atliktas,
  • kokie papildomi parametrai perduodami,
  • prireikus, ir autentifikavimo informacija,
  • kai kurių metodų atveju – siunčiami duomenys.

Interneto API dažniausiai naudoja HTTP protokolą. Naršyklė tuo pačiu protokolu prašo serverio pateikti interneto puslapį, o R gali juo paprašyti pateikti struktūrizuotus duomenis.

Užklausos parametrai

Dažnai nenorime gauti visų API turimų duomenų. Užklausos parametrais galima nurodyti filtrus, laikotarpį, rūšiavimo tvarką, puslapio numerį ar kitus pasirinkimus.

Parametrai URL adrese rašomi po klaustuko ?. Kiekvienas parametras sudaromas kaip rakto ir reikšmės pora, o kelios poros atskiriamos simboliu &. Pavyzdžiui:

https://jsonplaceholder.typicode.com/comments?postId=1
  • išteklius yra /comments;
  • parametro pavadinimas yra postId;
  • parametro reikšmė yra 1.

Keliais parametrais papildytas adresas galėtų atrodyti taip:

https://api.example.com/data?year=2025&region=LT

Tačiau kode parametrų nebūtina rankiniu būdu jungti į vieną ilgą tekstą. Vėliau aptarsime būdus, kaip juos perduoti kaip atskirą parametrų sąrašą.

HTTP metodai

HTTP metodas nurodo, kokį veiksmą klientas nori atlikti su ištekliumi. Dažniausi API metodai siejami su keturiais pagrindiniais duomenų tvarkymo veiksmais: sukurti, nuskaityti, atnaujinti ir pašalinti (angl. Create, Read, Update, Delete, CRUD).

HTTP metodas Veiksmas Paskirtis
POST Create sukurti naują išteklių arba inicijuoti veiksmą
GET Read gauti išteklių ar jo atvaizdą
PUT Update visiškai pakeisti esamą išteklių
PATCH Update iš dalies pakeisti esamą išteklių
DELETE Delete pašalinti išteklių

Kartais vartojamas platesnis trumpinys CRUDE, kur raidė E reiškia Execute – serveriui pavedama įvykdyti tam tikrą operaciją. Atskiro universalaus HTTP metodo EXECUTE nėra. Tokio veiksmo realizavimas priklauso nuo konkrečios API, o operacijai inicijuoti dažnai naudojamas POST metodas.

HTTP būsenos kodai

Labai tikėtina, kad kažkada esate susidūrę su interneto puslapio klaida “404 Not Found”. Šis kodas yra vienas iš daugelio HTTP puslapių būsenos kodų. Būsenos kodas yra triženklis skaičius. Pirmasis skaitmuo nurodo bendrą atsakymo kategoriją. Žemiau lentelėje pateiktos kodų grupės ir tam tikrų kodų pavyzdžiai.

Kodų grupė Reikšmė Pavyzdžiai
1xx informacinis atsakymas užklausa priimta ir dar apdorojama
2xx sėkmingas atsakymas 200 OK, 201 Created, 204 No Content
3xx nukreipimas išteklius pasiekiamas kitu adresu
4xx kliento užklausos klaida 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests
5xx serverio klaida 500 Internal Server Error, 503 Service Unavailable

Šiame skyriuje pagrindinį dėmesį skirsime GET, nes duomenų analizėje API dažniausiai naudojama duomenims gauti, o ne keisti serverio išteklius.

Atliekant GET užklausas dažniausiai sutinkami šie kodai:

  • 200 OK – užklausa įvykdyta, duomenys grąžinti;
  • 400 Bad Request – netinkamai suformuota užklausa;
  • 401 Unauthorized – nepateikti arba netinkami prisijungimo duomenys;
  • 403 Forbidden – klientas neturi teisės pasiekti ištekliaus;
  • 404 Not Found – nurodytas išteklius nerastas;
  • 429 Too Many Requests – per trumpą laiką išsiųsta per daug užklausų;
  • 500 Internal Server Error – serverio vidinė klaida;
  • 503 Service Unavailable – paslauga laikinai nepasiekiama.

Prieš apdorojant atsakymo turinį reikia patikrinti būsenos kodą. Priešingu atveju, klaidos pranešimą galime klaidingai bandyti apdoroti kaip duomenis.

GET metodas

GET užklausa prašo serverio pateikti pasirinkto ištekliaus atvaizdą. Sėkminga užklausa dažniausiai grąžina:

  • HTTP būsenos kodą 200,
  • atsakymo antraštes,
  • atsakymo turinį, dažniausiai JSON formatu.

GET užklausa nekeičia serverio duomenų. Dėl to tą pačią užklausą galima pakartoti norint iš naujo gauti duomenis.

ĮspėjimasDuomenų naudojimo sąlygos

Tai, kad duomenis techniškai galima pasiekti per API, dar nereiškia, kad juos galima naudoti be apribojimų. Reikia susipažinti su API naudojimo sąlygomis, licencija, privatumo reikalavimais ir užklausų skaičiaus ribojimais.

Serverio/API atsakymas

Atsakymas (angl. response) yra serverio klientui grąžinamas pranešimas. HTTP atsakymą paprastai sudaro trys dalys:

  1. būsenos kodas – nurodo, ar užklausa įvykdyta sėkmingai,
  2. antraštės – pateikia informaciją apie atsakymą, pavyzdžiui, turinio tipą,
  3. turinys (angl. body) – grąžinti duomenys arba klaidos aprašymas.

Pavyzdžiui, atsakymo antraštėje gali būti nurodyta:

Content-Type: application/json

Tai reiškia, kad atsakymo turinys pateiktas JSON formatu.

API dokumentacija

Kiekviena API gali turėti skirtingus išteklius, parametrus, autentifikavimo būdus ir atsakymo struktūrą. Todėl API dokumentacija yra praktiškai būtina. Net jeigu galinio taško adresą galima atspėti, be dokumentacijos negalime patikimai žinoti, kaip API turi būti naudojama.

Dokumentacijoje reikėtų rasti:

  • bazinį API adresą,
  • galimus galinius taškus,
  • kiekvieno galinio taško palaikomus HTTP metodus,
  • privalomus ir pasirenkamus parametrus,
  • parametrų tipus ir leistinas reikšmes,
  • autentifikavimo tvarką,
  • grąžinamo atsakymo pavyzdį ir duomenų laukų paaiškinimus,
  • galimus būsenos kodus ir klaidas,
  • puslapiavimo taisykles,
  • užklausų skaičiaus ribojimus,
  • duomenų licenciją ir naudojimo sąlygas.
SvarbuDokumentacija yra API sutartis

API dokumentacija apibrėžia kliento ir serverio komunikacijos taisykles. Kodas, kuris parašytas nesiremiant dokumentacija, gali veikti atsitiktinai, grąžinti ne visus duomenis arba nustoti veikti pasikeitus API.

18.2 Kas yra JSON?

JSON (angl. JavaScript Object Notation) yra tekstinis struktūrizuotų duomenų formatas. Jis plačiai naudojamas API atsakymams, nes yra gana lengvai perskaitomas žmogaus ir lengvai apdorojamas programų.

JSON formato struktūra remiasi:

  • objektais, rašomais riestiniuose skliaustuose {},
  • rakto ir reikšmės poromis, kurios sudaro objektų turinų,
  • masyvais, rašomais laužtiniuose skliaustuose [],
  • įdėtinėmis struktūromis.

Paprastas JSON objektas atrodo taip:

{
  "id": 1,
  "vardas": "Asta",
  "aktyvus": true
}

Objektą sudaro trys rakto ir reikšmės poros. Raktai yra id, vardas ir aktyvus. JSON reikšmės gali būti:

  • tekstas, rašomas dvigubose kabutėse;
  • skaičius;
  • loginė reikšmė true arba false;
  • tuščia reikšmė null;
  • objektas;
  • masyvas.

Keli vienodos struktūros objektai gali būti pateikti masyve:

[
  {
    "id": 1,
    "vardas": "Asta"
  },
  {
    "id": 2,
    "vardas": "Jonas"
  }
]

Tokią struktūrą dažnai galima tiesiogiai paversti R duomenų lentele: kiekvienas objektas tampa eilute (įrašu), raktai tampa stulpelių pavadinimais, o juos atitinkančios reikšmės lentelės langeliu.

JSON objektai gali būti įdėti vienas į kitą (nested):

{
  "id": 1,
  "vardas": "Asta",
  "adresas": {
    "miestas": "Vilnius",
    "gatve": "Gedimino pr."
  },
  "kalbos": ["lietuvių", "anglų"]
}

Šis pavyzdys nėra paprasta stačiakampė lentelė, nes rakto adresas reikšmė yra kitas objektas, o rakto kalbos reikšmė – masyvas. Todėl sudėtingesnis JSON atsakymas R aplinkoje dažnai pirmiausia tampa sąrašu, kurį vėliau reikia pertvarkyti.

PatarimasJSON struktūros suvokimas

Papildomai JSON struktūrą galima peržiūrėti vaizdo įraše „Learn JSON in 10 Minutes“. Svarbiausia atpažinti objektus {}, masyvus [] ir rakto bei reikšmės poras.

18.3 API užklausos naudojant R

R aplinkoje HTTP užklausoms galima naudoti kelis paketus. Šiame skyriuje naudosime:

  • httr2 – HTTP užklausoms formuoti, siųsti ir atsakymams apdoroti,
  • jsonlite – JSON tekstui paversti R objektais,
  • dplyr ir tidyr – gautiems duomenims pertvarkyti.

httr2 yra modernesnis httr paketo tęsinys. Vienas svarbiausių jo principų – užklausa pirmiausia sukuriama kaip objektas, tuomet nuosekliai papildoma keliu, parametrais, antraštėmis ar autentifikavimo informacija ir tik galiausiai išsiunčiama serveriui.

Paketus įdiegiame tik vieną kartą:

Kodas
install.packages(c("httr2", "jsonlite", "dplyr", "tidyr"))

Kiekvienos naujos R sesijos pradžioje paketus įkeliame:

Užklausos sukūrimas su httr2

Pagrindiniams pavyzdžiams naudosime viešą mokomąją JSONPlaceholder API. Jai nereikia registracijos ar autentifikavimo.

Tarkime, norime pasiimti naudotojų duomenis iš:

https://jsonplaceholder.typicode.com/users

Bazinę API adreso dalį galime saugoti atskirai:

Kodas
pagrindinis_url <- "https://jsonplaceholder.typicode.com"

Užklausa kuriama su funkcija request():

Kodas
uzklausa <- request(base_url = pagrindinis_url)
uzklausa

Sukurtas objektas tik aprašo būsimą HTTP užklausą, todėl jis dar nėra išsiųstas serveriui. Prie bazinio adreso pridedame kelią iki ištekliaus:

Kodas
uzklausa <- req_url_path(
  req = uzklausa,
  path = "/users"
)

Nurodome HTTP metodą:

Kodas
uzklausa <- req_method(
  req = uzklausa,
  method = "GET"
)

Taigi užklausa buvo konstruojama trimis moduliariais žingsniais:

request()
    ↓
req_url_path()
    ↓
req_method()
    ↓
paruošta GET užklausa

O visas užklausos paruošimo kodas atrodo taip:

Kodas
pagrindinis_url <- "https://jsonplaceholder.typicode.com"
uzklausa <- request(base_url = pagrindinis_url)
uzklausa <- req_url_path(req = uzklausa, path = "/users")
uzklausa <- req_method(req = uzklausa, method = "GET")

Užklausos formavimas naudojant |>

Ankstesnis kodas yra teisingas, tačiau nekompaktiškas. httr2 funkcijas patogu jungti bazinio R operatoriumi |>:

Kodas
uzklausa <- request(base_url = pagrindinis_url) |>
  req_url_path(path = "/users") |>
  req_method(method = "GET")

Kiekviena funkcija gauna ankstesniame žingsnyje sukurtą užklausą ir grąžina papildytą užklausos objektą.

PastabaAr būtina nurodyti GET?

httr2 pakete užklausos metodas pagal nutylėjimą yra GET, jeigu nėra pridedamas užklausos turinys. Todėl paprastai req_method(method = "GET") galima praleisti.

Mokantis API metodą kurį laiką nurodysime aiškiai, kad būtų matoma, kokio tipo HTTP užklausa siunčiama.

Užklausos parametrų perdavimas

JSONPlaceholder leidžia kai kuriuos išteklius filtruoti užklausos parametrais. Pavyzdžiui, galime prašyti tik pirmojo naudotojo įrašų:

https://jsonplaceholder.typicode.com/posts?userId=1

Su httr2 užklausų parametrų nereikia rankiniu būdu jungti į URL tekstą. Tam naudojama funkcija req_url_query():

Kodas
uzklausa <- request("https://jsonplaceholder.typicode.com") |>
  req_url_path("/posts") |>
  req_url_query(
    userId = 1
  )

Jeigu užklausos parametro reikšmė saugoma objekte, galime perduoti funkcijų argumentams užklausos parametrų reikšmes.

Kodas
vartotojo_id <- 1

uzklausa <- request("https://jsonplaceholder.typicode.com") |>
  req_url_path("/posts") |>
  req_url_query(
    userId = vartotojo_id
  )

Vieno ištekliaus gavimas

Vieno objekto identifikatorius dažnai yra ne užklausos parametras, o URL kelio dalis. Pavyzdžiui:

https://jsonplaceholder.typicode.com/users/3

Tokį adresą galima suformuoti taip:

Kodas
vartotojo_id <- 3

uzklausa <- request("https://jsonplaceholder.typicode.com") |>
  req_url_path("/users", vartotojo_id)

Kuris būdas naudojamas konkrečioje API (kelio dalis ar užklausos parametras) visada turi būti tikrinama API dokumentacijoje.

Užklausos atlikimas

Kai užklausa paruošta, ji išsiunčiama naudojant funkciją req_perform():

Kodas
atsakymas <- req_perform(req = uzklausa)

Suvykdžius komandą su šia funkcija, R iš tikrųjų susisiekia su serveriu ir perduoda sukonfiguruotą užklausą.

API autentifikavimas

Ne visos API yra viešai prieinamos. Kai kurioms užklausoms serveris reikalauja autentifikavimo. Klientas turi pateikti informaciją, pagal kurią serveris gali patikrinti, ar užklausą leidžiama vykdyti.

Autentifikavimo mechanizmai gali būti įvairūs. Dažniausi pavyzdžiai:

  • API raktas (angl. API key),
  • HTTP Basic autentifikavimas,
  • Bearer prieigos žetonas (angl. Bearer token),
  • OAuth 2.0,
  • kiti konkrečios API apibrėžti mechanizmai.

Šiame skyriuje pagrindinis dėmesys skiriamas API rakto perdavimui. Praktiniam autentifikavimo pavyzdžiui naudosime MockAPI išteklių:

https://api.mockapi.com/vartotojai

Kas yra API raktas?

API raktas yra serverio sugeneruota ar priskirta slapta reikšmė, kurią klientas pateikia kartu su užklausa. Pagal ją serveris gali atpažinti klientą, taikyti prieigos teises ar skaičiuoti naudojamų užklausų kiekį.

Pavyzdžiui, raktas gali atrodyti kaip ilga atsitiktinių simbolių seka:

f835e03be45644faa59cb49f093c6069

Svarbu žinoti ne kaip raktas turi atrodyti, o kur ir kokiu būdu API dokumentacija nurodo jį perduoti.

Pabandykime atlikti paprastą pavyzdį. Puslapis MockAPI leidžia susikurti testinę API su autentifikavimo raktu. Pirmiausia suformuojame įprastą GET užklausą:

Kodas
pagrindinis_url <- "https://api.mockapi.com"

uzklausa <- request(base_url = pagrindinis_url) |> 
  req_url_path(path = "/vartotojai") |> 
  req_method(method = "GET")

MockAPI dokumentacijoje autentifikavimas API raktu gali būti atliekamas dviem būdais.

1) API raktas x-api-key antraštėje

API raktą galima perduoti pasirinktinėje HTTP antraštėje:

x-api-key: MANO_API_RAKTAS

Su httr2 tam patogu naudoti req_headers_redacted():

Kodas
uzklausa_auth1 <- req_headers_redacted(
  .req = uzklausa,
  "x-api-key" = "f835e03be45644faa59cb49f093c6069"
)

req_headers_redacted() pažymi šios antraštės reikšmę kaip slaptą. Todėl spausdinant ar tikrinant užklausą pats raktas nėra rodomas.

2) Bearer autentifikavimas

Kitas būdas, tai tokeną perduoti standartinėje HTTP Authorization antraštėje:

Authorization: Bearer MANO_API_RAKTAS

Su httr2 tam skirta funkcija req_auth_bearer_token():

Kodas
uzklausa_auth2 <- req_auth_bearer_token(
  req = uzklausa,
  token = "f835e03be45644faa59cb49f093c6069"
)

Žodis Bearer pažodžiui išvertus reiškia turėtoją. Šios autentifikavimo schemos principas paprastas: prieiga suteikiama tam klientui, kuris pateikia galiojantį žetoną (tokeną). Bearer žetoną reikia saugoti panašiai kaip slaptažodį.

SvarbuRaktas vs tokenas

API raktas dažniausiai naudojamas identifikuoti programą ar sistemą, kuri kreipiasi į API, ir paprastai galioja ilgesnį laiką. API tokenas, kurio dažna forma yra Bearer tokenas, dažniau naudojamas konkrečiam naudotojui autentifikuoti ir gali turėti apibrėžtas prieigos teises bei ribotą galiojimo laiką.

x-api-key: <raktas>

arba:

Authorization: Bearer <raktas>

Kurį būdą naudoti, visada nurodo konkrečios API dokumentacija.

Kaip apsaugoti API raktą?

Tikro API rakto nerekomenduojama įrašyti tiesiai į .R, .Rmd ar .qmd, t. y. į šaltinio failą:

Kodas
# NEREKOMENDUOJAMA
api_raktas <- "f835e03be45644faa59cb49f093c6069"

Kodas gali būti persiųstas kitam žmogui, įkeltas į Git saugyklą ar publikuotas kartu su sugeneruotu HTML dokumentu.

Paprastas ir dažnai patogus būdas R projektuose – raktą laikyti aplinkos kintamajame .Renviron faile.

Naudotojo .Renviron failą patogu atidaryti su:

Kodas
install.packages("usethis")
usethis::edit_r_environ()

Faile įrašome:

MOCKAPI_KEY=f835e03be45644faa59cb49f093c6069

Po pakeitimo reikia iš naujo paleisti R sesiją. Tada raktą kode pasiimame iš sistemos aplinkos kintamųjų:

Kodas
api_raktas <- Sys.getenv("MOCKAPI_KEY")

# patikrinimas, ar raktas nėra tuščias 
if (identical(api_raktas, "")) {
  stop("Nerastas aplinkos kintamasis `MOCKAPI_KEY`.")
}

Ir naudojame užklausoje:

Kodas
uzklausa <- request(base_url = pagrindinis_url) |>
  req_url_path(path = "/vartotojai") |>
  req_method(method = "GET") |>
  req_auth_bearer_token(api_raktas)

Tokiu būdu tikras raktas nėra įrašytas į šaltinio kodą.

ĮspėjimasSys.setenv() nėra nuolatinio rakto saugykla

Techniškai galima vykdyti:

Kodas
Sys.setenv(MOCKAPI_KEY = "mano_tikras_api_raktas")

Tačiau jeigu tikrą raktą įrašome tiesiai į skriptą ar konsolės istoriją, jis vis tiek gali būti atskleistas. Ilgiau naudojamam raktui geriau naudoti .Renviron ar kitą saugią paslapčių saugyklą.

Užklausos patikrinimas prieš siunčiant

Prieš vykdant užklausą galima pasitikrinti, kas bus siunčiama serveriui:

Kodas
req_dry_run(req = uzklausa)

Naudojant Bearer autentifikavimą Authorization antraštės reikšmė bus paslėpta, pavyzdžiui:

GET /vartotojai HTTP/1.1
authorization: <REDACTED>

Atsakymo peržiūra

Grįžkime prie neautentifikuotos užklausos iš jsonplaceholder puslapio. Gautą atsakymą galima tiesiog išspausdinti, kur httr2 parodo svarbiausią informaciją.

Kodas
atsakymas
<httr2_response>
GET https://jsonplaceholder.typicode.com/users/3
Status: 200 OK
Content-Type: application/json
Body: In memory (520 bytes)

Atsakymo dalių gavimas

Atskiroms atsakymo dalims skirtos resp_*() funkcijos.

Galutinis URL:

Kodas
resp_url(atsakymas)

HTTP būsenos kodas:

Kodas
resp_status(atsakymas)

Būsenos tekstinis aprašymas:

Kodas
resp_status_desc(atsakymas)

Turinio tipas:

Kodas

Pavyzdžiui, sėkmingai JSON užklausai galime tikėtis:

200
application/json

JSON atsakymo apdorojimas

Atsakymas kaip tekstas

Neapdorotą atsakymo turinį galima gauti kaip tekstinę eilutę:

Kodas
ats_string <- resp_body_string(resp = atsakymas)

ats_string

Jeigu turinys yra JSON, rezultatą galima perduoti jsonlite:

Kodas
duomenys <- jsonlite::fromJSON(ats_string)

duomenys[1:4]

Šis būdas aiškiai parodo du atskirus veiksmus ir jiems realizuoti naudojamus paketus:

  1. serveris grąžina JSON tekstą (httr2 paketas),
  2. R programoje JSON tekstas išanalizuojamas ir paverčiamas objektu (jsonlite paketas)

Tiesioginis JSON apdorojimas su httr2

httr2 jau turi funkciją resp_body_json(), todėl JSON nebūtina pirmiausia išsaugoti kaip tekstą:

Kodas
atsakymas <- request("https://jsonplaceholder.typicode.com/users") |> 
  req_perform()

naudotojai <- resp_body_json(
  resp = atsakymas,
  simplifyVector = TRUE
)

naudotojai[1:4]
   id                     name         username                     email
1   1            Leanne Graham             Bret         Sincere@april.biz
2   2             Ervin Howell        Antonette         Shanna@melissa.tv
3   3         Clementine Bauch         Samantha        Nathan@yesenia.net
4   4         Patricia Lebsack         Karianne Julianne.OConner@kory.org
5   5         Chelsey Dietrich           Kamren  Lucio_Hettinger@annie.ca
6   6     Mrs. Dennis Schulist Leopoldo_Corkery   Karley_Dach@jasper.info
7   7          Kurtis Weissnat     Elwyn.Skiles    Telly.Hoeger@billy.biz
8   8 Nicholas Runolfsdottir V    Maxime_Nienow      Sherwood@rosamond.me
9   9          Glenna Reichert         Delphine   Chaim_McDermott@dana.io
10 10       Clementina DuBuque   Moriah.Stanton    Rey.Padberg@karina.biz

Argumentas simplifyVector = TRUE nurodo, kad, kai įmanoma, JSON struktūra turėtų būti supaprastinta į R vektorius ar duomenų lenteles. Pavyzdžiui:

Kodas
class(naudotojai)
str(naudotojai)

Jeigu pažvelgtume į funkcijos resp_body_json() vidų, pamatytume, kad viduje yra kviečiama fromJSON() funkcija iš jsonlite paketo. Kitaip tariant, resp_body_json() yra “wrapper” tipo funkcija.

Įdėtinės JSON struktūros

API atsakymas ne visada yra paprasta stačiakampė lentelė. JSON gali turėti įdėtinių (nested) objektų. Tarkime, gautas objektas naudotojai turi stulpelį address, kuriame saugoma kita struktūra:

Kodas
str(naudotojai)

Stulpeliuose address ir company saugomos vidinės duomenų lentelės. Jas galima išskleisti paprastu būdu, kadangi fromJSON() funkcija sudėtinius objektus grąžina kaip lentelės stulpelius:

Kodas
data.frame(
  naudotojai[, colnames(naudotojai) != "address"],
  "street"  = naudotojai$address$street,
  "suite"   = naudotojai$address$suite,
  "city"    = naudotojai$address$city,
  "zipcode" = naudotojai$address$zipcode,
  "geo"     = naudotojai$address$geo
)
   id                     name         username                     email
1   1            Leanne Graham             Bret         Sincere@april.biz
2   2             Ervin Howell        Antonette         Shanna@melissa.tv
3   3         Clementine Bauch         Samantha        Nathan@yesenia.net
4   4         Patricia Lebsack         Karianne Julianne.OConner@kory.org
5   5         Chelsey Dietrich           Kamren  Lucio_Hettinger@annie.ca
6   6     Mrs. Dennis Schulist Leopoldo_Corkery   Karley_Dach@jasper.info
7   7          Kurtis Weissnat     Elwyn.Skiles    Telly.Hoeger@billy.biz
8   8 Nicholas Runolfsdottir V    Maxime_Nienow      Sherwood@rosamond.me
9   9          Glenna Reichert         Delphine   Chaim_McDermott@dana.io
10 10       Clementina DuBuque   Moriah.Stanton    Rey.Padberg@karina.biz
                   phone       website       company.name
1  1-770-736-8031 x56442 hildegard.org    Romaguera-Crona
2    010-692-6593 x09125 anastasia.net       Deckow-Crist
3         1-463-123-4447   ramiro.info Romaguera-Jacobson
4      493-170-9623 x156      kale.biz      Robel-Corkery
5          (254)954-1289  demarco.info        Keebler LLC
6   1-477-935-8478 x6430       ola.org  Considine-Lockman
7           210.067.6132      elvis.io        Johns Group
8      586.493.6943 x140  jacynthe.com    Abernathy Group
9   (775)976-6794 x41206    conrad.com      Yost and Sons
10          024-648-3804   ambrose.net         Hoeger LLC
                        company.catchPhrase
1    Multi-layered client-server neural-net
2            Proactive didactic contingency
3         Face to face bifurcated interface
4  Multi-tiered zero tolerance productivity
5      User-centric fault-tolerant solution
6        Synchronised bottom-line interface
7        Configurable multimedia task-force
8             Implemented secondary concept
9     Switchable contextually-based project
10        Centralized empowering task-force
                             company.bs            street     suite
1           harness real-time e-markets       Kulas Light  Apt. 556
2      synergize scalable supply-chains     Victor Plains Suite 879
3       e-enable strategic applications Douglas Extension Suite 847
4  transition cutting-edge web services       Hoeger Mall  Apt. 692
5      revolutionize end-to-end systems      Skiles Walks Suite 351
6      e-enable innovative applications Norberto Crossing  Apt. 950
7         generate enterprise e-tailers         Rex Trail Suite 280
8         e-enable extensible e-tailers  Ellsworth Summit Suite 729
9      aggregate real-time technologies        Dayna Park Suite 449
10             target end-to-end models   Kattie Turnpike Suite 198
             city    zipcode  geo.lat   geo.lng
1     Gwenborough 92998-3874 -37.3159   81.1496
2     Wisokyburgh 90566-7771 -43.9509  -34.4618
3   McKenziehaven 59590-4157 -68.6102  -47.0653
4     South Elvis 53919-4257  29.4572 -164.2990
5      Roscoeview      33263 -31.8129   62.5342
6   South Christy 23505-1337 -71.4197   71.7478
7       Howemouth 58804-1099  24.8918   21.8984
8       Aliyaview      45169 -14.3990 -120.7677
9  Bartholomebury 76495-3109  24.6463 -168.8889
10    Lebsackbury 31428-2261 -38.2386   57.2232

Bet toks būdas nepatogus, jeigu sudėtinis objektas turi daugiau nei kelis vidinius objektus. Bendruoju atveju, paprasčiau tą patį galima atlikti naudojant unnest_wider() funkciją iš tidyr paketo:

Jeigu address yra sąrašo tipo stulpelis, jį galima išskleisti su tidyr::unnest_wider():

Kodas
naudotojai_tvarkingi <- naudotojai |>
  tidyr::unnest_wider(
    col = address,
    names_sep = "_"
  )

Jeigu išskleistoje struktūroje lieka dar vienas įdėtinis objektas, jį galima išskleisti pakartotinai:

Kodas
naudotojai_tvarkingi <- naudotojai_tvarkingi |>
  tidyr::unnest_wider(
    col = address_geo,
    names_sep = "_"
  )
PastabaJSON ne visada yra lentelė

JSON gali aprašyti hierarchinius duomenis, todėl nereikėtų tikėtis, kad kiekvienas API atsakymas automatiškai taps viena tvarkinga duomenų lentele. Pirmiausia patikrinkite objektą su str(), o tik tada pasirinkite pertvarkymo veiksmus.

Atsakymo ir metaduomenų išsaugojimas tiesiai į failą

Jeigu atsakymas labai didelis, jo nebūtina pirmiausia laikyti R darbinėje atmintyje. req_perform() argumentu path galima nurodyti failą, į kurį atsakymo turinys turi būti išsaugotas:

Kodas
atsakymas <- req_perform(
  req = uzklausa,
  path = "../jsonplaceholder-users.json"
)

Tokiu atveju serverio grąžintas turinys įrašomas tiesiai į JSON failą, kurį galbūt norėsime apdoroti vėliau.

Tai ypač naudinga, nes API duomenys laikui bėgant gali pakisti. Siekiant atkartojamumo naudinga išsaugoti ne tik pradinį serverio atsakymą, bet ir užklausos datą ir laiką, naudotus parametrus, API versiją ir HTTP būsenos kodą.

Klaidingos užklausos ir klaidų apdorojimas

Pavyzdžiui, kreipiantis į neegzistuojantį galinį tašką:

Kodas
request("https://jsonplaceholder.typicode.com") |>
  req_url_path("/neegzistuojantis-isteklius") |>
  req_perform()

httr2 pagal nutylėjimą 4xx ir 5xx atsakymus paverčia klaidomis R kalboje. Tai apsaugo nuo situacijos, kai klaidos atsakymas netyčia pradedamas apdoroti kaip tikras duomenų rinkinys.

Jeigu vykdome daug užklausų ir nenorime, kad viena klaida nutrauktų visą kodo procesą ar eigą, galime naudoti konstrukciją su tryCatch(). Tokiu atveju, klaidą paverčiame tik įspėjimu, nes įprastai klaidų nereikėtų ignoruoti:

Kodas
saugiai_gauti_json <- function(url) {
  tryCatch(
    {
      request(url) |>
        req_perform() |>
        resp_body_json(simplifyVector = TRUE)
    },
    error = function(e) {
      warning("API užklausa nepavyko: ", conditionMessage(e))
      NULL
    }
  )
}

saugiai_gauti_json("https://jsonplaceholder.typicode.com/neegzistuojantis-endpoint")

Puslapiavimas

Dideli duomenų rinkiniai dažnai negrąžinami vienu atsakymu. API gali padalyti rezultatus į mažesnes dalis arba puslapius (angl. pagination).

Skirtingos API puslapiavimą realizuoja skirtingai. Galima sutikti tokius parametrus kaip:

  • page – puslapio numeris,
  • limit arba per_page – įrašų skaičius puslapyje,
  • offset arba start – kiek įrašų praleisti,
  • cursor – serverio grąžinama žymė kitai rezultatų daliai.

JSONPlaceholder /posts ištekliuje yra 100 įrašų. Galime paprašyti ne visų įrašų iš karto, o, pavyzdžiui, 10 įrašų nuo 21-ojo:

https://jsonplaceholder.typicode.com/posts?_start=20&_limit=10

Su httr2:

Kodas
atsakymas <- request("https://jsonplaceholder.typicode.com") |>
  req_url_path("/posts") |>
  req_url_query(
    `_start` = 20,
    `_limit` = 10
  ) |>
  req_perform()

irasai_21_30 <- resp_body_json(
  atsakymas,
  simplifyVector = TRUE
)
SvarbuPuslapiavimo taisyklės nėra universalios

Parametrų _start ir _limit pavadinimai yra JSONPlaceholder pasirinkimas. Kita API gali naudoti page, offset, cursor arba kitą mechanizmą. Puslapiavimo logiką visada reikia tikrinti konkrečios API dokumentacijoje.

Taigi, bendra httr2 darbo eiga, komunikuojant su serveriu per API, atrodo taip:

request()
   ↓
req_*() funkcijos
   ↓
paruošta užklausa
   ↓
req_perform()
   ↓
serveris
   ↓
atsakymas
   ↓
resp_*() funkcijos

Praktinės užduotys

Šių užduočių tikslas – savarankiškai perskaityti API dokumentaciją, pasirinkti tinkamą galinį tašką, suformuoti užklausą ir paruošti analizavimui tinkamus duomenis.

1 užduotis. Lietuvos vietovių paieška

Naudokite Lietuvos hidrometeorologijos tarnybos API dokumentaciją: https://api.meteo.lt/.

ExerciseUžduotis
  1. Dokumentacijoje raskite galinį tašką, grąžinantį visas vietoves, kurioms teikiamos orų prognozės.
  2. Su httr2 gaukite šį sąrašą ir paverskite jį R duomenų objektu.
  3. Nustatykite, kiek vietovių yra gautame rinkinyje.
  4. Atrinkite tik tas vietoves, kurių administracinio vieneto pavadinime yra žodis „Vilniaus“.
  5. Iš gauto rezultato pasirinkite vieną vietovės kodą, kurį panaudosite kitoje užduotyje.

Sprendimuose turi būti aiškiai atskirta:

  • užklausos sukūrimo dalis,
  • užklausos atlikimas,
  • atsakymo patikrinimas,
  • JSON pavertimas R objektu,
  • duomenų atranka.

2 užduotis. Orų prognozės duomenų paruošimas analizei

Naudodami 1 užduotyje pasirinktą vietovę atlikite sekančias užduotis.

ExerciseUžduotis
  1. API dokumentacijoje raskite galinį tašką, kuris grąžina ilgalaikę tos vietovės orų prognozę.
  2. Suformuokite URL ne įrašydami visą adresą rankiniu būdu, o naudodami request() ir req_*() tipo funkcijas.
  3. Atlikite užklausą ir iš atsakymo išskirkite prognozės laiko žymas.
  4. Paruoškite duomenų lentelę, kurioje būtų bent:
    • prognozės laikas,
    • oro temperatūra,
    • vėjo greitis,
    • kritulių kiekis.
  5. Apskaičiuokite pasirinktai vietovei:
    • mažiausią ir didžiausią prognozuojamą temperatūrą,
    • laiką, kada prognozuojama didžiausia temperatūra,
    • bendrą prognozuojamų kritulių kiekį per pirmąsias 24 prognozės valandas.

3 užduotis. Kelių vietovių palyginimas

Parašykite savo funkciją, kuri kaip argumentą priima vietovės kodą ir per Meteo.lt API grąžina tos vietovės ilgalaikės prognozės duomenų lentelę.

Pasirinkite Vilnių, Kauną ir Klaipėdą. Naudodami šią funkciją atlikite šias užduotis.

ExerciseUžduotis
  1. Gaukite visų trijų miestų prognozes,
  2. prie kiekvieno rezultato pridėkite miesto pavadinimą,
  3. sujunkite rezultatus į vieną lentelę,
  4. palyginkite artimiausių 24 valandų vidutinę prognozuojamą temperatūrą.

4 užduotis. API dokumentacijos analizė

Atidarykite Lietuvos duomenų API dokumentaciją: https://lt-api.lt/dokumentacija.

Dar neatlikdami užklausos:

  1. Raskite bent tris skirtingas išteklių grupes.
  2. Pasirinkite vieną GET galinį tašką, kuris būtų įdomus duomenų analizei.
  3. Nustatykite:
    • bazinį API adresą,
    • galinio taško kelią,
    • kokie parametrai galimi arba privalomi,
    • kokia atsakymo struktūra,
    • ar reikia autentifikavimo,
    • kaip autentifikavimo informacija turi būti perduodama.
  4. Trumpai paaiškinkite, kokį R duomenų rinkinį iš šio galinio taško būtų galima sudaryti ir kokį analitinį klausimą su juo būtų galima nagrinėti.

5 užduotis. Autentifikuota užklausa į Lietuvos API

Susikurkite paskyrą https://lt-api.lt/ ir sugeneruokite API raktą. Dokumentacijoje nurodyta, kad raktas siunčiamas kaip Bearer tokenas:

Authorization: Bearer JUSU_API_RAKTAS
  1. Rakto nerašykite tiesiai į .R failą. Išsaugokite jį .Renviron faile, pavyzdžiui, kintamuoju LT_API_KEY.
  2. R kode raktą nuskaitykite su Sys.getenv().
  3. Iš Lietuvos API dokumentacijos pasirinkite vieną jums prieinamą GET galinį tašką ir atlikite autentifikuotą užklausą su req_auth_bearer_token().
  4. Prieš vykdydami užklausą panaudokite req_dry_run() ir įsitikinkite, kad autentifikavimo reikšmė konsolėje nėra atskleidžiama.
  5. Patikrinkite atsakymo būseną ir turinio tipą.
  6. JSON turinį paverskite R objektu ir iš jo paruoškite nedidelę analizei tinkamą lentelę.
  7. Pakeiskite vieną užklausos parametrą ir paaiškinkite, kaip dėl to pasikeitė grąžintas rezultatas.
PastabaJei Lietuvos API raktas nepasiekiamas

Jeigu užduoties atlikimo metu negalite susikurti rakto ar pasirinktas galinis taškas neprieinamas pagal jūsų planą, paruoškite pilną httr2 užklausos kodą su Sys.getenv("LT_API_KEY") ir paaiškinkite, kokios HTTP antraštės tikisi serveris bei kokio tipo atsakymo tikėtumėtės pagal dokumentaciją. Tikro rakto į darbą nekelkite.

Skyriaus santrauka

API yra standartizuota sąsaja, leidžianti klientui bendrauti su serveriu. Klientas siunčia HTTP užklausą į konkretų išteklių, o serveris grąžina HTTP atsakymą. Duomenims gauti dažniausiai naudojamas GET metodas.

Naudojant httr2, užklausa pirmiausia sukuriama funkcija request(), tada keičiama ir papildoma req_*() funkcijomis, o į serverį išsiunčiama su req_perform(). Serverio atsakymo informacijai ir turiniui apdoroti naudojamos resp_*() funkcijos.

API duomenys dažnai pateikiami JSON formatu. resp_body_json() leidžia JSON atsakymą tiesiogiai paversti R objektu, o resp_body_string() leidžia pamatyti pradinį tekstinį atsakymą.

Neviešoms API gali būti reikalingas autentifikavimas. API raktas gali būti perduodamas pasirinktinėje HTTP antraštėje, pavyzdžiui, x-api-key, arba kaip Bearer tokenas standartinėje Authorization antraštėje. Kurį būdą naudoti, visada nustato API dokumentacija.

API raktų nereikėtų rašyti tiesiai į analizės kodą. Paprastame R projekte juos patogu laikyti .Renviron faile ir nuskaityti su Sys.getenv().

Naudoti ir papildomi šaltiniai