OzbekPython / Bob 20

Internet va API bilan işlaş

HTTP asoslari, requests bilan sörov yuboriş, JSON, Markaziy bank valyuta kurslari ilovasi, web scraping asoslari va smtplib bilan e-poçta yuboriş.

Örta daraja17 daqiqa13 kod misoli20 / 24

Dasturlar internet orqali qanday “gaplaşadi”?

Telefoningizdagi ob-havo ilovasi, bank ilovasidagi valyuta kursi, taksi çaqiriş xizmati — bularning hammasi internetdagi boşqa kompyuterlar (serverlar) bilan maʼlumot almaşadi. Bu almaşinuv köpinça HTTP protokoli orqali böladi: sizning dasturingiz (mijoz) sörov yuboradi, server esa javob qaytaradi.

Har bir HTTP sörovning asosiy qismlari:

  • Manzil (URL) — qaysi serverga va qaysi resursga murojaat qilinyapti.
  • Metod — nima qilmoqçimiz: GET (maʼlumot oliş), POST (yangi maʼlumot yuboriş), PUT/PATCH (yangilaş), DELETE (öçiriş).
  • Sarlavhalar (headers) — qöşimça maʼlumot: qaysi formatni kutyapmiz, kim ekanmiz va hokazo.
  • Tana (body) — yuborilayotgan maʼlumotning özi (GETda odatda bölmaydi).

Server javobida esa holat kodi (status code), sarlavhalar va tana böladi. Dasturlar uçun möljallangan bunday manzillar töplami API (Application Programming Interface) deyiladi va ular köpinça maʼlumotni JSON formatida qaytaradi.

requests kutubxonasini örnatiş

Python'ning standart kutubxonasida urllib moduli bor, lekin uning bilan işlaş biroz noqulay. Deyarli barça Python dasturçilari buning örniga requests kutubxonasidan foydalanadi. U standart kutubxonaga kirmaydi, şuning uçun avval örnatamiz:

Terminal
pip install requests

Birinçi GET sörov

Özbekiston Respublikasi Markaziy banki valyuta kurslarini oçiq JSON körinişida eʼlon qiladi: cbu.uz/uz/arkhiv-kursov-valyut/json/. Bu manzilni brauzerda oçsangiz, valyutalar röyxatini körasiz. Endi xuddi şuni Python orqali qilamiz.

Python
import requests

# Eslatma: bu darslik yozilayotganda manzilga ulanib tekşirib bölmadi, şuning uçun
# quyida körsatilgan javob tuzilişi umumiy tavsif. Özingizda bir marta print() qilib köring.
URL = "https://cbu.uz/uz/arkhiv-kursov-valyut/json/"

javob = requests.get(URL, timeout=10)

print(javob.status_code)                   # 200 — hammasi joyida
kurslar = javob.json()                     # JSON matnni Python röyxatiga aylantiradi
print(type(kurslar), len(kurslar))
print(kurslar[0])

Natija taxminan şunday böladi (valyutalar soni va qiymatlar boşqaça bölişi mumkin, kurslar har kuni özgaradi):

Natija
200
<class 'list'> 75
{'id': 69, 'Code': '840', 'Ccy': 'USD', 'CcyNm_RU': 'Доллар США', 'CcyNm_UZ': 'AQŞ dollari', 'CcyNm_UZC': 'АҚШ доллари', 'CcyNm_EN': 'US Dollar', 'Nominal': '1', 'Rate': '12650.00', 'Diff': '-12.40', 'Date': '24.10.2026'}

Javobdagi har bir element — bitta valyuta haqidagi luğat. Bizga eng kerakli maydonlar:

MaydonMaʼnosi
Ccyvalyutaning uç harfli kodi (USD, EUR, RUB...)
CcyNm_UZvalyuta nomi özbekça (CcyNm_UZC — kirill yozuvida, CcyNm_EN, CcyNm_RU — boşqa tillarda)
Nominalkurs neçta birlik uçun berilgani (odatda "1")
RateNominal birlikning sömdagi qiymati (satr körinişida!)
Diffoldingi kurs bilan farq
Datekurs amal qiladigan sana, kun.oy.yil formatida

Holat kodlari

Server javobidagi uç xonali son sörov qanday yakunlanganini bildiradi. Birinçi raqamga qarab guruhni darhol tuşuniş mumkin.

