Shanraq.org Shanraq.org
Python-нан HTTP: статус, байттар және екі түрлі «болмады»
IT

Python: деректен өз есебіңізге дейін 18-сабақ (барлығы 56)

Python-нан HTTP: статус, байттар және екі түрлі «болмады»

Python курсының он сегізінші сабағы. Ұлттық банкке сұраныс: 200, `text/xml`, 11 407 байт — әрі бұл жол емес, `bytes`. Плюс қателерді өңдеуді бұзатын айырма: `HTTPError` — сервер «жоқ» деп жауап берді, `URLError` — жауап мүлде болмады.

Не үшін керек

Бірінші сабақ желіге барған еді — әрі адал ескерткен: «ештеңе түсінбей іске қосыңыз». Бүгін түсінеміз.

Санауға тұрарлық дерек әрдайым дерлік басқа біреуде жатады: банкте, статистикада, ауа райында. Сіздің бағдарламаңыз бен солардың арасында HTTP тұр: мекенжай, сұраныс, жауап.

Оны Ұлттық банктің бағамдарында талдаймыз, өйткені бұл — кілтсіз және тіркеусіз сан беретіндердің ішіндегі ең жақыны.

Бірден тұтас

surau.py файлы. Іске қосу: ортадан python surau.py.

Міндеттісі — бірінші блок: сұраныс, статус, байттар, кодтау. Екіншісі мекенжайдағы күн қайдан шығатынын, үшіншісі сәтсіздіктің екі түрін көрсетеді.

Сабақтағы күн әдейі бекітілген: кешегі бағам әр түні өзгереді, ал сабақ сізде шығатын нәрсені басып шығаруға міндетті. Дәл кешегіні қалай алу керегі сабақтың өзінде айтылған.

"""18-сабақ: HTTP — бағдарлама бөтен серверден қалай сұрайды.

Бірінші сабақ желіге ештеңе түсіндірмей барған еді. Бүгін сол баруды талдаймыз:
мекенжай, тақырыптар, статус, байттар — әрі сервер «жоқ» дегенде не істеу керек.
"""

import urllib.error
import urllib.request
from datetime import date, timedelta

# Ұлттық банк бір күндегі ресми бағамдарды береді. Күн мекенжайдың өзінде,
# күн.ай.жыл түрінде тұр. Сабақта күн бекітілген: сонда шығу бәрінде бірдей.
TEMPLATE = "https://nationalbank.kz/rss/get_rates.cfm?fdate={}"
URL = TEMPLATE.format("15.01.2026")

print("== сұраныс пен жауап")
request = urllib.request.Request(URL, headers={"User-Agent": "shanraq-course/1.0"})
with urllib.request.urlopen(request, timeout=30) as answer:
    status = answer.status
    kind = answer.headers.get_content_type()
    charset = answer.headers.get_content_charset()
    body = answer.read()

print("статус:", status)
print("мазмұн түрі:", kind, "| кодтауы:", charset)
print("алынды:", len(body), "байт, дерек типі:", type(body).__name__)

text = body.decode(charset or "utf-8")
print("бірінші жол:", text.splitlines()[0])
print("жауаптағы күн:", text.split("<date>")[1].split("</date>")[0])

print()
print("== «кеше» — сөз емес, күн")
day = date(2026, 1, 15)
print("осы күнге мекенжай:", TEMPLATE.format(day.strftime("%d.%m.%Y")))
print("кешені былай санайды: date.today() -", repr(timedelta(days=1)))

print()
print("== сервер «жоқ» дегенде")
try:
    urllib.request.urlopen("https://nationalbank.kz/net-takogo-adresa", timeout=30)
except urllib.error.HTTPError as error:
    print("HTTPError — сервер жауап берді:", error.code, error.reason)

try:
    urllib.request.urlopen("https://net-takogo-servera.shanraq.invalid", timeout=10)
