Python: деректен өз есебіңізге дейін 19-сабақ (барлығы 56)
XML талдау: жолдың орнына ағаш, тегтің орнына `None` және аттың алдындағы мекенжай
Python курсының он тоғызыншы сабағы. Ұлттық банктің сол жауабы, бірақ оны талдағыш оқиды: 39 валюта, бағам жолдың кесегі емес, тег аты бойынша. Плюс екі тұзақ: жоқ тег үшін `find` `None` қайтарады, ал аттар кеңістігі бар тег қысқа атымен мүлде табылмайды.
Не үшін керек
Өткен сабақта доллар бағамын былай алдық: <title>USD</title> дегенді тауып, бір кесекті, содан кейін тағы бір кесекті кестік. Бұл банк жағында бірінші өзгеріс болғанға дейін ғана жұмыс істейді.
Белгілеу — жол емес. Онда құрылым бар: тегтер бірінің ішінде бірі, әрқайсысының аты, кейбірінің атрибуттары бар. Талдағыш дәл сол құрылымды оқиды, сонда «доллар бағамы» таңбаның нөмірі емес, ағаштағы мекенжай болады.
Стандартты кітапхана мұны өзі істей алады: xml.etree.ElementTree модулі, ештеңе орнатудың қажеті жоқ.
Бірден тұтас
talda.py файлы. Іске қосу: ортадан python talda.py.
Міндеттісі — алғашқы екі блок: ағаш және керек валюталарды тег аты бойынша таңдау. Үшінші мен төртіншісі талдауды бәрінде кезекпен бұзатын екі тұзақты көрсетеді.
"""19-сабақ: XML талдау — бөтен белгілеуден алынған сандар.
Өткен сабақта доллар бағамын жол әдістерімен алдық: тегті тауып, кесектерге
бөлдік. Олай істеуге болмайды: бір бос орын өзгерсе, талдау бұзылады. Бүгін сол
жауапты тег дегеннің не екенін білетін талдағыш оқиды.
"""
import urllib.request
import xml.etree.ElementTree as ET
URL = "https://nationalbank.kz/rss/get_rates.cfm?fdate=15.01.2026"
WANTED = ("USD", "EUR", "RUB", "CNY")
request = urllib.request.Request(URL, headers={"User-Agent": "shanraq-course/1.0"})
with urllib.request.urlopen(request, timeout=30) as answer:
charset = answer.headers.get_content_charset() or "utf-8"
text = answer.read().decode(charset)
print("== жолдың орнына ағаш")
root = ET.fromstring(text)
print("түбір:", root.tag, "| баласы:", len(root))
print("күні:", root.findtext("date"))
print("валюта саны:", len(root.findall("item")))
print()
print("== тег аты бойынша төрт валюта")
for item in root.findall("item"):
code = item.findtext("title")
if code not in WANTED:
continue
rate = float(item.findtext("description"))
quant = int(item.findtext("quant"))
print(f"{code}: {rate:8.2f} — {quant} бірлікке | {item.findtext('fullname')}")
print()
print("== белгілеуде жоқ нәрсе")
first = root.find("item")
print("бөтен тег бойынша find:", first.find("net-takogo-tega"))
print("әдепкісі бар findtext:", first.findtext("net-takogo-tega", "мұндай тег жоқ"))
try:
print(first.find("net-takogo-tega").text)
except AttributeError as error:
print("ал былай құлайды:", error)
print()
print("== аттар кеңістігі: алдында мекенжайы бар тег")
atom = ET.fromstring('<feed xmlns="http://www.w3.org/2005/Atom">'
'<entry><title>Бағам</title></entry></feed>')
print("түбір тегі:", atom.tag)
print("қысқа ат бойынша іздеу:", atom.find("entry"))
NS = {"a": "http://www.w3.org/2005/Atom"}
print("кеңістігімен іздеу:", atom.find("a:entry/a:title", NS).text)
Шығатыны:
== жолдың орнына ағаш
түбір: rates | баласы: 45
күні: 15.01.2026
валюта саны: 39
== тег аты бойынша төрт валюта
USD: 510.43 — 1 бірлікке | ДОЛЛАР США
EUR: 594.86 — 1 бірлікке | ЕВРО
CNY: 73.21 — 1 бірлікке | КИТАЙСКИЙ ЮАНЬ
RUB: 6.49 — 1 бірлікке | РОССИЙСКИЙ РУБЛЬ
== белгілеуде жоқ нәрсе
бөтен тег бойынша find: None
әдепкісі бар findtext: мұндай тег жоқ
ал былай құлайды: 'NoneType' object has no attribute 'text'
== аттар кеңістігі: алдында мекенжайы бар тег
түбір тегі: {http://www.w3.org/2005/Atom}feed
қысқа ат бойынша іздеу: None
кеңістігімен іздеу: Бағам
Талдау
Жол емес, ағаш
root = ET.fromstring(text)
fromstring белгілеу мәтінін алады да, түбірлік элементті қайтарады. Әрі қарай мәтінмен емес, ағашпен жұмыс істейді:
root.tag— тегтің аты;len(root)— тікелей баласы нешеу;root.find("item")— осы аты бар бірінші бала;root.findall("item")— осындай балалардың бәрі;root.findtext("date")— осындай бірінші баланың ішіндегі мәтін.
Шығудағы айырмаға назар аударыңыз: түбірдің баласы 45, ал item — 39. Қалған алтауы — generator, title, link және басқа бас бөлігі. findall тек сұралғанды алады; len бәрін санайды.
Елестетіп көріңіз. Кітаптың мазмұны. Үшінші тарауды табу үшін басынан бастап әріп санамайсыз — мазмұнға қарап, бетті ашасыз.
Мәндер әрқашан жол
rate = float(item.findtext("description"))
quant = int(item.findtext("quant"))
XML-де сан жоқ — онда тек мәтін бар. findtext "510.43" қайтарады, әрі ол жол болып тұрғанда оны қосуға да, шамасы бойынша салыстыруға да келмейді.
Бұл — JSON мен күндер туралы болған сол әңгіме: санға талдау кезінде айналдырады. Әрі сол жерде, талдау кезінде, n/a-мен, бос жолмен және нүктенің орнындағы үтірмен не істейтіні көрінеді.
Мұндағы quant әшекей емес: банк бағамды әрдайым бір бірлікке бере бермейді. Өзбек сомы — жүзге, иран риалы — мыңға. quant-қа бөлмей салыстыру жүз есе қателеседі, әрі үнсіз қателеседі.
find қате емес, None қайтарады
бөтен тег бойынша find: None
әдепкісі бар findtext: мұндай тег жоқ
ал былай құлайды: 'NoneType' object has no attribute 'text'
Тег болмаса, find None береді — әрі бұл жетінші сабақтағы сол жетіспейтін мән. .text деп сұрағанша ештеңе болмайды; сұрадыңыз — бағдарлама зиянсыз көрінетін жерде құлайды.
Осыдан ереже шығады: find(...).text емес, әдепкісі бар findtext алады. Екі әрекеттің орнына бір функция, әрі жетіспейтін мән туралы шешім құлағанға дейін емес, бірден қабылданады.
Аттар кеңістігі: алдында мекенжайы бар тег
түбір тегі: {http://www.w3.org/2005/Atom}feed
қысқа ат бойынша іздеу: None
кеңістігімен іздеу: Бағам
Міне, RSS, Atom немесе банк алмасуын алғаш талдағандардың бәрі сүрінетін тұзақ. Белгілеуде xmlns="..." болса, әр тегтің аты көрінгеннен ұзын: оның алдында жүйелі жақшада аттар кеңістігінің мекенжайы тұрады.
find("entry") аты entry болатын тегті іздейді — әрі таппайды, өйткені тегтің аты {http://www.w3.org/2005/Atom}entry. Бұл кезде қате де жоқ: тағы да None, тағы да үнсіздік.
Префикстер сөздігімен емделеді:
NS = {"a": "http://www.w3.org/2005/Atom"}
atom.find("a:entry/a:title", NS)
Ұлттық банктің жауабында аттар кеңістігі жоқ — сондықтан ол сабақта қысқа талданады. Бірақ бірінші бөтен фидтен-ақ мұны кездестіресіз.
Бөтен XML және қауіпсіздік
ElementTree не берілсе, соны талдайды, әрі әдейі құрастырылған файлмен оны көп жад жегізуге болады. Бақылаудағы дерек үшін — банк, статистика — бұл мәселе емес. Пайдаланушылар жіберетін файлдар үшін defusedxml алады: сол интерфейс, өшірілген қауіпті мүмкіндіктер.
Бұл туралы айту үндемегеннен адалырақ: бөтен белгілеуді талдау — бөтен кірістірумен жұмыс.
Сабақ картасы
Өз сөзіңізбен айтыңыз
Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптары — сабақтың соңында.
- Нәтиже бірдей болса, ағашты талдау жол бойынша іздеуден немен жақсы?
- Жоқ тег үшін
findне қайтарады, әрі мұның қаупі неде? xmlnsбар белгілеудеfind("entry")тегті неге таппайды?
Жаттығу
Тапсырмаға дейінгі үш қысқа қадам: болжау, толықтыру, жөндеу. Жауаптары — сабақтың соңында, бірақ алдымен өзіңіз жауап беріңіз.
1. Болжаңыз. Бұл бағдарлама не басып шығарады?
import xml.etree.ElementTree as ET
root = ET.fromstring("<rates><item><title>USD</title></item><item><title>EUR</title></item></rates>")
print(len(root.findall("item")), root.find("item").findtext("title"))
2. Бос орынды толтырыңыз. ... орнына мәнді санға айналдырыңыз.
import xml.etree.ElementTree as ET
root = ET.fromstring("<item><description>510.43</description></item>")
value = root.findtext("description")
print(type(value).__name__, ... * 2)
3. Жөндеңіз. Бұл жауапта change тегі жоқ, әрі бағдарлама құлайды. Ол бұл туралы сөзбен айтатындай етіңіз.
import xml.etree.ElementTree as ET
root = ET.fromstring("<item><title>USD</title></item>")
print(root.find("change").text)
Тапсырма
Міндетті. Ұлттық банктің сол бекітілген күнгі жауабын алып, оны талдағышпен талдаңыз:
URL = "https://nationalbank.kz/rss/get_rates.cfm?fdate=15.01.2026"
WANTED = ("USD", "RUB", "UZS")
Жауаптағы күнді және ондағы валюта санын басып шығарыңыз. Содан кейін үш валютаның әрқайсысы бойынша — бағамды берілген күйінде, ол неше бірлікке берілгенін және бір бірлігі қанша тұратынын (нүктеден кейін төрт таңба). Соңғы жолмен жауапта жоқ тегті сұраңыз — құлаудың орнына әдепкімен.
Күтілетін шығу:
күні: 15.01.2026 | жауаптағы валюта: 39
USD: 510.43 — 1 бірлікке → бір бірлігі 510.4300 теңге
RUB: 6.49 — 1 бірлікке → бір бірлігі 6.4900 теңге
UZS: 4.24 — 100 бірлікке → бір бірлігі 0.0424 теңге
жауапта автор жоқ: author тегі жоқ
Дайын болғаны: шығу жол-жолмен сәйкес келеді; белгілеу бойынша бірде-бір split жоқ — тек find, findall, findtext; бір бірліктің бағамы quant-қа бөлумен саналған; жоқ тег find-тан кейінгі None тексеруімен емес, әдепкімен өңделген.
Өз деректеріңізде. Өзіңіз оқитын кез келген RSS-фидті алыңыз — жаңалық, блог, подкаст. Оны сол тәсілмен талдап, алғашқы бес жазбаның тақырыбын басып шығарыңыз. Фидте xmlns болса, мұны бос нәтижеден білесіз — енді не істеу керегін де білесіз.
Қалауыңызша.
- Кез келген элемент үшін
item.attribбасып шығарып, ондағы нәрсеге қараңыз. - Жауаптан
quant= 1000 болатын валютаны тауып, оның бір бірлікке бағамын санаңыз. - Сол жауапты өткен сабақтағы жол әдістерімен талдап, кодтың ұзындығын салыстырыңыз.
Жобада бұл қайда тұрады
Шолу екінші нағыз дереккөзді алады — валюта бағамын, — әрі оны сенімді алады: талдау енді банктің бос орынды жылжытуына тәуелді емес. Әрі қарай екі дереккөздің сандарын бір кестеге жинауға болады: жылдық инфляция және күндік бағам.
Қарыздар. Біз жауапты жадқа тұтас оқимыз — бір күн үшін бұл дұрыс, он жылдық тарих үшін енді дұрыс емес. Әрі quant-ты ескердік, ал бағам өзгермейтін демалыс пен мереке күндерін шолу әзірге ажыратпайды.
Жауаптар
Жауаптарды көрсету
Сұрақтарға
- Өйткені нәтиже тек бүгін бірдей. Жол бойынша талдау таңбалардың ретіне сүйенеді: бос орын өзгерсе, тегтердің орны ауысса, өріс қосылса — ол үнсіз басқа нәрсені ала бастайды. Талдағыш құрылымға сүйенеді, ал белгілеу үшін құрылым — уәде.
Noneқайтарады. Қаупі — қате тег жоқ жерде емес,None-нан.textсұралған жерде шығады. Сондықтан әдепкісі барfindtextалады.- Өйткені
xmlnsболғанда тегтің толық аты кеңістіктің мекенжайын қамтиды:{http://www.w3.org/2005/Atom}entry. Қысқа ат оған тең емес, әріfindадал түрдеNoneдейді.
Жаттығуға
2 USD.findallекі элементті де тапты,find— біріншісін, алfindtextоныңtitle-ының ішіндегі мәтінді алды.
2 USD
float(value).findtextәрқашан жол береді, ал жолды екіге қалағандай көбейтуге болмайды:"510.43" * 2екі еселенген санды емес, екі еселенген жолды берер еді.
import xml.etree.ElementTree as ET
root = ET.fromstring("<item><description>510.43</description></item>")
value = root.findtext("description")
print(type(value).__name__, float(value) * 2)
str 1020.86
findNoneқайтарды, алNone-да.textжоқ. Әдепкі сұраққа бірден жауап береді:
import xml.etree.ElementTree as ET
root = ET.fromstring("<item><title>USD</title></item>")
print(root.findtext("change", "өзгеріс жоқ"))
өзгеріс жоқ
Пікірлер (0)
Пікір қалдыру үшін кіріңіз →
Әзірге пікір жоқ. Бірінші болыңыз.