KodMaʼnosiQaçon uçraydi
200OKhammasi joyida
201Createdyangi maʼlumot yaratildi (POSTdan keyin)
301 / 302yönaltirişsahifa boşqa manzilga köçgan
400Bad Requestsörov notöğri tuzilgan
401 / 403ruxsat yöqkalit yoki parol kerak / taqiqlangan
404Not Foundbunday manzil yöq
429Too Many Requestsjuda köp sörov yubordingiz
500 / 503server xatosimuammo serverning özida

Qisqasi: 2xx — muvaffaqiyat, 3xx — yönaltiriş, 4xx — sizning xatoingiz, 5xx — serverning xatosi. requestsda javob.ok xossasi kod 400 dan kiçik bölsa True qaytaradi, javob.raise_for_status() esa xato kodida istisno kötaradi.

json moduli: matn ↔ Python obyektlari

JSON — bu oddiy matn, lekin uning tuzilişi Python luğat va röyxatlariga juda öxşaydi. Standart kutubxonadagi json moduli ikki yönalişda işlaydi: json.loads() matnni Python obyektiga, json.dumps() esa Python obyektini matnga aylantiradi. Bu misol tarmoqsiz işlaydi:

Python
import json

matn = '{"Ccy": "USD", "Rate": "12650.00", "Nominal": "1", "faol": true, "izoh": null}'

valyuta = json.loads(matn)
print(type(valyuta).__name__)
print(valyuta["Ccy"], float(valyuta["Rate"]) * 2)
print(valyuta["faol"], valyuta["izoh"])

hisobot = {"şahar": "Toşkent", "kurslar": [12650.0, 13700.0], "yangilandi": True}
print(json.dumps(hisobot, ensure_ascii=False))
print(json.dumps(hisobot, ensure_ascii=False, indent=2))

Natija:

Natija
dict
USD 25300.0
True None
{"şahar": "Toşkent", "kurslar": [12650.0, 13700.0], "yangilandi": true}
{
  "şahar": "Toşkent",
  "kurslar": [
    12650.0,
    13700.0
  ],
  "yangilandi": true
}

Eʼtibor bering: JSONdagi true, false, null Python'da True, False, None ga aylandi. ensure_ascii=False özbekça harflarni \u02bb kabi kodlarga aylantirmasdan, öz holiça yoziş uçun kerak, indent=2 esa natijani öqişga qulay qiladi.

Faylga yoziş va fayldan öqiş uçun s harfisiz json.dump() va json.load() funksiyalari bor:

Python
import json
from pathlib import Path

sozlamalar = {"asosiy_valyuta": "USD", "valyutalar": ["USD", "EUR", "RUB"]}

fayl = Path("sozlamalar.json")
with fayl.open("w", encoding="utf-8") as f:
    json.dump(sozlamalar, f, ensure_ascii=False, indent=2)

with fayl.open(encoding="utf-8") as f:
    oqilgan = json.load(f)

print(oqilgan == sozlamalar, oqilgan["valyutalar"])

Natija:

Natija
True ['USD', 'EUR', 'RUB']

Valyuta maʼlumotlarini qayta işlaş

Tarmoq kodini maʼlumotni qayta işlaş kodidan ajratiş yaxşi odat. Şunda hisob-kitob qismini internetsiz ham sinab köriş mumkin. Quyidagi misolda API javobiga öxşaş kiçik namuna röyxatdan foydalanamiz (kurslar öylab topilgan).

Python
namuna = [
    {"Ccy": "USD", "CcyNm_UZ": "AQŞ dollari", "Nominal": "1", "Rate": "12650.00", "Date": "24.10.2026"},
    {"Ccy": "EUR", "CcyNm_UZ": "Yevro", "Nominal": "1", "Rate": "13700.50", "Date": "24.10.2026"},
    {"Ccy": "RUB", "CcyNm_UZ": "Rossiya rubli", "Nominal": "1", "Rate": "155.40", "Date": "24.10.2026"},
    {"Ccy": "TRY", "CcyNm_UZ": "Turk lirasi", "Nominal": "1", "Rate": "370.25", "Date": "24.10.2026"},
]

def kurslar_lugati(royxat: list[dict]) -> dict[str, float]:
    """{'USD': 12650.0, ...} körinişidagi luğat: 1 birlikning sömdagi narxi."""
    return {v["Ccy"]: float(v["Rate"]) / int(v["Nominal"]) for v in royxat}

def somga(miqdor: float, valyuta: str, kurslar: dict[str, float]) -> float:
    return miqdor * kurslar[valyuta]

def valyutaga(som: float, valyuta: str, kurslar: dict[str, float]) -> float:
    return som / kurslar[valyuta]