except urllib.error.URLError as error:
    print("URLError — жауап болған жоқ:", type(error.reason).__name__)

Шығатыны:

== сұраныс пен жауап
статус: 200
мазмұн түрі: text/xml | кодтауы: utf-8
алынды: 11407 байт, дерек типі: bytes
бірінші жол: <?xml version="1.0" encoding="utf-8"?>
жауаптағы күн: 15.01.2026

== «кеше» — сөз емес, күн
осы күнге мекенжай: https://nationalbank.kz/rss/get_rates.cfm?fdate=15.01.2026
кешені былай санайды: date.today() - datetime.timedelta(days=1)

== сервер «жоқ» дегенде
HTTPError — сервер жауап берді: 404 Not Found
URLError — жауап болған жоқ: gaierror

Талдау

Жауап мәтінмен емес, байтпен келеді

алынды: 11407 байт, дерек типі: bytes

answer.read() bytes береді — желімен қалай келсе, сол шикі байттар. Оларды мәтінге decode айналдырады, ал ол үшін кодтауды ойлап таппайды: серверді өзі Content-Type тақырыбында атайды.

charset = answer.headers.get_content_charset()
text = body.decode(charset or "utf-8")

Мұндағы or "utf-8" — жалқаулық емес, шешім: әр сервер кодтауды атай бермейді, әрі ондай жағдайда не істейтінін алдын ала білу керек. Бұл — файлдар туралы он бірінші сабақтағы сол әңгіме, тек байттар туралы келісім енді солармен бірге келеді.

Елестетіп көріңіз. Сәлемдеме. Таспамен зат емес, қорап келеді: затты алу үшін қорапты жапсырмада жазылған кілтпен ашу керек.

Алдымен статусты оқиды

статус: 200

200 дегені — «міне жауап». Сондай-ақ 301 мен 302 (көшкен, urllib артынан өзі барады), 404 (мекенжай жоқ), 403 (болмайды), 429 (тым жиі сұрайсыз), 500 (оларда сынды) кездеседі.

Ереже қарапайым: алдымен статус, содан кейін дене. Қатенің де денесі болады — «табылмады» беті де бет, — әрі оны дерек деп талдау дүниедегі ең таңғаларлық қателерді береді.

Тақырыптар: кім сұрап тұр және қанша күтеді

request = urllib.request.Request(URL, headers={"User-Agent": "shanraq-course/1.0"})
with urllib.request.urlopen(request, timeout=30) as answer:

User-Agent — бағдарламаның белгісі. Әдепкіде urllib өзін өзі деп жазады, әрі кейбір серверлер ондай сұранысты қабылдамайды; өзін адал атау — сыпайырақ та, сенімдірек те.

timeout=30 міндетті. Онсыз бағдарлама жауапты мәңгі күтуі мүмкін: сервер жауап беруге де, байланысты жабуға да міндетті емес. Таймаутсыз бес минуттық үнсіздік «бағдарлама қатып қалды» болып көрінеді.

with байланысты файлды жапқандай жабады: ішінде қате болса да.

Екі түрлі «болмады»

HTTPError — сервер жауап берді: 404 Not Found
URLError — жауап болған жоқ: gaierror

Бұл — екі бөлек бәле, әрі емі де бөлек. HTTPError сервердің тірі екенін әрі жауап бергенін білдіреді — тек «жоқ» деп жауап берген: мекенжай олай емес, қатынау жабық, тым жиі сұрап тұрсыз. Мұнда error.code-қа қарайды.

URLError жауаптың мүлде болмағанын білдіреді: желі жоқ, ат шешілмеді, байланыс үзілді. Ішінде себебі жатыр (error.reason), әрі оның типінен не болғаны көрінеді.

Маңызды нәзіктік: HTTPErrorURLError-дың жеке жағдайы. Демек, оныншы сабақтағыдай, тармақтардың реті шешеді: жекесі жалпысынан жоғары, әйтпесе except URLError бәрін алып қояды да, жауап кодын көрмейсіз. Үшінші жаттығу да соған негізделген.

