Asosiy qismga o'tish

O'z ma'lumotingni yig'ish: scraping va API

37-bob 19 kod bloki 4006 so'z

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:

  1. robots.txt ni o'qing. sayt.uz/robots.txt — sayt egasi nimaga ruxsat berganini yozadi. Disallow bo'lsa, u yerga bormaysiz.
  2. 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.
  3. Sekin so'rang. Soniyada 1 so'rov — xushmuomilalik chegarasi. Parallel 50 so'rov — bu hujum, va sayt egasi uchun shunday ko'rinadi.
  4. O'zingizni tanitib qo'ying. User-Agent da loyiha nomi va aloqa usulingiz bo'lsin. Yashirinish niyat yomonligini ko'rsatadi.
  5. 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.
  6. Avtorizatsiya ortidagi ma'lumotga tegmang. Login kerak bo'lsa, demak u ochiq ma'lumot emas.
  7. 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.

python11 qator
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'lSifatiIzoh
1. Rasmiy APIEng yaxshiStruktura toza, shartlar aniq, buzilmaydi
2. Ochiq ma'lumot / eksportYaxshistat.uz, data.gov.uz, saytning CSV eksporti
3. Hamkorlik so'rashYaxshiKo'pincha ishlaydi — shunchaki so'rang. Bepul va qonuniy
4. ScrapingOxirgi choraSayt 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.

python9 qator
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])
Chiqish
  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 &amp; 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&sup2;</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

python13 qator
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'}")
Chiqish
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

python34 qator
"""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

python39 qator
"""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

python28 qator
"""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
python18 qator
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))
Chiqish
  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_idsarlavhanarx_xomxona_xommaydon_xomtumanqavat_xom
101Olmazor — 1 xonali kvartira & balkon10 625 000 so'm1 xona33.1 m²Olmazor11-qavat
1024 xonali kvartira, Bektemir12 500 000 so'm4 xona81.3 m²Bektemir2-qavat
1032 xonali kvartira, Sergeli500 у.е.2 xona45.5 m²Sergeli7-qavat
104Yakkasaroy — 5 xonali kvartira & balkon500 у.е.5 xona94.5 m²Yakkasaroy6-qavat
105Bektemir — 5 xonali kvartira & balkon10 625 000 so'm5 xona102.8 m²Bektemir5-qavat
1061 xonali, remont qilinganKelishilgan1 xona37.8 m²Chilonzor8-qavat

3-sahifa buzilgan HTML — parser to'xtadimi?

python7 qator
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.")
Chiqish
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

python7 qator
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))
ustunbo'shbo'sh %noyob qiymat
sarlavha00.0%98
narx_xom00.0%21
xona_xom268.7%5
maydon_xom3511.7%194
tuman00.0%8
qavat_xom00.0%12
python12 qator
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.")
Chiqish
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.
python12 qator
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)}")
Chiqish
Jami yozuv: 300
dublikat kalitinoyob qoldiolib tashlanadi
manba_id (sayt bergan id)3000
sarlavha + tuman + maydon + narx2919
tuman + maydon + xona24555

Uchta kalit, uchta butunlay boshqa javob. Qaysi biri to'g'ri?

python7 qator
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.")
Chiqish
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:

  1. manba_id bo'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.
  2. Dublikat kalitiga faqat BARQAROR maydonlar kiradi. Formatlanishi mumkin bo'lgan har narsa kalitdan chiqariladi.
  3. 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

python13 qator
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))}")
Chiqish
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.

python10 qator
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.")
Chiqish
Shuning uchun qoida: API bor bo'lsa, scraping yozishga vaqt sarflamaysiz.
JihatHTML scrapingAPI
Narx3 xil matn formatson, bitta birlik
Bo'sh maydonboryo'q
Dublikatborid bo'yicha toza
Sayt dizayni o'zgarsakod buziladiishlaydi (versiya bor)
Shartlarnoaniqyozilgan
Tezliksekin (HTML katta)tez (faqat ma'lumot)

Sahifalash va so'rov chegarasi

shu faylni yaratingyiguv/api_mijoz.py

python24 qator
"""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
python13 qator
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']}")
Chiqish
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

python10 qator
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.")
Chiqish
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.

python7 qator
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"},
]))
QadamNatija
0. Qoidalarni tekshirrobots.txt, ToS, shaxsiy ma'lumot qarori
1. Manbani tanlaAPI > eksport > hamkorlik > scraping
2. Xomni yig'ib saqlakesh/ papkasi — bir marta yuklanadi
3. Tahlil qilxom/elonlar_xom.csv — tozalanmagan
4. Dataset qur37-bob: tozalash, dedup, split

Bugun nima o'rgandik

python10 qator
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"},
]))
TushunchaBitta jumla
Qoidalar koddan oldinrobots.txt, ToS, sekin so'rash, shaxsiyni yig'maslik
Manba tartibiAPI > eksport > hamkorlik so'rash > scraping
select_one None qaytaradiHar natija tekshiriladi, aks holda yig'uv to'xtaydi
html.parserBuzilgan HTML ni kechiradi, XML parser yo'q
KeshBir marta yuklanadi — xushmuomilalik va tezlik
ID noyobligi aldaydiO'lchandi: id boshqa, mazmun bir xil
Shaxsiy ma'lumotYozilmagan ma'lumot o'g'irlanmaydi
Xomni saqlaQayta 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.

Telegramda yozish Narxlar