kurslar = kurslar_lugati(namuna)
print(kurslar)
print(f"250 USD = {somga(250, 'USD', kurslar):,.0f} söm")
print(f"1 000 000 söm = {valyutaga(1_000_000, 'TRY', kurslar):,.2f} TRY")

Natija:

Natija
{'USD': 12650.0, 'EUR': 13700.5, 'RUB': 155.4, 'TRY': 370.25}
250 USD = 3,162,500 söm
1 000 000 söm = 2,700.88 TRY

Töliq ilova: valyuta kalkulyatori

Endi hammasini birlaştiramiz: kurslarni internetdan olamiz, xatolarni uşlaymiz va foydalanuvçiga natija körsatamiz. Har bir tarmoq sörovida timeout beriş şart: busiz server javob bermasa, dasturingiz çeksiz kutib qolişi mumkin.

Python
import requests

URL = "https://cbu.uz/uz/arkhiv-kursov-valyut/json/"

def kurslarni_ol() -> tuple[dict[str, float], str]:
    """Markaziy bankdan kurslarni oladi. (kurslar, sana) qaytaradi."""
    javob = requests.get(URL, timeout=10)
    javob.raise_for_status()          # 4xx/5xx bölsa, HTTPError kötaradi
    royxat = javob.json()
    kurslar = {v["Ccy"]: float(v["Rate"]) / int(v["Nominal"]) for v in royxat}
    sana = royxat[0]["Date"] if royxat else "nomaʼlum"
    return kurslar, sana

def main() -> None:
    try:
        kurslar, sana = kurslarni_ol()
    except requests.exceptions.Timeout:
        print("Server 10 soniya içida javob bermadi. Keyinroq urinib köring.")
        return
    except requests.exceptions.ConnectionError:
        print("Internetga ulanib bölmadi. Aloqani tekşiring.")
        return
    except requests.exceptions.HTTPError as xato:
        print("Server xato qaytardi:", xato)
        return
    except (ValueError, KeyError):
        print("Javob formati kutilganidek emas.")
        return

    print(f"Markaziy bank kurslari ({sana})")
    for kod in ("USD", "EUR", "RUB", "TRY", "KZT"):
        if kod in kurslar:
            print(f"  1 {kod} = {kurslar[kod]:>10,.2f} söm")

    valyuta = input("Valyuta kodi (masalan, USD): ").strip().upper()
    if valyuta not in kurslar:
        print("Bunday valyuta topilmadi.")
        return
    miqdor = float(input(f"Neça {valyuta}? "))
    print(f"{miqdor:,.2f} {valyuta} = {miqdor * kurslar[valyuta]:,.0f} söm")

if __name__ == "__main__":
    main()

Bu yerda istisnolar tartibi muhim: requests.exceptions içidagi xatolar öziga xos, ValueError esa javob.json() notöğri matn olganda yoki float() aylantira olmaganda çiqadi. Har bir holatda foydalanuvçiga tuşunarli xabar beramiz.

Web scraping asoslari

Har bir sayt ham API taqdim etmaydi. Baʼzan kerakli maʼlumot faqat oddiy HTML sahifada böladi. Sahifadan maʼlumotni dastur yordamida ajratib oliş web scraping deyiladi. Bu iş uçun eng maşhur kutubxona — BeautifulSoup (bs4 paketi).

Terminal
pip install requests beautifulsoup4

Avval qoidalar haqida. Scraping qilişdan oldin:

  • Saytda rasmiy API bormi, tekşiring — u doim afzal.
  • Saytning foydalaniş şartlarini (Terms of Service) öqing: baʼzi saytlar avtomatik yiğişni taqiqlaydi.
  • robots.txt faylini hurmat qiling: unda qaysi sahifalarni robotlar oçmasligi kerakligi yozilgan.
  • Serverni ortiqça yuklamang: sörovlar orasida pauza qiling, bir xil sahifani qayta-qayta yuklamang.
  • Şaxsiy maʼlumotlarni yiğmang.

robots.txt qoidalarini standart kutubxonadagi urllib.robotparser bilan tekşiriş mumkin. Odatda set_url() va read() bilan faylni saytdan öqiysiz; quyida esa tarmoqsiz işlaşi uçun qoidalarni töğridan-töğri beramiz:

Python
from urllib.robotparser import RobotFileParser

qoidalar = """
User-agent: *
Disallow: /admin/
Disallow: /savat/
Crawl-delay: 2
""".splitlines()

rp = RobotFileParser()
rp.parse(qoidalar)