requests — сол нәрсе, бірақ қораптан шықпайды

Бөтен кодта сіз urllib-ті емес, requests-ті көресіз дерлік:

# сол нәрсе requests-пен — оны бөлек орнатады: pip install requests
import requests

answer = requests.get(URL, timeout=30, headers={"User-Agent": "shanraq-course/1.0"})
print(answer.status_code, answer.headers["content-type"])
print(answer.text[:38])          # decode сіздің орныңызға жасалған

Ол қысқарақ әрі ыңғайлырақ: кодтауды өзі шешеді, сессияны өзі ұстайды. Бірақ ол Python-мен бірге келмейді — оны орнату керек, демек бағдарламада тәуелділік пайда болады. Курс бәрі барлық жерде іске қосылсын деп стандартты кітапхананы ұстанады; өз жұмысыңызда requests-ті алыңыз — тек енді оның сіздің орныңызға не істейтінін білесіз.

Сабақ картасы

Сабақ картасы: мекенжай, статус және байттар

Өз сөзіңізбен айтыңыз

Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптары — сабақтың соңында.

  1. answer.read() неге байт береді, әрі decode үшін кодтауды қайдан алады?
  2. HTTPError URLError-дан немен өзгеше, әрі тармақтардың реті неге маңызды?
  3. Желі жылдам болса да, сұранысқа timeout не үшін керек?

Жаттығу

Тапсырмаға дейінгі үш қысқа қадам: болжау, толықтыру, жөндеу. Жауаптары — сабақтың соңында, бірақ алдымен өзіңіз жауап беріңіз.

1. Болжаңыз. Бұл бағдарлама не басып шығарады?

body = b'<?xml version="1.0"?>'
print(type(body).__name__, len(body))
text = body.decode("utf-8")
print(type(text).__name__, text[:5])

2. Бос орынды толтырыңыз. ... орнына күнді 15.01.2026 түрінде беретін нәрсені қойыңыз.

from datetime import date

TEMPLATE = "https://nationalbank.kz/rss/get_rates.cfm?fdate={}"
day = date(2026, 1, 15)
print(TEMPLATE.format(...))

3. Жөндеңіз. Сервер 404 деп жауап берді, ал бағдарлама желі жоқ дейді. Себебін табыңыз — ол тармақтардың ретінде.

import urllib.error

try:
    raise urllib.error.HTTPError("url", 404, "Not Found", None, None)
except urllib.error.URLError as error:
    print("желі қолжетімсіз:", error.reason)
except urllib.error.HTTPError as error:
    print("жауап коды:", error.code)

Тапсырма

Міндетті. Ұлттық банктен бекітілген күнге — 15.01.2026 — доллар бағамын алыңыз:

TEMPLATE = "https://nationalbank.kz/rss/get_rates.cfm?fdate={}"

Сұранысқа өз User-Agent-іңізді қойып, timeout беріңіз. Статусты және алынған байт санын оқып, денені сервер атаған кодтаумен ашыңыз да, жауаптағы күнді басып шығарыңыз. Содан кейін доллар бағамын және оның күндегі өзгерісін алыңыз — әзірге жол әдістерімен, белгілеуді талдау келесі сабақта болады. Сервердің қатесі бар жауабын бөлек тармақпен өңдеңіз.

Күтілетін шығу:

статус: 200 | алынған байт: 11407
жауаптағы күн: 15.01.2026
доллар: 510.43 теңге | күндегі өзгеріс: +0.68

Дайын болғаны: шығу жол-жолмен сәйкес келеді; сұраныста timeout та, өз User-Agent-і де бар; кодтау қолмен жазылмай, жауаптың тақырыбынан алынған; HTTPError URLError-дан бұрын ұсталған.

