Python: деректен өз есебіңізге дейін 35-сабақ (барлығы 56)
Есеп-бет: кесте, сурет және дереккөз бір файлда
Python курсының отыз төртінші сабағы. HTML-ді деректен жинаймыз: жол жалғаудың орнына үлгі, кестеден келгеннің бәріне `html.escape`, файл ішіндегі сурет — және өзін өзі тексеретін бет. Бір файл, ол кез келген браузерде ашылады, PDF-ке басылады және хатпен бір тіркеме болып кетеді.
Не үшін керек
Жиынтықта үш файл бар: сандары бар CSV, суреті бар PNG және экрандағы журнал. Алушыға оларды өзі жинауға тура келеді: кестені CSV оқи алатын бір нәрседе ашып, суретті бөлек қарап, екеуі бір нәрсе туралы екенін өзі түсінуі керек.
Бет мұны бір файлмен шешеді. Ол кез келген браузерде ештеңе орнатпай ашылады, сол браузерден PDF-ке басылады, хатпен бір тіркеме болып кетеді әрі бәрінде бірдей көрінеді.
Ол үшін HTML білудің қажеті жоқ: бес тег жетеді. Керегі басқа — деректі белгілеуге сол күйінде қоюға болмайтынын түсіну.
Бірден тұтас
otchet.py файлы. Деректе белгілеуді бұзатын атау әдейі жатыр.
"""34-сабақ: есеп-бет.
Кесте, сурет және дереккөз бір HTML файлға жиналады. Дерек белгілеуге тек
экрандау арқылы түседі — әйтпесе бет бір «&» таңбасынан бұзылады.
"""
import base64
import html
from datetime import date
from pathlib import Path
from string import Template
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd
HERE = Path(__file__).resolve().parent
# Атауда белгілеуді бұзатын нәрсе әдейі тұр: бұрыштық жақшалар мен амперсанд.
rows = [
("Қазақстан", 2024, 8.7),
("Өзбекстан", 2024, 9.6),
("<b>Ресей</b> & Ко", 2024, 8.4),
]
table = pd.DataFrame(rows, columns=["ел", "жыл", "инфляция"])
SOURCE = "Дереккөз: Дүниежүзілік банк, FP.CPI.TOTL.ZG"
print("== деректі сол күйінде қоюға неге болмайды")
raw = f"<td>{rows[2][0]}</td>"
safe = f"<td>{html.escape(rows[2][0])}</td>"
print("сол күйінде:", raw)
print("экрандаумен:", safe)
# Сурет бірден беттің ішіне: хатпен жіберуге болатын бір файл.
fig, ax = plt.subplots(figsize=(6, 3))
ax.bar(table["ел"].str.replace("<b>", "", regex=False).str.replace("</b>", "", regex=False),
table["инфляция"], color="#b03a2e")
ax.set_ylim(0, 12)
ax.set_ylabel("инфляция, %")
ax.set_title("Инфляция, 2024")
png = HERE / "kartinka.png"
fig.savefig(png, dpi=110, bbox_inches="tight")
plt.close(fig)
picture = base64.b64encode(png.read_bytes()).decode("ascii")
PAGE = Template("""<!doctype html>
<meta charset="utf-8">
<title>$title</title>
<h1>$title</h1>
<p>Дайындалды: $day. Бақылау саны: $count.</p>
<img alt="$title" src="data:image/png;base64,$picture">
<table border="1" cellspacing="0" cellpadding="6">
$head
$body
</table>
<p>$source</p>
""")
head = "<tr>" + "".join(f"<th>{html.escape(name)}</th>" for name in table.columns) + "</tr>"
body = "\n".join(
"<tr>" + "".join(f"<td>{html.escape(str(cell))}</td>" for cell in row) + "</tr>"
for row in table.itertuples(index=False)
)
page = PAGE.substitute(
title=html.escape("Үш елдегі инфляция"),
day=date(2026, 9, 11).isoformat(),
count=len(table),
picture=picture,
head=head,
body=body,
source=html.escape(SOURCE),
)
out = HERE / "otchet.html"
out.write_text(page, encoding="utf-8")
print()
print("== не шықты")
print("кестедегі жол:", page.count("<tr>") - 1, "| баған:", page.count("<th>"))
print("сурет файлдың ішінде:", "data:image/png;base64," in page)
print("қауіпті атау белгілеуге тег болып түсті:", "<b>Ресей</b>" in page)
print("ол оқырманға мәтін болып көрінеді:", "<b>Ресей</b> & Ко" in page)
print("файл:", out.name, "| жасалды:", out.exists())
Шығысы:
== деректі сол күйінде қоюға неге болмайды
сол күйінде: <td><b>Ресей</b> & Ко</td>
экрандаумен: <td><b>Ресей</b> & Ко</td>
== не шықты
кестедегі жол: 3 | баған: 3
сурет файлдың ішінде: True
қауіпті атау белгілеуге тег болып түсті: False
ол оқырманға мәтін болып көрінеді: True
файл: otchet.html | жасалды: True
Ал қасында otchet.html пайда болады — іші суретті бет.
Талдау
Экрандау — артық сақтық емес
Шығыстың бірінші блогы — сабақтың бүкіл мәні:
сол күйінде: <td><b>Ресей</b> & Ко</td>
экрандаумен: <td><b>Ресей</b> & Ко</td>
Экрандаусыз деректегі бұрыштық жақшалар белгілеуге айналды: мәтіннің бір бөлігі жоғалды, бір бөлігі жуан болды, ал & тіпті бетті бұзуы мүмкін. html.escape <, > және & таңбаларын қауіпсіз тізбекке айналдырады, әрі оқырман деректе не болса, соны көреді.
Бұл — жиырма бірінші сабақтағы params= және жиырма жетіншідегі query туралы сол ой: дерек кодқа айналмауға тиіс. SQL-де бұл инъекция, HTML-де — жақсы жағдайда бұзылған беттеу, нашар жағдайда сіздің бетіңіздегі бөтен скрипт.
Ереже қарапайым: сізден келмегеннің бәрі экранданады — тақырыптар, ұяшықтар, атаулар, дереккөз. Өзіңіздікі бүгін қауіпсіз, ал ертең анықтамада «ЖШС “Таң & Ко”» пайда болады.
Жалғаудың орнына үлгі
Стандартты кітапханадағы string.Template: мәтінде $title, $body тұрады, ал substitute мәндерді қояды. Бұл + арқылы жалғаудан және бүкіл бетке арналған f-жолдардан несімен жақсы:
- белгілеу тұтас жатады, ол көрініп тұрады әрі кодты қозғамай түзетуге болады;
- ұмытылған қойылым — үнсіз бос орын емес, қате;
- дерек ішіндегі
$қауіпті емес: тек аты аталған орындар ғана қойылады.
Үлгі Template-ден асып кеткенде — циклдер, шарттар, бір бөлікті екіншісіне қосу пайда болғанда — Jinja2 алады. Ол да солай құрылған, тек көбірек біледі, әрі ондағы escape әдепкі күйінде қосулы. Бұл курста стандартты кітапхана жетеді.
DataFrame-нен кесте
Екі жолмен: тақырып table.columns-тан, денесі itertuples-тен. pandas-та дайын to_html да бар, әрі ол әдепкі күйінде экрандайды, — бірақ ол өз кластары мен индексін ала келеді, ал оны керекті түрге келтіру көбіне екі жолды өзіңіз жазғаннан ұзақ.
Ұяшықтағы жетіспейтін мәнді бос емес, сызықшамен көрсеткен жөн: бос ұяшық ұмытылған мән сияқты көрінеді, ал сызықша «дерек жоқ» дейді — отызыншы сабақтағы сол айырма.
Файл ішіндегі сурет
base64.b64encode(png.read_bytes()) суретті беттің дәл өзіне салады: src="data:image/png;base64,…". Сонда бет бір файл болып қалады — оны хатпен жібереді, әрі сурет үзіліп қалмайды.
Төлемі: бет суреттің өзінен шамамен үштен бірге үлкен болады (base64 үштен бір қосады), әрі оны бөлек кэштеуге болмайды. Бір-екі суреті бар есеп үшін бұл — дұрыс айырбас; жиырма суреті бар бет үшін — жоқ, онда суреттер қасында файлмен жатады.
Бетте не болуға тиіс
Өткен сабақтағы графиктегінің бәрі, плюс біреу:
- тақырып — бұл не;
- дайындалған күн — қашан;
- бақылау саны — қанша;
- дереккөз — сандар қайдан;
- кестенің өзі — суреттің қасында нақты мәндер тұруы үшін.
Сурет «бұл қалай көрінеді» дегенге, кесте «нақты қанша» дегенге жауап береді, әрі бірге олар екеуін де жабады.
Сабақ картасы
Өз сөзіңізбен айтыңыз
Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптар — сабақтың соңында.
<b>Ресей</b> & Коатауын экрандаусыз қойсақ, бетке не болады?- Суретті қасына қоюға болатын бола тұра, оны файл ішіне не үшін саламыз?
- Кестедегі жетіспейтін мәнді бос ұяшықпен емес, неге сызықшамен көрсетеді?
Жаттығу
Тапсырмаға дейінгі үш қысқа қадам: болжау, толықтыру, түзету. Жауаптар — сабақтың соңында, бірақ алдымен өзіңіз жауап беріңіз.
1. Болжаңыз. Бұл бағдарлама не басып шығарады?
import html
name = '<b>Ресей</b> & Ко "оңтүстік"'
print(html.escape(name))
print(html.escape(name, quote=False))
print("ұзындығы:", len(name), "| кейін:", len(html.escape(name)))
2. Бос орынды толтырыңыз. ... орнына деректің белгілеуге айналмауын қамтамасыз етіңіз.
# ұяшыққа кестеден келген нәрсе түседі
import html
cells = ["тамақ", "12 & 13", "<b>байланыс</b>"]
row = "<tr>" + "".join(f"<td>{...}</td>" for cell in cells) + "</tr>"
print(row)
print("жолдағы тег:", row.count("<td>"), "| деректен келген белгілеу:", "<b>" in row)
3. Түзетіңіз. Кестенің жолы экрандаусыз жиналған, әрі дерек тегке айналған.
# «байланыс» жуан болып келеді, ал «12 & 13» бетті бұзуы мүмкін
cells = ["тамақ", "12 & 13", "<b>байланыс</b>"]
row = "<tr>" + "".join(f"<td>{cell}</td>" for cell in cells) + "</tr>"
print(row)
print("жолдағы тег:", row.count("<td>"), "| деректен келген белгілеу:", "<b>" in row)
Тапсырма
Міндетті. Бес жылдық есепті бір бетке жинаңыз: тақырып, дайындалған күн, бақылау саны, файл ішіндегі сурет, үш бағаннан тұратын кесте (жыл, инфляция, өткен жылға өзгеріс) және төменде дереккөз. Өзгеріс бағанындағы жетіспейтін мәнді сызықшамен көрсетіңіз. otchet.html сақтап, беттің тексерісін басып шығарыңыз — өткен сабақтағы сол тізім бойынша.
Күтілетін шығыс:
бетті тексеру:
дерек жолы: 5 | баған: 3
күні көрсетілген: True
дерек көлемі аталған: True
дереккөз бетте: True
сурет файл ішінде: True
жетіспейтін мән сызықшамен: True
файл: otchet.html | жасалды: True
Дайын болғаны: шығыс жол-жолымен сәйкес келеді; мәндердің бәрі html.escape арқылы өтеді; бет жалғаумен емес, үлгімен жиналған; сурет файл ішінде жатыр; тексеру сіз жазғанды қайталамай, беттің дайын мәтінінен сұрайды. Ең бастысы — otchet.html-ді браузерде ашып, көзбен қараңыз.
Өз деректеріңізде. Өз кестеңіз бойынша бет жасап, оны өзіңізге мессенджерге жіберіңіз. Телефоннан ашыңыз. Онда оқылмайтынның бәрі — тым кең кесте, ұсақ сурет — есепті басқа біреу көргенге дейін түзететін нәрсе.
Қалауыңыз бойынша.
- Бетті браузерден PDF-ке басып, ол қай жерден үзілетінін қараңыз.
- Суреті ішінде және қасында тұрған файлдың өлшемдерін салыстырыңыз.
- Сол бетті
table.to_html()арқылы жинап, қайсысы өзіңізге жақын екенін шешіңіз.
Жобада бұл қайда тұрады
Он төртінші қадам: жиынтық үш файл беруді қояды. sholu/bet.py кестені, суретті және дереккөзді бір report.html-ге жинайды — оны браузерде ашады, PDF-ке басады және бір тіркемемен жібереді.
Сол файлдағы үш шешімді кодтан қараған жөн: беттің ішіндегі сурет, деректен келгеннің бәрін экрандау және күні бар дереккөз жолы — өткен сабақ жариялаған сол қарыз: бөлек жіберілген сурет сандардың қайдан келгенін айтпайтын.
Әлі қарастырылмағаны. Бет string.Template арқылы жиналады, әрі сыртқы көрінісі кодпен бір файлда тұр. Бет біреу болғанда бұл қалыпты; екеу болғанда белгілеу бөлек файлға сұранады — сонда Jinja2-нің де мәні шығады.
Жауаптар
Жауаптарды көрсету
Сұрақтарға
- Бұрыштық жақшалар белгілеуге айналады: «Ресей» жуан болып келеді, ал тегтердің өзі мәтіннен жоғалады.
&таңбасын браузер арнайы таңбаның басы деп оқуға тырысады. Жақсы жағдайда бет оғаш көрінеді, нашарында — оған бөтен код түседі.html.escapeбұл таңбаларды қауіпсіз тізбекке айналдырады. - Бет бір файл болып қалуы үшін. Оны хатпен және мессенджермен жібереді, ал қасында жатқан сурет бұл кезде жоғалады. Төлемі — өлшем: base64 суреттің салмағына шамамен үштен бір қосады.
- Бос ұяшық «толтыруды ұмытқан» болып оқылады, сызықша — «дерек жоқ» болып. Бұл — әртүрлі тұжырым, әрі екіншісі — шындық.
Жаттығуға
html.escape<,>,&таңбаларын ауыстырады, алquote=True(әдепкісі) кезінде тырнақшаларды да. Жол дәл сол ауыстырулардың есебінен ұзарады.
<b>Ресей</b> & Ко "оңтүстік"
<b>Ресей</b> & Ко "оңтүстік"
ұзындығы: 28 | кейін: 54
html.escape(cell). Әр мән бөлек экрандалады, дайын жол тұтас емес — әйтпесе өзіңіздің тегтеріңіз де экрандалады.
import html
cells = ["тамақ", "12 & 13", "<b>байланыс</b>"]
row = "<tr>" + "".join(f"<td>{html.escape(cell)}</td>" for cell in cells) + "</tr>"
print(row)
print("жолдағы тег:", row.count("<td>"), "| деректен келген белгілеу:", "<b>" in row)
<tr><td>тамақ</td><td>12 & 13</td><td><b>байланыс</b></td></tr>
жолдағы тег: 3 | деректен келген белгілеу: False
- Сол нәрсе: f-жолдың ішіндегі
html.escape(cell). «Деректен келген белгілеу» тексерісіFalseдеп жауап береді — демек, ұяшықтардағы тегтер бетке түспеген.
import html
cells = ["тамақ", "12 & 13", "<b>байланыс</b>"]
row = "<tr>" + "".join(f"<td>{html.escape(cell)}</td>" for cell in cells) + "</tr>"
print(row)
print("жолдағы тег:", row.count("<td>"), "| деректен келген белгілеу:", "<b>" in row)
<tr><td>тамақ</td><td>12 & 13</td><td><b>байланыс</b></td></tr>
жолдағы тег: 3 | деректен келген белгілеу: False
Тапсырмаға
Тексеру тағы да ниетті емес, нәтижені сұрайды: page.count("<tr>"), беттің мәтінінен күн мен дереккөзді іздеу. Мұндай тексеруді есеп кодында қалдырған жөн — ол ұмытылған дереккөзді де, кездейсоқ жоғалған кесте жолын да ұстайды.
Ұяшықтағы сызықша экрандауға дейін және одан тыс қойылады: бұл — деректің емес, сіздің өз таңбаңыз. Сондықтан cell функциясында алдымен жетіспейтін мән тексеріледі, содан кейін ғана қалғаны экрандалады.
Дереккөздер
- html модулі —
escapeжәне оның нақты нені ауыстыратыны. - string.Template — стандартты кітапханадағы қарапайым үлгілер.
- Jinja2 —
Templateтарылғанда алынатын үлгілер.
Пікірлер (0)
Пікір қалдыру үшін кіріңіз →
Әзірге пікір жоқ. Бірінші болыңыз.