for yol in ["/kitoblar/", "/admin/panel", "/savat/"]:
    ruxsat = rp.can_fetch("MeningBotim", "https://kitob.example" + yol)
    print(f"{yol:15} -> {'mumkin' if ruxsat else 'taqiqlangan'}")
print("Pauza:", rp.crawl_delay("MeningBotim"), "soniya")

Natija:

Natija
/kitoblar/      -> mumkin
/admin/panel    -> taqiqlangan
/savat/         -> taqiqlangan
Pauza: 2 soniya

Endi BeautifulSoup bilan HTMLdan maʼlumot ajratamiz. Saytga sörov yubormaslik uçun kiçik kitob dökoni sahifasini satr körinişida beramiz. Haqiqiy loyihada bu satr örniga requests.get(url, timeout=10).text böladi.

Python
from bs4 import BeautifulSoup

html = """
<html><body>
  <h1>Kitob dökoni</h1>
  <div class="kitob">
    <h2>Ötkan kunlar</h2>
    <span class="narx">65000</span>
  </div>
  <div class="kitob">
    <h2>Mehrobdan çayon</h2>
    <span class="narx">58000</span>
  </div>
  <a href="/kitoblar?sahifa=2">Keyingi sahifa</a>
</body></html>
"""

sahifa = BeautifulSoup(html, "html.parser")

print(sahifa.h1.get_text())
for kitob in sahifa.find_all("div", class_="kitob"):
    nom = kitob.find("h2").get_text(strip=True)
    narx = int(kitob.select_one(".narx").get_text())
    print(f"{nom}: {narx:,} söm")

havola = sahifa.find("a")
print(havola["href"])
Natija
Kitob dökoni
Ötkan kunlar: 65,000 söm
Mehrobdan çayon: 58,000 söm
/kitoblar?sahifa=2

Asosiy metodlar: find() birinçi mos elementni, find_all() hammasini qaytaradi; select() va select_one() esa CSS selektorlari bilan işlaydi (.narx — narx klassli element). get_text() teg içidagi matnni, element["href"] esa atribut qiymatini beradi.

E-poçta yuboriş: smtplib

Python standart kutubxonasi bilan xat yuboriş ham mumkin. Xatning özini email.message.EmailMessage yordamida tuzamiz, uni yuborişni esa smtplib bajaradi. Xatni tuziş tarmoqsiz işlaydi:

Python
from email.message import EmailMessage

xat = EmailMessage()
xat["Subject"] = "Bugungi valyuta kurslari"
xat["From"] = "bot@example.com"
xat["To"] = "dilnoza@example.com"
xat.set_content("1 USD = 12 650 söm\n1 EUR = 13 700 söm")

print(xat["Subject"])
print(xat["To"])
print(xat.get_content())

Natija:

Natija
Bugungi valyuta kurslari
dilnoza@example.com
1 USD = 12 650 söm
1 EUR = 13 700 söm

Yuboriş uçun poçta serveri (SMTP) manzili, port va parol kerak. Parolni heç qaçon kod içiga yozmang: kod GitHubga yuklanganda yoki döstingizga yuborilganda parol ham oşkor böladi. Uni muhit özgaruvçisida saqlang. Gmail kabi xizmatlarda oddiy parol emas, ikki bosqiçli tasdiqlaş yoqilgandan keyin yaratiladigan maxsus ilova paroli (App Password) işlatiladi.

Terminal
export EMAIL_MANZIL="sizning.manzil@gmail.com"
export EMAIL_PAROL="ilova-paroli-shu-yerga"
Python
import os
import smtplib
from email.message import EmailMessage

manzil = os.environ["EMAIL_MANZIL"]
parol = os.environ["EMAIL_PAROL"]       # kod içida parol yöq!

xat = EmailMessage()
xat["Subject"] = "Bugungi valyuta kurslari"
xat["From"] = manzil
xat["To"] = "dilnoza@example.com"
xat.set_content("1 USD = 12 650 söm\n1 EUR = 13 700 söm")

try:
    with smtplib.SMTP_SSL("smtp.gmail.com", 465, timeout=20) as server:
        server.login(manzil, parol)
        server.send_message(xat)
    print("Xat yuborildi.")
except smtplib.SMTPAuthenticationError:
    print("Login yoki ilova paroli notöğri.")
except (smtplib.SMTPException, OSError) as xato:
    print("Xat yuborilmadi:", xato)

SMTP_SSL 465-port orqali boşidanoq şifrlangan ulaniş oçadi. Baʼzi serverlar 587-portda smtplib.SMTP(...) va undan keyin server.starttls() çaqiruvini talab qiladi — poçta xizmatingiz sozlamalariga qarang. with bloki tugaganda ulaniş avtomatik yopiladi.

