Ачааллж байна...
Өмнөх хичээлд бид status code -ыг зохицуулсан: 404, 400, 500. Тэдгээр нь бүгд ХАРИУ байсан — сервер танд ямар нэг зүйл хэлж байсан, хэдийгээр тэр нь "олдсонгүй" эсвэл "би эвдэрсэн" гэсэн зүйл байсан ч.
Гэвч заримдаа хариу ОГТ ИРДЭГГҮЙ.
Интернэт тань тасарсан
Домэйн байхгүй (та хаягийг буруу бичсэн)
Сервер унтарсан
Сервер маш удаан хариулж байна
Дунд зам дээр ямар нэг зүйл эвдэрсэн
Тэдгээр нь ТЭС ӨӨР төрлийн асуудал юм. Status code байхгүй, учир нь хариу ирээгүй. raise_for_status() нь тусалж чадахгүй, учир нь шалгах юм байхгүй.
Тэр үед requests нь ӨӨР төрлийн алдаа гаргана.
Мөн энэ хичээлд би нэг ЧУХАЛ дадлыг заана: timeout= параметр. Түүнгүйгээр таны программ МӨНХӨД хүлээж болно — тэр нь бодит, ноцтой асуудал юм.
Энэ хичээл нь Курс 1-ийн try/except мэдлэгийг сүлжээний ертөнцөд өргөжүүлнэ.
Ресторан руу буцъя. Өмнөх хичээлүүдэд:
200 OK — зөөгч хоол авчирлаа
404 — зөөгч "тэр хоол цэсэнд байхгүй" гэлээ
500 — зөөгч "гал тогоо шатсан" гэлээ
Гурвуулаа ХАРИУ. Зөөгч ирсэн, ямар нэг зүйл хэлсэн.
Одоо өөр нөхцөл байдлууд:
Та ресторанд явлаа — гэвч ресторан тэр хаягт БАЙХГҮЙ. Таны газрын зураг буруу байсан. → ConnectionError
Та ресторанд ирлээ — гэвч тэр ХААЛТТАЙ. Гэрэл унтарсан, хаалга түгжээтэй. → ConnectionError
Та захиалга өглөө — гэвч зөөгч ХЭЗЭЭ Ч ирэхгүй. Та нэг цаг хүлээлээ. Хоёр цаг. Гурав. → Timeout
Таны машин эвдэрсэн, та ресторан руу явж ч чадсангүй. → ConnectionError (интернэт тасарсан)
Эдгээрт ХАРИУ ОГТ БАЙХГҮЙ. Тиймээс status code ч байхгүй.
Зориудаар алдаа үүсгэе. Байхгүй домэйн руу хандъя:
python
import requests
hariult = requests.get("https://энэ-домэйн-байхгүй-12345.com")
print(hariult.status_code)Traceback (most recent call last):
...
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='энэ-домэйн-байхгүй-12345.com', port=443): Max retries exceeded with url: / (Caused by NameResolutionError(...): Failed to resolve 'энэ-домэйн-байхгүй-12345.com'))requests.exceptions.ConnectionError — "холбогдож чадсангүй."
Мессежийг задалъя. Failed to resolve 'энэ-домэйн-байхгүй-12345.com' гэдэг нь "тэр нэрийг IP хаяг болгож хөрвүүлж чадсангүй" гэсэн үг.
Бүлэг 11.1-ээс санаж байгаа байх: DNS нь домэйн нэрийг IP хаяг болгож хөрвүүлдэг. Хэрэв тэр нэр байхгүй бол — DNS хариулж чадахгүй.
Хамгийн чухал зүйл: hariult хувьсагч нь ХЭЗЭЭ Ч үүсээгүй. Программ requests.get() дээр унасан. hariult.status_code мөр хүртэл хүрээгүй.
Тиймээс raise_for_status() нь тусалж ЧАДАХГҮЙ. Тэр нь Response объект дээр дуудагддаг, гэвч Response огт байхгүй.
requests нь хэдэн төрлийн алдаа гаргадаг. Тэдгээр нь БҮГД requests.exceptions.RequestException -ээс өвлөдөг.
Бүлэг 7.1-ийн inheritance! RequestException нь эцэг class, бусад нь хүү class.
RequestException (эцэг — БҮХ requests-ийн алдаа)
├── ConnectionError — холбогдож чадсангүй
│ └── ConnectTimeout — холболт үүсгэхэд хугацаа хэтэрсэн
├── Timeout — хугацаа хэтэрсэн
│ ├── ConnectTimeout
│ └── ReadTimeout — хариу хүлээхэд хугацаа хэтэрсэн
├── HTTPError — 4xx/5xx (raise_for_status-аас)
├── TooManyRedirects — хэт олон шилжүүлэлт
└── JSONDecodeError — хариу нь JSON бишПрактикт та ГУРВЫГ мэдэхэд хангалттай:
1. ConnectionError — холбогдож чадсангүй.
Шалтгаанууд: интернэт тасарсан, домэйн байхгүй, сервер унтарсан, галт хана хориглосон.
2. Timeout — хэт удаан.
Сервер хариулж байна, гэхдээ маш удаан. Эсвэл огт хариулахгүй байна.
3. HTTPError — 4xx/5xx (Бүлэг 11.4-өөс танил).
Хариу ИРСЭН, гэхдээ алдааны код.
Эцэг class нь БҮГДИЙГ барина:
python
except requests.exceptions.RequestException as alda:
print(f"Ямар нэг сүлжээний алдаа: {alda}")Бүлэг 7.1-ийн polymorphism! Эцэг class-ыг барихад бүх хүү class баригдана.
timeout= — ХАМГИЙН ЧУХАЛ ДАДАЛОдоо энэ хичээлийн хамгийн чухал зүйл рүү орно.
Асуулт: хэрэв сервер ХЭЗЭЭ Ч хариулахгүй бол таны программ юу хийх вэ?
Туршиж үзье (гэхдээ болгоомжтой):
python
import requests
# Энэ сервер нь ЗОРИУДААР удаан хариулдаг
hariult = requests.get("https://httpbin.org/delay/10")
print(hariult.status_code)Энэ код нь 10 секунд ХҮЛЭЭНЭ. Программ зогсох болно, юу ч хийхгүй.
Гэвч 10 секунд бол сайн тохиолдол. Хэрэв сервер огт хариулахгүй бол?
requests нь АНХДАГЧААР МӨНХӨД хүлээдэг.
Тэр нь ноцтой асуудал:
Таны программ ХӨЛДӨНӨ
Хэрэглэгч юу болж байгааг мэдэхгүй
Ctrl+C дарж албадан зогсоох л үлдэнэ
Хэрэв тэр нь сервер дээр ажиллаж байвал — таны сервер ч бас хөлдөнө
Шийдэл: timeout= параметр.
python
import requests
try:
hariult = requests.get("https://httpbin.org/delay/10", timeout=3)
print(hariult.status_code)
except requests.exceptions.Timeout:
print("Хугацаа хэтэрлээ (3 секунд). Сервер хариулсангүй.")Хугацаа хэтэрлээ (3 секунд). Сервер хариулсангүй.Гурван секундийн дараа программ ЗОГССОН.
timeout=3 нь: "гурван секунд хүлээ, дараа нь БОЛЬ."
ДҮРЭМ: requests.get() дуудах бүрдээ timeout= бич. ҮЛ ХАМААРАХ ЗҮЙЛ БАЙХГҮЙ.
Энэ бол мэргэжлийн Python-ы хамгийн чухал зуршлуудын нэг. timeout -гүй код нь эвдэрсэн код гэж тооцогддог.
Ямар утга сонгох вэ?
5-10 секунд — ердийн API дуудалт
30 секунд — хүнд ажил (том файл татах)
2-3 секунд — хурдан хариу шаардлагатай (хэрэглэгч хүлээж байгаа)
Практик зөвлөгөө: timeout=10 -ээс эхэл. Хэрэв хэтэрч байвал — нэмээрэй.
Одоо БҮХ алдааг зохицуулсан функц бичье.
python
import requests
def tsag_agaar_avakh(lat: float, lon: float) -> dict | None:
url = "https://api.open-meteo.com/v1/forecast"
parametruud = {
"latitude": lat,
"longitude": lon,
"current": "temperature_2m,wind_speed_10m",
"timezone": "auto",
}
try:
hariult = requests.get(url, params=parametruud, timeout=10)
hariult.raise_for_status()
return hariult.json()
except requests.exceptions.Timeout:
print("Алдаа: сервер хариулсангүй (10 секунд хэтэрлээ).")
print(" Дараа дахин оролдоно уу.")
return None
except requests.exceptions.ConnectionError:
print("Алдаа: серверт холбогдож чадсангүй.")
print(" Интернэт холболтоо шалгана уу.")
return None
except requests.exceptions.HTTPError as alda:
kod = alda.response.status_code
if 400 <= kod < 500:
print(f"Алдаа: хүсэлт буруу байна ({kod}).")
else:
print(f"Алдаа: серверт асуудал гарлаа ({kod}).")
print(" Дараа дахин оролдоно уу.")
return None
except requests.exceptions.JSONDecodeError:
print("Алдаа: серверийн хариу JSON биш байна.")
return None
uguudul = tsag_agaar_avakh(47.92, 106.92)
if uguudul is None:
print("\nМэдээлэл авч чадсангүй.")
else:
odoo = uguudul["current"]
print(f"\nТемператур: {odoo['temperature_2m']}°C")
print(f"Салхи: {odoo['wind_speed_10m']} км/ц")Хэвийн үед:
Температур: 18.7°C
Салхи: 8.3 км/цОдоо интернэтээ САЛГААД ажиллуулаарай (WiFi унтраа, эсвэл кабель сугалж ав):
Алдаа: серверт холбогдож чадсангүй.
Интернэт холболтоо шалгана уу.
Мэдээлэл авч чадсангүй.Программ унасангүй. Хэрэглэгчид ТОДОРХОЙ, ХЭРЭГТЭЙ мессеж хэлэв.
Энэ бол сайн программын шинж: алдаа гарахад тэр нь ЮУ БОЛСОН, ЮУ ХИЙХИЙГ хэлдэг.
except блокуудын ДАРААЛАЛ чухал.
Курс 1-ээс санаж байгаа байх: Python нь except -үүдийг ДЭЭРЭЭС ДООШ шалгадаг, эхний тохирохыг сонгодог.
Тиймээс ТУСГАЙ алдааг ЭХЭНД, ЕРӨНХИЙ алдааг АРААС бичнэ:
python
except requests.exceptions.Timeout: # тусгай
...
except requests.exceptions.ConnectionError: # тусгай
...
except requests.exceptions.RequestException: # ЕРӨНХИЙ (эцэг class)
...Хэрэв RequestException -ыг ЭХЭНД бичвэл — тэр нь БҮГДИЙГ барих ба доод блокууд ХЭЗЭЭ Ч ажиллахгүй.
Хэрэв та тодорхой мессеж хэрэггүй бол — эцэг class -ыг бариарай:
python
import requests
def tsag_agaar_avakh(lat: float, lon: float) -> dict | None:
url = "https://api.open-meteo.com/v1/forecast"
parametruud = {
"latitude": lat,
"longitude": lon,
"current": "temperature_2m",
"timezone": "auto",
}
try:
hariult = requests.get(url, params=parametruud, timeout=10)
hariult.raise_for_status()
return hariult.json()
except requests.exceptions.RequestException as alda:
print(f"Сүлжээний алдаа: {alda}")
return NoneБогино, гэхдээ мессеж нь техникийн.
Хэрэглэгч HTTPSConnectionPool(host=... Max retries exceeded... гэсэн зүйлийг харвал — ойлгохгүй.
Практик зөвлөгөө:
Дотоод хэрэглээ, дебаг → эцэг class хангалттай
Жинхэнэ хэрэглэгч → тусгай блокууд, ойлгомжтой мессежтэй
Бүлэг 11.1-ийн сургамжийг санаж байна уу?
4xx — ТАНЫ буруу. Дахин оролдох нь УТГАГҮЙ.
5xx — СЕРВЕРИЙН буруу. Дахин оролдох нь УТГАТАЙ.
Мөн Timeout ба ConnectionError нь ч бас түр зуурын байж болно. Магадгүй сүлжээ хормын зуур эвдэрсэн.
Тиймээс: дахин оролдъё.
python
import requests
import time
def tsag_agaar_avakh(
lat: float, lon: float, oroldlogo: int = 3
) -> dict | None:
url = "https://api.open-meteo.com/v1/forecast"
parametruud = {
"latitude": lat,
"longitude": lon,
"current": "temperature_2m",
"timezone": "auto",
}
for i in range(1, oroldlogo + 1):
try:
hariult = requests.get(url, params=parametruud, timeout=10)
hariult.raise_for_status()
return hariult.json()
except requests.exceptions.HTTPError as alda:
kod = alda.response.status_code
if 400 <= kod < 500:
# ТАНЫ буруу — дахин оролдох нь УТГАГҮЙ
print(f"Хүсэлт буруу байна ({kod}). Зогсож байна.")
return None
# 5xx — дахин оролдоё
print(f"Серверийн алдаа ({kod}). Оролдлого {i}/{oroldlogo}")
except (
requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
) as alda:
print(f"Сүлжээний алдаа. Оролдлого {i}/{oroldlogo}")
# Дахин оролдохоос өмнө ХҮЛЭЭНЭ
if i < oroldlogo:
khuleeltiin_khugatsaa = 2**i # 2, 4, 8 секунд
print(f" {khuleeltiin_khugatsaa} секунд хүлээж байна...")
time.sleep(khuleeltiin_khugatsaa)
print(f"{oroldlogo} удаа оролдсон ч амжилтгүй.")
return NoneХэрэв сүлжээ тасарсан бол:
Сүлжээний алдаа. Оролдлого 1/3
2 секунд хүлээж байна...
Сүлжээний алдаа. Оролдлого 2/3
4 секунд хүлээж байна...
Сүлжээний алдаа. Оролдлого 3/3
3 удаа оролдсон ч амжилтгүй.Гурван удаа оролдож, дараа нь эвлэгээр бууж өглөө.
Хэдэн зүйлийг тайлбарлая.
if 400 <= kod < 500: return None — 4xx бол ШУУД зогсоно. Дахин оролдох нь утгагүй — та ижил буруу хүсэлт илгээх болно.
Энэ бол Бүлэг 11.1-ийн ялгааны ПРАКТИК хэрэглээ.
except (Timeout, ConnectionError) — ХОЁР алдааг НЭГ блокт барьж байна. Tuple ашиглана (Бүлэг 9.3-т ижил хэв маягийг харсан: except (TypeError, ValueError)).
khuleeltiin_khugatsaa = 2**i — exponential backoff (экспоненциал ухралт).
Хүлээх хугацаа нь ӨСНӨ: 2, 4, 8 секунд.
Яагаад? Учир нь хэрэв сервер ачаалалтай байвал — бүх client нэг дор дахин оролдвол тэр нь бүр ч муудна. Хүлээлтийг өсгөснөөр ачаалал тархана.
time.sleep(секунд) — программыг тэр хугацаанд ЗОГСООНО.
if i < oroldlogo: — СҮҮЛЧИЙН оролдлогын ДАРАА хүлээхгүй. Утгагүй.
Шударгаар хэлье: дахин оролдох нь үргэлж зөв БИШ.
Дахин оролд:
Timeout, ConnectionError — түр зуурын байж болно
5xx — сервер засагдаж болно
429 Too Many Requests — гэхдээ УРТ хүлээлттэй!
БҮҮ дахин оролд:
4xx (429-аас бусад) — та буруу зүйл асуусан, ижил хариу ирнэ
POST хүсэлт — АНХААР! Хэрэв эхний хүсэлт нь үнэндээ АМЖИЛТТАЙ болсон ч хариу нь алга болсон бол — дахин оролдоход ХОЁР зүйл үүснэ.
Тэр сүүлчийн зүйл нь чухал. Бүлэг 11.1-ээс:
GET нь АЮУЛГҮЙ. Хэдэн ч удаа дуудсан ямар ч зүйл өөрчлөгдөхгүй.
POST нь АЮУЛГҮЙ БИШ. Хоёр удаа дуудвал — хоёр ном нэмэгдэнэ.
Тиймээс: GET -ыг чөлөөтэй дахин оролдож болно. POST -ыг БОЛГООМЖТОЙ.
Session — хурдан болгохЖижиг ч гэсэн ашигтай зөвлөгөө.
Хэрэв та ОЛОН хүсэлт илгээх гэж байгаа бол — Session ашиглаарай.
python
import requests
khotууd = [
(47.92, 106.92, "Улаанбаатар"),
(49.49, 105.92, "Дархан"),
(49.03, 104.08, "Эрдэнэт"),
]
with requests.Session() as session:
for lat, lon, ner in khotууd:
try:
hariult = session.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": lat,
"longitude": lon,
"current": "temperature_2m",
"timezone": "auto",
},
timeout=10,
)
hariult.raise_for_status()
temp = hariult.json()["current"]["temperature_2m"]
print(f"{ner:<15} {temp:>6.1f}°C")
except requests.exceptions.RequestException:
print(f"{ner:<15} мэдээлэл авч чадсангүй")Улаанбаатар 18.7°C
Дархан 17.2°C
Эрдэнэт 16.9°Crequests.Session() нь холболтыг ДАХИН АШИГЛАДАГ.
Ердийн requests.get() нь хүсэлт бүрд ШИНЭ холболт үүсгэдэг. Тэр нь цаг үрдэг (гар барих, шифрлэлт тохируулах).
Session нь холболтыг НЭЭЛТТЭЙ үлдээж, дараагийн хүсэлтэд ашигладаг. Олон хүсэлтэд хамаагүй хурдан.
with ... as session: — Курс 1-ээс танил бичлэг (файл нээхтэй ижил). Ажил дуусахад холболтыг автоматаар ХААНА.
(with яг юу хийдгийг Бүлэг 13.3-т бүрэн тайлбарлана.)
Хэрэв сонирхвол: интернэтээ САЛГААД (WiFi унтраад) программаа ажиллуулж, ConnectionError -ыг өөрийн нүдээр хараарай. Дараа нь интернэтээ залгаад, ажиллахыг нь хараарай.
Тэр туршилт нь try/except -ийн бодит ач холбогдлыг мэдрүүлнэ.
Мөн хүсвэл: timeout= -ыг ЗОРИУДААР жижиг болгоод (жишээ нь timeout=0.001) ажиллуулж, Timeout алдааг хараарай. Дараа нь timeout= -ыг БҮРЭН устгаад, https://httpbin.org/delay/10 руу хандаж, программ 10 секунд хөлдөж байгааг мэдрээрэй.
Тэр хөлдөлт нь timeout -ыг хэзээ ч мартахгүй болгоно.
1. timeout= мартах
python
hariult = requests.get(url) # timeout БАЙХГҮЙПрограмм МӨНХӨД хүлээж болно. requests дуудах бүрдээ timeout= бич.
2. except Exception гэж бүх зүйлийг барих
python
try:
...
except Exception:
print("Алдаа")Таны кодын алдаа (KeyError, TypeError) ч бас баригдана. Дараа нь та юу болсныг мэдэхгүй.
Тодорхой бари: except requests.exceptions.RequestException.
3. except -ийн дарааллыг буруу тавих
python
except requests.exceptions.RequestException: # ЕРӨНХИЙ — эхэнд!
...
except requests.exceptions.Timeout: # ХЭЗЭЭ Ч ажиллахгүй
...Эцэг class нь бүгдийг барина. Хоёр дахь блок хэзээ ч ажиллахгүй.
Тусгайг эхэнд, ерөнхийг араас.
4. 4xx -ыг дахин оролдох
python
for i in range(3):
try:
hariult = requests.get(url, timeout=10)
hariult.raise_for_status()
return hariult.json()
except requests.exceptions.HTTPError:
continue # 404 -ыг гурван удаа оролдож байна!Утгагүй. 404 нь гурван удаа ижил 404 байна. 4xx бол ШУУД зогсоно.
5. POST -ыг сохроор дахин оролдох
python
for i in range(3):
requests.post(url, json=uguudul, timeout=10)Хэрэв эхний хүсэлт нь АМЖИЛТТАЙ болсон ч хариу нь алга болсон бол — гурван зүйл үүсэх болно.
POST -ыг болгоомжтой.
6. Хүлээлтгүй дахин оролдох
python
for i in range(100):
try:
...
except requests.exceptions.ConnectionError:
continue # хүлээлтгүй, шууд дахин!Сервер рүү зуун хүсэлт секундэд илгээнэ. 429 ирнэ, эсвэл таныг хориглоно.
time.sleep() -ээр хүлээ.
Status code алдаа (404, 500) нь ХАРИУ — сервер ямар нэг зүйл хэлсэн.
Сүлжээний алдаа нь ХАРИУ ОГТ БАЙХГҮЙ — status code ч байхгүй, raise_for_status() тусалж чадахгүй.
Гурван гол алдаа:
ConnectionError — холбогдож чадсангүй (интернэт тасарсан, домэйн байхгүй, сервер унтарсан)
Timeout — хэт удаан
HTTPError — 4xx/5xx (Бүлэг 11.4)
Бүгд RequestException -ээс өвлөдөг (Бүлэг 7.1-ийн inheritance). Эцгийг барихад бүгд баригдана.
timeout= — ХАМГИЙН ЧУХАЛ ДАДАЛ. requests нь анхдагчаар МӨНХӨД хүлээдэг. timeout -гүй код бол эвдэрсэн код.
python
requests.get(url, params=..., timeout=10)except -ийн ДАРААЛАЛ: тусгай алдааг ЭХЭНД, ерөнхийг (эцэг class) АРААС.
Дахин оролдох (retry):
Дахин оролд: Timeout, ConnectionError, 5xx
БҮҮ оролд: 4xx (та буруу зүйл асуусан — ижил хариу ирнэ)
POST -ыг БОЛГООМЖТОЙ — хоёр удаа дуудвал хоёр зүйл үүснэ
Exponential backoff: хүлээлтийг ӨСГӨ (2, 4, 8 секунд). Сервер дээрх ачаалал тархана.
Хүлээлтгүй дахин оролдох нь 429 -д хүргэнэ. time.sleep() ашигла.
requests.Session() — олон хүсэлтэд холболтыг дахин ашигладаг, хамаагүй хурдан.
Бүлэг 11 дууслаа. Таны программ одоо интернэттэй ярьж чадна — мөн алдаа гарахад эвлэгээр зогсож чадна.
Бүлэг 12-т бид өөр төрлийн ур чадвар руу орно: олон файлтай төсөл.
Одоо хүртэл таны бүх программ НЭГ файлд байсан. Бүлэг 9.4-ийн номын сангийн систем нь бараг 400 мөр болсон — тэр нь аль хэдийн хэтэрхий урт.
Мэргэжлийн төслүүд нь хэдэн арван, зуун файлаас бүрддэг. Бүлэг 12-т та кодоо хэрхэн хуваах, өөрийн модулийг хэрхэн import хийх, package гэж юу вэ, if __name__ == "__main__" гэсэн нууцлаг мөр юу хийдгийг сурна.
Дараа нь Түвшин 4-ийн guided build: жинхэнэ цаг агаарын API-аас өгөгдөл татдаг, ОЛОН ФАЙЛТАЙ, бүрэн type hint-тэй, мэргэжлийн бүтэцтэй программ.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.