Ma'lumot yig'ish: PDF va sayt
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
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
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:
scrape_page("https://sayt.uz/tariflar", css_selector="div.content")
Bu avtomatik topishdan ancha aniq.
normalizer.py
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
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(...)—trafilaturakutubxonasi 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.