/
grenki70
/
practice
Обзор
Документация
Войти
/
grenki70
/
practice
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
scraper.py
240 строк
8 KB
grenki70
Initial commit: детектор рукописных дат в PDF
22 июн 2026, 22:23
22 июн 2026, 22:23
f4901f0
Код
Авторство
О чём код?
import os import re import time import hashlib from urllib.parse import urljoin, urlparse from urllib.robotparser import RobotFileParser from collections import deque import requests from bs4 import BeautifulSoup SEED_URLS = [ "https://sfu-kras.ru/sveden/document/", "https://www.dvfu.ru/sveden/document/", "https://ncfu.ru/sveden/document/", "https://narfu.ru/sveden/document/", "https://kantiana.ru/sveden/document/", "https://s-vfu.ru/sveden/document/", "https://cfuv.ru/sveden/document/", "https://ssau.ru/sveden/document/", "https://nstu.ru/sveden/document/", "https://www.ugatu.su/sveden/document/", "https://donstu.ru/sveden/document/", "https://moscowpolytech.ru/sveden/document/", "https://www.sut.ru/sveden/document/", "https://www.tulsu.ru/sveden/document/", "https://www.sstu.ru/sveden/document/", "https://madi.ru/sveden/document/", "https://pstu.ru/sveden/document/", "https://samgtu.ru/sveden/document/", "https://vstu.ru/sveden/document/", "https://bstu.ru/sveden/document/", "https://istu.edu/sveden/document/", "https://kstu.ru/sveden/document/", "https://knastu.ru/sveden/document/", "https://stu.ru/sveden/document/", "https://omgtu.ru/sveden/document/", "https://ugtu.net/sveden/document/", "https://ulstu.ru/sveden/document/", "https://www.rsuh.ru/sveden/document/", "https://guu.ru/sveden/document/", "https://pgu.ru/sveden/document/", "https://www.bgu.ru/sveden/document/", "https://rgpu.ru/sveden/document/", "https://sgu.ru/sveden/document/", "https://chelsu.ru/sveden/document/", "https://bashedu.ru/sveden/document/", "https://udsu.ru/sveden/document/", "https://omsu.ru/sveden/document/", "https://osu.ru/sveden/document/", "https://vlsu.ru/sveden/document/", "https://volsu.ru/sveden/document/", "https://yarsu.ru/sveden/document/", "https://altgu.ru/sveden/document/", "https://amursu.ru/sveden/document/", "https://asu.ru/sveden/document/", "https://dgu.ru/sveden/document/", "https://ivsu.ru/sveden/document/", "https://kemsu.ru/sveden/document/", "https://kgsu.ru/sveden/document/", "https://khsu.ru/sveden/document/", "https://klgtu.ru/sveden/document/", "https://krsu.ru/sveden/document/", "https://kubsu.ru/sveden/document/", "https://kursksu.ru/sveden/document/", "https://lstu.ru/sveden/document/", "https://mrsu.ru/sveden/document/", "https://chuvsu.ru/sveden/document/", "https://ulsu.ru/sveden/document/", "https://rsmu.ru/sveden/document/", "https://szgmu.ru/sveden/document/", "https://pspgmu.ru/sveden/document/", "https://rostgmu.ru/sveden/document/", "https://rgup.ru/sveden/document/", "https://msal.ru/sveden/document/", "https://mgpu.ru/sveden/document/", "https://mpgu.su/sveden/document/", "https://stankin.ru/sveden/document/", "https://mirea.ru/sveden/document/", "https://mtci.ru/sveden/document/", "https://rguts.ru/sveden/document/", "https://rsreu.ru/sveden/document/", "https://sibstrin.ru/sveden/document/", "https://sguwt.ru/sveden/document/", "https://usfeu.ru/sveden/document/", ] KEYWORDS = ["договор", "приказ", "регламент", "положение", "устав", "соглашение", "акт"] OUTPUT_DIR = "downloaded_pdfs" MAX_PAGES_PER_SITE = 200 MAX_PDFS = 3000 STALL_TIMEOUT = 300 REQUEST_DELAY = 1.0 TIMEOUT = 20 USER_AGENT = "research-dataset-bot/1.0" HEADERS = { "User-Agent": f"Mozilla/5.0 (compatible; {USER_AGENT})" } os.makedirs(OUTPUT_DIR, exist_ok=True) _robots_cache: dict[str, RobotFileParser] = {} def get_robots(url: str) -> RobotFileParser: domain = urlparse(url).netloc if domain in _robots_cache: return _robots_cache[domain] rp = RobotFileParser() robots_url = urljoin(f"{urlparse(url).scheme}://{domain}", "/robots.txt") try: r = requests.get(robots_url, headers=HEADERS, timeout=TIMEOUT) if r.status_code == 200: rp.parse(r.text.splitlines()) else: rp.allow_all = True except Exception: rp.allow_all = True _robots_cache[domain] = rp return rp def can_fetch(url: str) -> bool: rp = get_robots(url) if getattr(rp, "allow_all", False): return True return rp.can_fetch(USER_AGENT, url) def matches_keyword(text: str) -> bool: text = text.lower() return any(kw in text for kw in KEYWORDS) def is_pdf_link(href: str) -> bool: return href.lower().split("?")[0].endswith(".pdf") def safe_filename(url: str) -> str: name = os.path.basename(urlparse(url).path) or "doc.pdf" name = re.sub(r"[^\w.\-]", "_", name) h = hashlib.md5(url.encode()).hexdigest()[:8] base, ext = os.path.splitext(name) return f"{base}_{h}{ext or '.pdf'}" def download_pdf(url: str, session: requests.Session) -> bool: path = os.path.join(OUTPUT_DIR, safe_filename(url)) if os.path.exists(path): return False try: r = session.get(url, headers=HEADERS, timeout=TIMEOUT, stream=True) ct = r.headers.get("Content-Type", "").lower() if r.status_code != 200 or "pdf" not in ct and not url.lower().endswith(".pdf"): return False with open(path, "wb") as f: for chunk in r.iter_content(8192): f.write(chunk) print(f" ⬇ {os.path.basename(path)}") return True except Exception as e: print(f" ⚠ ошибка скачивания {url}: {e}") return False def crawl_site(seed: str, session: requests.Session, pdf_counter: list): domain = urlparse(seed).netloc visited = set() queue = deque([seed]) pages_done = 0 last_pdf_time = time.time() while queue and pages_done < MAX_PAGES_PER_SITE and pdf_counter[0] < MAX_PDFS: if time.time() - last_pdf_time > STALL_TIMEOUT: print(f" ⏭ {domain}: {STALL_TIMEOUT // 60} мин без новых PDF — пропускаем") break url = queue.popleft() if url in visited: continue visited.add(url) if not can_fetch(url): continue try: r = session.get(url, headers=HEADERS, timeout=TIMEOUT) if r.status_code != 200 or "html" not in r.headers.get("Content-Type", "").lower(): continue except Exception: continue pages_done += 1 soup = BeautifulSoup(r.text, "lxml") for a in soup.find_all("a", href=True): href = a["href"] link_text = a.get_text(" ", strip=True) full_url = urljoin(url, href) if is_pdf_link(full_url) and (matches_keyword(link_text) or matches_keyword(href)): if not can_fetch(full_url): continue if download_pdf(full_url, session): pdf_counter[0] += 1 last_pdf_time = time.time() if pdf_counter[0] >= MAX_PDFS: return time.sleep(REQUEST_DELAY) elif urlparse(full_url).netloc == domain and full_url not in visited: if not is_pdf_link(full_url): queue.append(full_url) time.sleep(REQUEST_DELAY) print(f" ✓ {domain}: обойдено {pages_done} страниц") if __name__ == "__main__": if not SEED_URLS: print("⚠ Заполни SEED_URLS — список стартовых страниц вузов.") raise SystemExit(1) session = requests.Session() pdf_counter = [0] for seed in SEED_URLS: print(f"\n🌐 Обходим: {seed}") crawl_site(seed, session, pdf_counter) if pdf_counter[0] >= MAX_PDFS: break print("-" * 40) print(f"🎯 Готово! Скачано PDF: {pdf_counter[0]}") print(f"📁 Папка: {os.path.abspath(OUTPUT_DIR)}") print("👉 Дальше: pdf_to_images.py превратит их в сканы для модели.")