O'z ma'lumotingni yig'ish: scraping va API
Sahna: hech kimda yo'q ma'lumot
Shu paytgacha uy_ijara.csv tayyor holda turgan edi. Haqiqiy loyihada esa birinchi savol boshqa: ma'lumot qayerdan keladi?
Kaggle da hamma narsa bor degan tasavvur noto'g'ri. Toshkent ijara bozori, o'zbek tilidagi mijoz murojaatlari, mahalliy do'kon savdosi — bularning hech biri Kaggle da yo'q. Agar siz shu ma'lumotni yig'a olsangiz, sizda raqobatchida bo'lmagan narsa paydo bo'ladi.
Bu bob shuning yo'lini ko'rsatadi. Lekin birinchi navbatda qoidalar.
Qoidalar — koddan OLDIN
Bu bo'limni o'tkazib yuborish mumkin emas
Veb-dan ma'lumot olish texnik masala emas, huquqiy va axloqiy masala. Quyidagilar tavsiya emas, shart:
robots.txtni o'qing.sayt.uz/robots.txt— sayt egasi nimaga ruxsat berganini yozadi.Disallowbo'lsa, u yerga bormaysiz.- Foydalanish shartlarini (ToS) o'qing. Ko'p saytlar avtomatik yig'ishni to'g'ridan-to'g'ri taqiqlaydi. Taqiq bor bo'lsa — to'xtaysiz yoki yozma ruxsat olasiz.
- Sekin so'rang. Soniyada 1 so'rov — xushmuomilalik chegarasi. Parallel 50 so'rov — bu hujum, va sayt egasi uchun shunday ko'rinadi.
- O'zingizni tanitib qo'ying.
User-Agentda loyiha nomi va aloqa usulingiz bo'lsin. Yashirinish niyat yomonligini ko'rsatadi. - Shaxsiy ma'lumotni yig'MANG. Telefon, ism, manzil, email — bular qonun bilan qo'riqlanadi (O'zbekistonda ham: "Shaxsga doir ma'lumotlar to'g'risida" qonuni). Modelingizga telefon raqami kerak emas. Yig'masangiz — yo'qotmaysiz, sizdan o'g'irlanmaydi, sizni sudga bermaydi.
- Avtorizatsiya ortidagi ma'lumotga tegmang. Login kerak bo'lsa, demak u ochiq ma'lumot emas.
- API bor bo'lsa — API ishlatiladi. Scraping — oxirgi chora, birinchi emas.
Qisqa mezon: "Sayt egasi mening kodimni ko'rsa, xafa bo'ladimi?" Agar ha — qilmang.
import pandas as pd show(pd.DataFrame([ {"Yo'l": "1. Rasmiy API", "Sifati": "Eng yaxshi", "Izoh": "Struktura toza, shartlar aniq, buzilmaydi"}, {"Yo'l": "2. Ochiq ma'lumot / eksport", "Sifati": "Yaxshi", "Izoh": "stat.uz, data.gov.uz, saytning CSV eksporti"}, {"Yo'l": "3. Hamkorlik so'rash", "Sifati": "Yaxshi", "Izoh": "Ko'pincha ishlaydi — shunchaki so'rang. Bepul va qonuniy"}, {"Yo'l": "4. Scraping", "Sifati": "Oxirgi chora", "Izoh": "Sayt o'zgarsa buziladi; huquqiy javobgarlik sizda"}, ]))
| Yo'l | Sifati | Izoh |
|---|---|---|
| 1. Rasmiy API | Eng yaxshi | Struktura toza, shartlar aniq, buzilmaydi |
| 2. Ochiq ma'lumot / eksport | Yaxshi | stat.uz, data.gov.uz, saytning CSV eksporti |
| 3. Hamkorlik so'rash | Yaxshi | Ko'pincha ishlaydi — shunchaki so'rang. Bepul va qonuniy |
| 4. Scraping | Oxirgi chora | Sayt o'zgarsa buziladi; huquqiy javobgarlik sizda |
Bu bobda jonli sayt YO'Q
Kitobdagi har raqam abadiy takrorlanishi kerak. Jonli saytni qirqib olsak, ertaga sayt dizaynini o'zgartiradi va kitobdagi raqamlar yolg'onga aylanadi.
Shuning uchun datasets/fixture/ papkasida saqlangan sahifalar turadi. Ular haqiqiy e'lon saytining tuzilishini takrorlaydi — iflosliklari bilan birga.
from pathlib import Path FIX = DATA / "fixture" for p in sorted(FIX.iterdir()): print(f" {p.name:<18} {p.stat().st_size:>6} bayt") print() xom = (FIX / "elonlar_1.html").read_text(encoding="utf-8") print("Birinchi e'lon bloki qanday ko'rinadi:") bosh = xom.index('<div class="elon"') print(xom[bosh:bosh + 560])
api.json 36707 bayt
elonlar_1.html 13135 bayt
elonlar_10.html 12978 bayt
elonlar_2.html 13036 bayt
elonlar_3.html 12953 bayt
elonlar_4.html 13018 bayt
elonlar_5.html 12979 bayt
elonlar_6.html 13010 bayt
elonlar_7.html 12842 bayt
elonlar_8.html 13004 bayt
elonlar_9.html 12835 bayt
Birinchi e'lon bloki qanday ko'rinadi:
<div class="elon" data-id="101">
<h3 class="sarlavha">Olmazor — 1 xonali kvartira & balkon</h3>
<div class="narx">10 625 000 so'm</div>
<div class="tafsilot">
<span class="xona">1 xona</span>
<span class="maydon">33.1 m²</span>
<span class="tuman">Olmazor</span>
<span class="qavat">11-qavat</span>
</div>
<div class="aloqa">Tel: +998 91 328-86-89</div>
</div>
<div class="elon" data-id="102">
<h3 class="sarlavha">4 xonali kvartira, Bektemir</h3>
<div class="narx">12
HTML ni o'qish: BeautifulSoup
from bs4 import BeautifulSoup sup = BeautifulSoup(xom, "html.parser") elonlar = sup.select("div.elon") print(f"1-sahifada topilgan e'lonlar: {len(elonlar)}") print() birinchi = elonlar[0] print(f" data-id : {birinchi['data-id']}") print(f" sarlavha : {birinchi.select_one('h3.sarlavha').get_text(strip=True)}") print(f" narx : {birinchi.select_one('div.narx').get_text(strip=True)}") print(f" tuman : {birinchi.select_one('span.tuman').get_text(strip=True)}") xona = birinchi.select_one("span.xona") print(f" xona : {xona.get_text(strip=True) if xona else 'YO`Q'}")
1-sahifada topilgan e'lonlar: 30 data-id : 101 sarlavha : Olmazor — 1 xonali kvartira & balkon narx : 10 625 000 so'm tuman : Olmazor xona : 1 xona
`select_one` har doim `None` qaytarishi mumkin
Yuqoridagi if xona else qismi tasodifiy emas. Haqiqiy sahifalarda maydon yo'q bo'lishi odatiy hol.
birinchi.select_one("span.xona").get_text() deb yozsangiz, birinchi to'liq bo'lmagan e'londa AttributeError: 'NoneType' object has no attribute 'get_text' olasiz va 3 soatlik yig'uv to'xtaydi.
Qoida: har select_one natijasi tekshiriladi. Bu paranoya emas — 4-bobdagi "tashqi ma'lumotga ishonmaslik" qoidasining shu yerdagi shakli.
Yig'uvchini yozamiz
Endi haqiqiy fayl. U ish/yiguv/ papkasida qoladi va siz uni keyin ham ishlatasiz.
shu faylni yaratingyiguv/qoidalar.py
"""Yig'uvdan OLDIN tekshiriladigan qoidalar.""" import urllib.parse import urllib.request import urllib.robotparser ARIZA = "MLKitobTalaba/1.0 (+o'quv loyihasi; aloqa: talaba@example.uz)" def ruxsat_bormi(url, agent=ARIZA): """robots.txt ga ko'ra shu URL ni olish mumkinmi. Diqqat: robots.txt ruxsat bergani ToS ruxsat berganini BILDIRMAYDI. Shartlarni odam o'qishi kerak, kod emas. """ qism = urllib.parse.urlparse(url) rp = urllib.robotparser.RobotFileParser() rp.set_url(f"{qism.scheme}://{qism.netloc}/robots.txt") try: rp.read() except Exception: return False # robots.txt o'qilmasa — RUXSAT YO'Q deb hisoblaymiz return rp.can_fetch(agent, url) SHAXSIY = {"tel", "telefon", "phone", "ism", "name", "email", "manzil", "address"} def shaxsiyni_tashla(yozuv): """Shaxsiy maydonlarni yozuvdan olib tashlaydi. Yig'uvning OXIRIDA emas, BOSHIDA chaqiriladi: diskka yozilmagan ma'lumot o'g'irlanmaydi. """ return {k: v for k, v in yozuv.items() if k.lower() not in SHAXSIY}
shu faylni yaratingyiguv/olish.py
"""Sekin, kechiradigan va keshlaydigan yuklab oluvchi.""" import hashlib import time import urllib.request from pathlib import Path KESH = Path("kesh") KECHIKISH = 1.0 # soniyada bitta so'rov URINISH = 3 def _kesh_yoli(url): return KESH / (hashlib.sha256(url.encode()).hexdigest()[:16] + ".html") def ol(url, agent="MLKitobTalaba/1.0", majburlab=False): """URL matnini qaytaradi. Bir marta yuklab olinadi, keyin keshdan. Kesh — xushmuomilalikning asosiy qismi: kodni 50 marta ishga tushirsangiz ham sayt bitta so'rov oladi. """ KESH.mkdir(exist_ok=True) yol = _kesh_yoli(url) if yol.exists() and not majburlab: return yol.read_text(encoding="utf-8"), "kesh" oxirgi_xato = None for urinish in range(1, URINISH + 1): try: soro = urllib.request.Request(url, headers={"User-Agent": agent}) with urllib.request.urlopen(soro, timeout=20) as javob: matn = javob.read().decode("utf-8", errors="replace") yol.write_text(matn, encoding="utf-8") time.sleep(KECHIKISH) return matn, "tarmoq" except Exception as x: oxirgi_xato = x time.sleep(KECHIKISH * 2 ** urinish) # eksponensial kutish raise RuntimeError(f"{URINISH} urinishdan keyin olinmadi: {url}") from oxirgi_xato
shu faylni yaratingyiguv/tahlil.py
"""HTML -> yozuvlar. Hech qanday tozalash YO'Q — xom holda saqlanadi.""" from bs4 import BeautifulSoup from .qoidalar import shaxsiyni_tashla def matn(blok, selektor): """Yo'q bo'lsa None qaytaradi — AttributeError emas.""" topildi = blok.select_one(selektor) return topildi.get_text(strip=True) if topildi else None def sahifani_tahlil(html_matn): sup = BeautifulSoup(html_matn, "html.parser") natija = [] for blok in sup.select("div.elon"): yozuv = { "manba_id": blok.get("data-id"), "sarlavha": matn(blok, "h3.sarlavha"), "narx_xom": matn(blok, "div.narx"), "xona_xom": matn(blok, "span.xona"), "maydon_xom": matn(blok, "span.maydon"), "tuman": matn(blok, "span.tuman"), "qavat_xom": matn(blok, "span.qavat"), "tel": matn(blok, "div.aloqa"), # yig'iladi, lekin SAQLANMAYDI } natija.append(shaxsiyni_tashla(yozuv)) return natija
import sys sys.path.insert(0, str(ISH)) from yiguv.tahlil import sahifani_tahlil sahifalar = sorted(FIX.glob("elonlar_*.html")) yozuvlar = [] for sah_yoli in sahifalar: bu = sahifani_tahlil(sah_yoli.read_text(encoding="utf-8")) yozuvlar += bu if sah_yoli is sahifalar[0] or sah_yoli is sahifalar[-1]: print(f" {sah_yoli.name} -> {len(bu)} yozuv") print(f" ... jami {len(sahifalar)} sahifa o'qildi") xom_df = pd.DataFrame(yozuvlar) print(f"\nJami: {len(xom_df)} yozuv, {len(xom_df.columns)} ustun") print(f"Ustunlar: {list(xom_df.columns)}") print() print(f"Telefon ustuni bormi? {'tel' in xom_df.columns} <- shaxsiyni_tashla() ishladi") show(xom_df.head(6))
elonlar_1.html -> 30 yozuv elonlar_9.html -> 30 yozuv ... jami 10 sahifa o'qildi Jami: 300 yozuv, 7 ustun Ustunlar: ['manba_id', 'sarlavha', 'narx_xom', 'xona_xom', 'maydon_xom', 'tuman', 'qavat_xom'] Telefon ustuni bormi? False <- shaxsiyni_tashla() ishladi
| manba_id | sarlavha | narx_xom | xona_xom | maydon_xom | tuman | qavat_xom |
|---|---|---|---|---|---|---|
| 101 | Olmazor — 1 xonali kvartira & balkon | 10 625 000 so'm | 1 xona | 33.1 m² | Olmazor | 11-qavat |
| 102 | 4 xonali kvartira, Bektemir | 12 500 000 so'm | 4 xona | 81.3 m² | Bektemir | 2-qavat |
| 103 | 2 xonali kvartira, Sergeli | 500 у.е. | 2 xona | 45.5 m² | Sergeli | 7-qavat |
| 104 | Yakkasaroy — 5 xonali kvartira & balkon | 500 у.е. | 5 xona | 94.5 m² | Yakkasaroy | 6-qavat |
| 105 | Bektemir — 5 xonali kvartira & balkon | 10 625 000 so'm | 5 xona | 102.8 m² | Bektemir | 5-qavat |
| 106 | 1 xonali, remont qilingan | Kelishilgan | 1 xona | 37.8 m² | Chilonzor | 8-qavat |
3-sahifa buzilgan HTML — parser to'xtadimi?
buzilgan = (FIX / "elonlar_3.html").read_text(encoding="utf-8") print(f"3-sahifada yopilmagan <div> bor: {buzilgan.count('<div')} ochildi, {buzilgan.count('</div>')} yopildi") print(f"Shunga qaramay topilgan e'lon: {len(sahifani_tahlil(buzilgan))}") print() print("`html.parser` buzilgan HTML ni kechiradi — brauzer kabi.") print("Shuning uchun yig'uvda XML parser (`xml`, `lxml-xml`) ishlatilmaydi:") print("u qat'iy va birinchi xatoda to'xtaydi.")
3-sahifada yopilmagan <div> bor: 122 ochildi, 121 yopildi Shunga qaramay topilgan e'lon: 30 `html.parser` buzilgan HTML ni kechiradi — brauzer kabi. Shuning uchun yig'uvda XML parser (`xml`, `lxml-xml`) ishlatilmaydi: u qat'iy va birinchi xatoda to'xtaydi.
Yig'ilgan ma'lumot qanchalik iflos
jadval = [] for ustun in ["sarlavha", "narx_xom", "xona_xom", "maydon_xom", "tuman", "qavat_xom"]: yoq = int(xom_df[ustun].isna().sum()) jadval.append({"ustun": ustun, "bo'sh": yoq, "bo'sh %": f"{yoq / len(xom_df):.1%}", "noyob qiymat": int(xom_df[ustun].nunique())}) show(pd.DataFrame(jadval))
| ustun | bo'sh | bo'sh % | noyob qiymat |
|---|---|---|---|
| sarlavha | 0 | 0.0% | 98 |
| narx_xom | 0 | 0.0% | 21 |
| xona_xom | 26 | 8.7% | 5 |
| maydon_xom | 35 | 11.7% | 194 |
| tuman | 0 | 0.0% | 8 |
| qavat_xom | 0 | 0.0% | 12 |
print("Narx ustuni — nechta xil format?") for n in xom_df["narx_xom"].dropna().unique()[:8]: print(f" {n!r}") print() usd = xom_df["narx_xom"].str.contains("у.е.", na=False).sum() som = xom_df["narx_xom"].str.contains("so'm", na=False).sum() kel = xom_df["narx_xom"].str.contains("Kelishilgan", na=False).sum() print(f" у.е. ko'rinishida : {usd}") print(f" so'm ko'rinishida : {som}") print(f" 'Kelishilgan' : {kel} <- raqam YO'Q, lekin qator bor") print() print("Uchta format uchta xil qayta ishlash talab qiladi. Bu — 37-bobning ishi.")
Narx ustuni — nechta xil format? "10 625 000 so'm" "12 500 000 so'm" '500\xa0у.е.' 'Kelishilgan' '450\xa0у.е.' "8 750 000 so'm" '700\xa0у.е.' '400\xa0у.е.' у.е. ko'rinishida : 173 so'm ko'rinishida : 81 'Kelishilgan' : 46 <- raqam YO'Q, lekin qator bor Uchta format uchta xil qayta ishlash talab qiladi. Bu — 37-bobning ishi.
variantlar = [ ("manba_id (sayt bergan id)", ["manba_id"]), ("sarlavha + tuman + maydon + narx", ["sarlavha", "tuman", "maydon_xom", "narx_xom"]), ("tuman + maydon + xona", ["tuman", "maydon_xom", "xona_xom"]), ] natijalar = [] for nom, kalit in variantlar: qoldi = len(xom_df.drop_duplicates(subset=kalit)) natijalar.append({"dublikat kaliti": nom, "noyob qoldi": qoldi, "olib tashlanadi": len(xom_df) - qoldi}) show(pd.DataFrame(natijalar)) print(f"Jami yozuv: {len(xom_df)}")
Jami yozuv: 300
| dublikat kaliti | noyob qoldi | olib tashlanadi |
|---|---|---|
| manba_id (sayt bergan id) | 300 | 0 |
| sarlavha + tuman + maydon + narx | 291 | 9 |
| tuman + maydon + xona | 245 | 55 |
Uchta kalit, uchta butunlay boshqa javob. Qaysi biri to'g'ri?
takrorlangan = xom_df["maydon_xom"].value_counts() ortiqcha = int((takrorlangan[takrorlangan > 1] - 1).sum()) print(f"Maydon qiymati bir martadan ko'p uchragan: {int((takrorlangan > 1).sum())} ta") print(f"Haqiqiy ortiqcha yozuv soni : {ortiqcha}") print() print("Maydon — uyning FIZIK xossasi. Ikki e'londa aynan bir xil maydon bo'lsa,") print("bu deyarli har doim bitta uy ikki marta e'lon qilingani degani.")
Maydon qiymati bir martadan ko'p uchragan: 48 ta Haqiqiy ortiqcha yozuv soni : 71 Maydon — uyning FIZIK xossasi. Ikki e'londa aynan bir xil maydon bo'lsa, bu deyarli har doim bitta uy ikki marta e'lon qilingani degani.
Dublikat kalitiga sarlavha yoki narxni qo'shish — xato
Yuqoridagi jadval mening birinchi tavsiyamni rad etdi. Men bu bo'limga "dublikatni mazmun bo'yicha aniqlang: tuman + maydon + narx + sarlavha" deb yozgandim. O'lchov ko'rsatdi: shu kalit haqiqiy dublikatlarning ko'pini topmaydi.
Sababi: bitta uy qayta e'lon qilinganda sayt egasi sarlavhani boshqacha yozadi ("Shoshilinch!", "remont qilingan"), narxni esa boshqa formatda ko'rsatadi (у.е. yoki so'm). Ya'ni sarlavha va narx — beqaror maydonlar.
tuman + maydon + xona esa uyning fizik xossalari — ular qayta e'londa ham o'zgarmaydi.
Uchta saboq:
manba_idbo'yicha dedup ma'nosiz. Hamma id noyob, hech narsa o'chmaydi, siz esa o'zingizni toza ma'lumot bilan deb hisoblaysiz. Keyin 5-bobdagi muammo keladi: bitta uy train va test ga ikkalasiga tushadi — leakage.- Dublikat kalitiga faqat BARQAROR maydonlar kiradi. Formatlanishi mumkin bo'lgan har narsa kalitdan chiqariladi.
- Shuning uchun dedup tozalashdan KEYIN qilinadi. Narxni songa aylantirgandan so'ng uni kalitga qo'shish mumkin bo'ladi. Tartib: tahlil -> normallashtirish -> dedup -> split. Bu 37-bobning ishi.
API bo'lsa — hammasi oson
import json api = json.loads((FIX / "api.json").read_text(encoding="utf-8")) print(f"API javobi kalitlari: {list(api.keys())}") print(f"Jami yozuv: {api['jami']}, sahifa hajmi: {api['sahifa_hajmi']}") print() print("Birinchi yozuv:") for k, v in api["natijalar"][0].items(): print(f" {k:<12} {v!r} ({type(v).__name__})") print() api_df = pd.DataFrame(api["natijalar"]) print(f"DataFrame: {api_df.shape}") print(f"Bo'sh qiymat: {int(api_df.isna().sum().sum())}") print(f"Tiplar to'g'ri: {dict(api_df.dtypes.astype(str))}")
API javobi kalitlari: ['jami', 'sahifa_hajmi', 'natijalar']
Jami yozuv: 300, sahifa hajmi: 10
Birinchi yozuv:
id 101 (int)
xonalar 1 (int)
tuman 'Olmazor' (str)
maydon_m2 33.1 (float)
narx_usd 850 (int)
qavat 11 (int)
DataFrame: (300, 6)
Bo'sh qiymat: 0
Tiplar to'g'ri: {'id': 'int64', 'xonalar': 'int64', 'tuman': 'object', 'maydon_m2': 'float64', 'narx_usd': 'int64', 'qavat': 'int64'}
Farqni ko'ryapsizmi: API da narx_usd — son, maydon_m2 — son. HTML da ikkisi ham matn edi, uch xil formatda.
show(pd.DataFrame([ {"Jihat": "Narx", "HTML scraping": "3 xil matn format", "API": "son, bitta birlik"}, {"Jihat": "Bo'sh maydon", "HTML scraping": "bor", "API": "yo'q"}, {"Jihat": "Dublikat", "HTML scraping": "bor", "API": "id bo'yicha toza"}, {"Jihat": "Sayt dizayni o'zgarsa", "HTML scraping": "kod buziladi", "API": "ishlaydi (versiya bor)"}, {"Jihat": "Shartlar", "HTML scraping": "noaniq", "API": "yozilgan"}, {"Jihat": "Tezlik", "HTML scraping": "sekin (HTML katta)", "API": "tez (faqat ma'lumot)"}, ])) print() print("Shuning uchun qoida: API bor bo'lsa, scraping yozishga vaqt sarflamaysiz.")
Shuning uchun qoida: API bor bo'lsa, scraping yozishga vaqt sarflamaysiz.
| Jihat | HTML scraping | API |
|---|---|---|
| Narx | 3 xil matn format | son, bitta birlik |
| Bo'sh maydon | bor | yo'q |
| Dublikat | bor | id bo'yicha toza |
| Sayt dizayni o'zgarsa | kod buziladi | ishlaydi (versiya bor) |
| Shartlar | noaniq | yozilgan |
| Tezlik | sekin (HTML katta) | tez (faqat ma'lumot) |
Sahifalash va so'rov chegarasi
shu faylni yaratingyiguv/api_mijoz.py
"""Sahifalangan API dan hamma yozuvni olish — chegarani hurmat qilib.""" import json import time from pathlib import Path def hamma_sahifa(ol_funksiya, sahifa_hajmi=10, maks_sahifa=100, kechikish=1.0): """`ol_funksiya(ofset, limit)` -> dict. Hamma yozuvni yig'adi. maks_sahifa — himoya: API 'keyingi sahifa bor' deb cheksiz aytsa, kod abadiy aylanmasligi kerak. """ hammasi, ofset = [], 0 for _ in range(maks_sahifa): javob = ol_funksiya(ofset, sahifa_hajmi) bolak = javob.get("natijalar", []) if not bolak: break hammasi += bolak ofset += len(bolak) if ofset >= javob.get("jami", 0): break time.sleep(kechikish) return hammasi
from yiguv.api_mijoz import hamma_sahifa sorovlar = {"soni": 0} def mahalliy_api(ofset, limit): """Haqiqiy API ni taqlid qiladi — tarmoq yo'q, sahifalash bor.""" sorovlar["soni"] += 1 return {"jami": api["jami"], "natijalar": api["natijalar"][ofset:ofset + limit]} olingan = hamma_sahifa(mahalliy_api, sahifa_hajmi=10, kechikish=0) print(f"So'rov soni : {sorovlar['soni']}") print(f"Olingan yozuv : {len(olingan)}") print(f"API dagi jami : {api['jami']}") print(f"Hammasi olindi : {len(olingan) == api['jami']}")
So'rov soni : 30 Olingan yozuv : 300 API dagi jami : 300 Hammasi olindi : True
Xomni saqlang, keyin tahlil qiling
Eng ko'p uchraydigan yig'uv xatosi: HTML ni yuklab olib, darhol tahlil qilib, faqat natijani saqlash.
Ikki hafta o'tib "menga qavat ham kerak edi" deb tushunasiz — va hammasini qaytadan yuklab olishga to'g'ri keladi. Sayt esa allaqachon o'zgargan bo'ladi.
To'g'ri tartib:
1. Xom HTML ni diskka yoz (kesh/ papkasi — yuqoridagi olish.py shuni qiladi)
2. Tahlilni alohida ishga tushir
3. Yangi maydon kerak bo'lsa — keshdan qayta tahlil qil, tarmoqqa chiqmasdan
Xom ma'lumot — arzon. Qayta yig'ish — qimmat va ba'zan imkonsiz.
Natijani saqlaymiz
XOM = ISH / "xom" XOM.mkdir(exist_ok=True) yol = XOM / "elonlar_xom.csv" xom_df.to_csv(yol, index=False) print(f"Saqlandi: ish/xom/{yol.name}") print(f" {len(xom_df)} qator, {len(xom_df.columns)} ustun") print(f" hajmi: {yol.stat().st_size} bayt") print() print("Diqqat: bu fayl ATAYLAB iflos. Tozalash — 37-bobning vazifasi.") print("Yig'uv va tozalash alohida qadamlar: yig'uv xomni oladi, tozalash qaror qabul qiladi.")
Saqlandi: ish/xom/elonlar_xom.csv 300 qator, 7 ustun hajmi: 24469 bayt Diqqat: bu fayl ATAYLAB iflos. Tozalash — 37-bobning vazifasi. Yig'uv va tozalash alohida qadamlar: yig'uv xomni oladi, tozalash qaror qabul qiladi.
Protokolga ulanish
0-bobdagi 12 qadam protokoli "ma'lumotni tushun" dan boshlanadi. Bu bob undan oldingi qadam: ma'lumotni yaratish.
show(pd.DataFrame([ {"Qadam": "0. Qoidalarni tekshir", "Natija": "robots.txt, ToS, shaxsiy ma'lumot qarori"}, {"Qadam": "1. Manbani tanla", "Natija": "API > eksport > hamkorlik > scraping"}, {"Qadam": "2. Xomni yig'ib saqla", "Natija": "kesh/ papkasi — bir marta yuklanadi"}, {"Qadam": "3. Tahlil qil", "Natija": "xom/elonlar_xom.csv — tozalanmagan"}, {"Qadam": "4. Dataset qur", "Natija": "37-bob: tozalash, dedup, split"}, ]))
| Qadam | Natija |
|---|---|
| 0. Qoidalarni tekshir | robots.txt, ToS, shaxsiy ma'lumot qarori |
| 1. Manbani tanla | API > eksport > hamkorlik > scraping |
| 2. Xomni yig'ib saqla | kesh/ papkasi — bir marta yuklanadi |
| 3. Tahlil qil | xom/elonlar_xom.csv — tozalanmagan |
| 4. Dataset qur | 37-bob: tozalash, dedup, split |
Bugun nima o'rgandik
show(pd.DataFrame([ {"Tushuncha": "Qoidalar koddan oldin", "Bitta jumla": "robots.txt, ToS, sekin so'rash, shaxsiyni yig'maslik"}, {"Tushuncha": "Manba tartibi", "Bitta jumla": "API > eksport > hamkorlik so'rash > scraping"}, {"Tushuncha": "select_one None qaytaradi", "Bitta jumla": "Har natija tekshiriladi, aks holda yig'uv to'xtaydi"}, {"Tushuncha": "html.parser", "Bitta jumla": "Buzilgan HTML ni kechiradi, XML parser yo'q"}, {"Tushuncha": "Kesh", "Bitta jumla": "Bir marta yuklanadi — xushmuomilalik va tezlik"}, {"Tushuncha": "ID noyobligi aldaydi", "Bitta jumla": "O'lchandi: id boshqa, mazmun bir xil"}, {"Tushuncha": "Shaxsiy ma'lumot", "Bitta jumla": "Yozilmagan ma'lumot o'g'irlanmaydi"}, {"Tushuncha": "Xomni saqla", "Bitta jumla": "Qayta yig'ish qimmat, disk arzon"}, ]))
| Tushuncha | Bitta jumla |
|---|---|
| Qoidalar koddan oldin | robots.txt, ToS, sekin so'rash, shaxsiyni yig'maslik |
| Manba tartibi | API > eksport > hamkorlik so'rash > scraping |
| select_one None qaytaradi | Har natija tekshiriladi, aks holda yig'uv to'xtaydi |
| html.parser | Buzilgan HTML ni kechiradi, XML parser yo'q |
| Kesh | Bir marta yuklanadi — xushmuomilalik va tezlik |
| ID noyobligi aldaydi | O'lchandi: id boshqa, mazmun bir xil |
| Shaxsiy ma'lumot | Yozilmagan ma'lumot o'g'irlanmaydi |
| Xomni saqla | Qayta yig'ish qimmat, disk arzon |
Keyingi bobda shu iflos faylni dataset ga aylantiramiz: narx formatlarini birlashtiramiz, dublikatni olib tashlaymiz va split ni to'g'ri qilamiz.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.