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), әрі оның типінен не болғаны көрінеді.
Маңызды нәзіктік: HTTPError — URLError-дың жеке жағдайы. Демек, оныншы сабақтағыдай, тармақтардың реті шешеді: жекесі жалпысынан жоғары, әйтпесе 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-ті алыңыз — тек енді оның сіздің орныңызға не істейтінін білесіз.
Сабақ картасы
Өз сөзіңізбен айтыңыз
Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптары — сабақтың соңында.
answer.read()неге байт береді, әріdecodeүшін кодтауды қайдан алады?HTTPErrorURLError-дан немен өзгеше, әрі тармақтардың реті неге маңызды?- Желі жылдам болса да, сұранысқа
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немен жауап беретінін оқыңыз.
Жобада бұл қайда тұрады
Шолуда екінші дереккөз пайда болады. Инфляция Дүниежүзілік банктен жылына бір рет келетін; валюта бағамын Ұлттық банк әр жұмыс күні береді, әрі шолу алғаш рет «бүгіні» бар дерек алады.
Қарыздар. Бағамды белгілеуден жол әдістерімен суырып алдық — олай істеуге болмайды, әрі келесі сабақ соны жөндейді. Әрі әр іске қосуда желіге барамыз: дискідегі кэш он бірінші сабақтан бері бар, ал оны күнмен байланыстыру әлі алда.
Жауаптар
Жауаптарды көрсету
Сұрақтарға
- Өйткені желімен мәтін емес, байт жүреді: HTTP-дың ішінде не бар екенін білуге міндеті жоқ. Кодтауды сервердің өзі атайды —
Content-Typeтақырыбында; оны содан алады, ал сервер үндемей қалған жағдайға әдепкі туралы келіседі. HTTPError— сервер жауап берді, бірақ бас тартумен: оның коды бар (404,403,500).URLError— жауап мүлде болмады: желі жоқ, ат шешілмеді. Біріншісі екіншісінің жеке жағдайы, сондықтан оны бұрын ұстау керек, әйтпесе жалпы тармақ бәрін алып қояды.- Өйткені сервер жауап беруге міндетті емес.
timeout-сыз бағдарлама ол үндемеген уақыттың бәрін күтеді, әрі сырттан бұл «желі бүгін нашар» емес, «қатып қалды» болып көрінеді.
Жаттығуға
bytes 21жәнеstr <?xml. Желімен байт келеді;decodeолардан жол жасайды, әрі таныс әріптер тек жолда болады.
bytes 21
str <?xml
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
HTTPError—URLError-дың жеке жағдайы, ал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
Пікірлер (0)
Пікір қалдыру үшін кіріңіз →
Әзірге пікір жоқ. Бірінші болыңыз.