Xulosa

  • HTTP — mijoz sörov yuboradi, server holat kodi, sarlavhalar va tana bilan javob beradi; 2xx muvaffaqiyat, 4xx mijoz xatosi, 5xx server xatosi.
  • requests.get(url, timeout=...) bilan maʼlumot olinadi; raise_for_status() xato kodlarini istisnoga aylantiradi.
  • json.loads()/json.dumps() matn bilan, json.load()/json.dump() fayl bilan işlaydi; özbekça matn uçun ensure_ascii=False.
  • Markaziy bank APIsida Ccy, Rate, Nominal, Date maydonlari bor; Rate satr, uni float() ga aylantiring.
  • Scraping qilişdan oldin API, foydalaniş şartlari va robots.txtni tekşiring; BeautifulSoup bilan find, find_all, select işlatiladi.
  • Xat EmailMessage bilan tuziladi va smtplib bilan yuboriladi; parol faqat muhit özgaruvçisida saqlanadi.

Maşqlar

  1. json modulidan foydalanib, talabalar baholari luğatini ({"Aziza": [5, 4, 5], ...}) har bir talabaning örtaça bahosi bilan birga çiroyli JSON matnga aylantiring.
Yeçim
Python
import json

baholar = {"Aziza": [5, 4, 5], "Bekzod": [3, 4, 4], "Gulnora": [5, 5, 5]}
natija = {ism: {"baholar": b, "orta": round(sum(b) / len(b), 2)} for ism, b in baholar.items()}
print(json.dumps(natija, ensure_ascii=False, indent=2))
  1. Valyuta kurslari luğatini ({"USD": 12650.0, "EUR": 13700.5, ...}) olib, eng qimmat va eng arzon valyutani hamda ikki valyuta orasidagi kross-kursni (masalan, 1 EUR neça USD) hisoblaydigan funksiyalar yozing.
Yeçim
Python
kurslar = {"USD": 12650.0, "EUR": 13700.5, "RUB": 155.4, "TRY": 370.25}

def kross_kurs(dan: str, ga: str, kurslar: dict[str, float]) -> float:
    return kurslar[dan] / kurslar[ga]

eng_qimmat = max(kurslar, key=kurslar.get)
eng_arzon = min(kurslar, key=kurslar.get)
print("Eng qimmat:", eng_qimmat, "| Eng arzon:", eng_arzon)
print(f"1 EUR = {kross_kurs('EUR', 'USD', kurslar):.4f} USD")
print(f"1 USD = {kross_kurs('USD', 'TRY', kurslar):.2f} TRY")
  1. holat_matni(kod) funksiyasini match yordamida yozing: u HTTP holat kodiga qarab “Muvaffaqiyat”, “Yönaltiriş”, “Mijoz xatosi”, “Server xatosi” yoki “Nomaʼlum” qaytarsin.
Yeçim
Python
def holat_matni(kod: int) -> str:
    match kod // 100:
        case 2:
            return "Muvaffaqiyat"
        case 3:
            return "Yönaltiriş"
        case 4:
            return "Mijoz xatosi"
        case 5:
            return "Server xatosi"
        case _:
            return "Nomaʼlum"

for kod in (200, 301, 404, 503, 99):
    print(kod, holat_matni(kod))
  1. Valyuta kalkulyatorini kengaytiring: kurslarni olganda ularni kurslar_kesh.json fayliga saqlang, internet bölmasa esa oxirgi saqlangan kurslardan foydalaning va bu haqda ogohlantiring.
Yeçim
Python
import json
from pathlib import Path

import requests

URL = "https://cbu.uz/uz/arkhiv-kursov-valyut/json/"
KESH = Path("kurslar_keş.json")

def kurslarni_ol() -> dict[str, float]:
    try:
        javob = requests.get(URL, timeout=10)
        javob.raise_for_status()
        kurslar = {v["Ccy"]: float(v["Rate"]) / int(v["Nominal"]) for v in javob.json()}
        KESH.write_text(json.dumps(kurslar, indent=2), encoding="utf-8")
        return kurslar
    except (requests.exceptions.RequestException, ValueError, KeyError):
        if KESH.exists():
            print("Ogohlantiriş: internet yöq, saqlangan kurslar işlatilmoqda.")
            return json.loads(KESH.read_text(encoding="utf-8"))
        raise

kurslar = kurslarni_ol()
print(f"1 USD = {kurslar['USD']:,.2f} söm")