Өз деректеріңізде. Кешегі күнді алыңыз — date.today() - timedelta(days=1) — да, соған бағам сұраңыз. Сан әр оқырманда әрі әр күні басқа болады: өлшенгенді басып шығаратын сабақ пен нақты уақытта тұратын бағдарламаның айырмасы да сол.

Қалауыңызша.

  • Әлі болмаған күнді сұрап, сандардың орнына не келетінін қараңыз.
  • User-Agent-ті алып тастап, жауапты салыстырыңыз.
  • timeout=0.001 қойып, urlopen немен жауап беретінін оқыңыз.

Жобада бұл қайда тұрады

Шолуда екінші дереккөз пайда болады. Инфляция Дүниежүзілік банктен жылына бір рет келетін; валюта бағамын Ұлттық банк әр жұмыс күні береді, әрі шолу алғаш рет «бүгіні» бар дерек алады.

Қарыздар. Бағамды белгілеуден жол әдістерімен суырып алдық — олай істеуге болмайды, әрі келесі сабақ соны жөндейді. Әрі әр іске қосуда желіге барамыз: дискідегі кэш он бірінші сабақтан бері бар, ал оны күнмен байланыстыру әлі алда.

Жауаптар

Жауаптарды көрсету

Сұрақтарға

  1. Өйткені желімен мәтін емес, байт жүреді: HTTP-дың ішінде не бар екенін білуге міндеті жоқ. Кодтауды сервердің өзі атайды — Content-Type тақырыбында; оны содан алады, ал сервер үндемей қалған жағдайға әдепкі туралы келіседі.
  2. HTTPError — сервер жауап берді, бірақ бас тартумен: оның коды бар (404, 403, 500). URLError — жауап мүлде болмады: желі жоқ, ат шешілмеді. Біріншісі екіншісінің жеке жағдайы, сондықтан оны бұрын ұстау керек, әйтпесе жалпы тармақ бәрін алып қояды.
  3. Өйткені сервер жауап беруге міндетті емес. timeout-сыз бағдарлама ол үндемеген уақыттың бәрін күтеді, әрі сырттан бұл «желі бүгін нашар» емес, «қатып қалды» болып көрінеді.

Жаттығуға

  1. bytes 21 және str <?xml. Желімен байт келеді; decode олардан жол жасайды, әрі таныс әріптер тек жолда болады.
bytes 21
str <?xml
  1. day.strftime("%d.%m.%Y"). Ұлттық банк күнді күн.ай.жыл түрінде күтеді, әрі бұл — он төртінші сабақта екі түрлі оқылған сол пішім: мұнда ол анық аталады.
from datetime import date

TEMPLATE = "https://nationalbank.kz/rss/get_rates.cfm?fdate={}"
day = date(2026, 1, 15)
print(TEMPLATE.format(day.strftime("%d.%m.%Y")))
https://nationalbank.kz/rss/get_rates.cfm?fdate=15.01.2026
  1. HTTPErrorURLError-дың жеке жағдайы, ал except тізбегінде жекесін жалпысынан жоғары қояды. Әйтпесе бірінші тармақ бәрін алып кетеді де, жауап коды жоғалады:
import urllib.error

try:
    raise urllib.error.HTTPError("url", 404, "Not Found", None, None)
except urllib.error.HTTPError as error:
    print("жауап коды:", error.code, error.reason)
except urllib.error.URLError as error:
    print("желі қолжетімсіз:", error.reason)
жауап коды: 404 Not Found

Дереккөздер

Мәтінде қате не теру қатесі кездессе, бізге айтыңыз

Тапсырманы тексеру

Алдымен VS Code-та шешіп, іске қосыңыз — редактор қатені сол жерде көрсетеді. Дайын шешімді осында қойыңыз. Тексеретін — модель: ол қатені атап көрсетеді, бірақ дайын жауапты бермейді.

Тексеру үшін кіру керек. Кіру

Пікірлер (0)

Әзірге пікір жоқ. Бірінші болыңыз.