Asosiy qismga o'tish

Ma'lumot yig'ish: PDF va sayt

7-bob 5 kod bloki 676 so'z

RAG ning sifati ma'lumot sifatiga bog'liq. Yomon matn — yomon javob. Hech qanday model buni tuzata olmaydi.

Bu bobda uchta fayl. Ularni yozib o'tirmaysiz — tayyor oling va tushunib chiqing.

pdf_parser.py

python18 qator
import fitz          # PyMuPDF


def parse_pdf_bytes(file_bytes: bytes, filename: str = "document.pdf") -> dict:
    doc = fitz.open(stream=file_bytes, filetype="pdf")
    sahifalar = []

    for i, sahifa in enumerate(doc, 1):
        matn = sahifa.get_text("text").strip()
        if matn:
            sahifalar.append({"page": i, "text": matn})

    doc.close()
    return {
        "title": filename.rsplit(".", 1)[0] or "PDF hujjat",
        "pages": sahifalar,
        "full_text": "\n\n".join(s["text"] for s in sahifalar),
    }

stream=content nega? Faylni diskka saqlamasdan, to'g'ridan-to'g'ri xotiradan o'qiymiz. Foydalanuvchi PDF yuklaganda uni /tmp ga yozish — keraksiz xavf va keraksiz tozalash ishi.

Sahifa raqamini saqlaymiz. Keyin javobda "12-sahifada yozilgan" deb ko'rsata olamiz. Bu ishonchni oshiradi.

Muhim cheklov: bu faqat matnli PDF bilan ishlaydi. Skanerlangan PDF — bu rasm. Undan matn chiqmaydi. Buning uchun OCR kerak (pytesseract). Talabalar buni albatta uchratadi, oldindan ayting.

web_scraper.py

python22 qator
import httpx
from bs4 import BeautifulSoup


def scrape_page(url: str, css_selector: str | None = None) -> dict:
    javob = httpx.get(url, timeout=30.0, follow_redirects=True,
                      headers={"User-Agent": "Mozilla/5.0 (RagDemo)"})
    javob.raise_for_status()

    sup = BeautifulSoup(javob.text, "html.parser")

    for teg in sup(["script", "style", "nav", "footer", "header"]):
        teg.decompose()

    hudud = sup.select_one(css_selector) if css_selector else (
        sup.find("main") or sup.find("article") or sup.body)

    return {
        "title": sup.title.get_text(strip=True) if sup.title else url,
        "text": hudud.get_text(separator="\n", strip=True) if hudud else "",
        "url": url,
    }

decompose() — eng muhim qism. Menyu, footer, reklama har bir sahifada takrorlanadi. Ularni olib tashlamasangiz, bazangiz "Bosh sahifa | Biz haqimizda | Aloqa" degan chunk'lar bilan to'ladi va qidiruv shularni topaveradi.

css_selector — agar sayt tuzilishini bilsangiz, aniq ko'rsating:

python1 qator
scrape_page("https://sayt.uz/tariflar", css_selector="div.content")

Bu avtomatik topishdan ancha aniq.

normalizer.py

python9 qator
import re


def normalize(matn: str) -> str:
    matn = matn.replace(" ", " ")        # NBSP -> oddiy probel
    matn = re.sub(r"[ \t]+", " ", matn)       # ko'p probel -> bitta
    matn = re.sub(r"\n{3,}", "\n\n", matn)    # ko'p bo'sh qator -> ikkita
    matn = re.sub(r"-\n(\w)", r"\1", matn)    # ko'chirilgan so'zni birlashtir
    return matn.strip()

Har bir qator haqiqiy muammoni hal qiladi:

NBSP () — ko'zga oddiy probel bo'lib ko'rinadi, lekin boshqa belgi. Qidiruvda so'z topilmay qoladi.

-\n — PDF'da so'z satr oxirida bo'linadi: "tari-\nflar". Tuzatmasangiz "tari" va "flar" degan ikkita ma'nosiz so'z qoladi.

Sinab ko'ring

python6 qator
from scraper.web_scraper import scrape_page
from scraper.normalizer import normalize

n = scrape_page("https://kun.uz")
print(n["title"])
print(normalize(n["text"])[:300])

Matnni o'z ko'zingiz bilan ko'ring. Bu bosqichni o'tkazib yubormang. Agar bu yerda axlat bo'lsa — keyingi hamma narsa axlat.

Haqiqiy loyihadagi qo'shimchalar

scraper/web_scraper.py da yuqoridagidan ko'ra ko'proq narsa bor:

  • scrape_site(...) — bir nechta sahifani ketma-ket yig'adi
  • _extract_with_trafilatura(...) — trafilatura kutubxonasi bilan asosiy matnni ajratadi (selector topilmaganda zaxira yo'l)
  • _clean_html(...) — keraksiz teglarni olib tashlaydi

Kitobda soddalashtirilgan variant ko'rsatildi — g'oya bir xil. Haqiqiy faylni ochib solishtiring.


Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar