Files
pgz-sport/_handoff/HANDOFF_20260503_1410_SPORT_SCRAPING_PIPELINE.md
T

17 KiB
Raw Blame History

HANDOFF — Sport Scraping Pipeline za PGŽ

Datum: 03.05.2026 14:10 | Verzija: 8.0 | Autor: Damir + Claude


0. TL;DR za novu sesiju

Damir hoće brutalno efikasan multi-savez scraping pipeline za PGŽ:

  • Sve klubove (~525) iz svih saveza (HVS, HNS, HRS, HKS, HOS, HBS, HŠS, HJS, HJK, HAS, HKaratS, HTS, HSTS, HSA, HOO...)
  • Sve osobe sa svim ulogama (igrač, predsjednik, tajnik, trener, fizioterapeut, liječnik, team manager...)
  • Identifikator za svaku osobu (OIB, ili external_id ako nema)
  • Inteligentno dedup — bez 5x istog kluba u varijantama imena
  • Postojeća arhitektura — vLLM Qwen 7B + BGE-M3 embedder (NE pokretat dodatne GPU stvari)

3-strike rule aktivan. Damir hvata lažiranje = manual mode.


1. STACK — što JE i što NIJE

Active i radi

Servis Port Kako koristiti
vLLM Qwen 7B AWQ 8001 POST /v1/chat/completions OpenAI-kompatibilan, max_tokens 8192, GPU 18.2/20.5GB PUN
BGE-M3 embedder 9879 POST /api/embeddings body {"model":"bge-m3","input":["text1","text2"]} (LIST!)
Qdrant 6333 30+ collections: pgz_universe, pgz_sport_v1 (32K chunks), entities_v2, udruge_v2
PgBouncer 6432 DB rinet_v3, user rinet, pwd R1net2026!SecureDB#v7
Bridge API 9877 POST /bridge/exec X-API-KEY rinet-yS4ZnKlwUqsjk

NE koristi (broken/full)

  • F10 LoRA :8765 — server živ ali endpoint vraća 404, NE OpenAI-kompatibilan, ostavi
  • Ollama :11434panic: $HOME is not defined (env problem)
  • Anthropic API — Damirov $$, koristi za final compose, NE za batch ekstrakciju

Pravila pri korištenju vLLM

  • GPU je već PUN (Qwen 7B 18.2/20.5GB)
  • Ne pokretat 2. LLM proces ili novi embed batch
  • Qwen 7B context 8192 tokena — chunk text na ~5500 znakova (1.5K tokena)
  • response_format: {type: "json_object"} radi
  • Throughput: ~10 paralelnih requesta sa 30s avg = ~50K req/sat

2. KLJUČNI BREAKTHROUGH — HVS JWT TOKEN

Damir dao primjere https://hvs.hr/igrac/6893/ i /igrac/43303/. Network sniff otkrio:

POST https://rezultati.hvs.hr/api/?rubrika={rubrika}&id={id}
Headers:
  Content-Type: application/x-www-form-urlencoded
  Origin: https://hvs.hr
  Referer: https://hvs.hr/
Body: token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIVlMiLCJpYXQiOjE1Njg3MjI1MzEsImV4cCI6MTY1NTM0NTIyMywiYXVkIjoiSFZTIiwic3ViIjoiSFZTIiwiR2l2ZW5OYW1lIjoiSFZTIn0.iteM3Hewl0ugQVEDqPdg_7hHwRTxnSeFVg59vPH25uU

Rubrike koje rade:

  • person&id={id}{data: {fname, lname, club, date, position, image, country, games, goals, ...}, players3: {...suigrači}}
  • team&id={id}{info: {name, gender, category, logo}, utakmice: [...], stat: {A,B}}
  • persongames&limit=10&id={id} → utakmice osobe
  • sticky&id={id} → highlights

Performans: 50K player ID-eva u ~50s (parallel=20). 795 PGZ vaterpolo osoba inserted.

Implementacija: /tmp/hvs_mass.py (saved). Replicirati pattern za druge saveze.

Ovaj pattern treba pokušati za druge saveze:

  • rezultati.hbs.hr (boćanje)
  • rezultati.hks-cbf.hr (košarka)
  • rezultati.hrs.hr (rukomet — ali ima Zoraxy gateway = blokirano)
  • rezultati.hjs.hr (jedrenje)
  • rezultati.has.hr (atletika)
  • itd.

3. DAMIROV SEED LIST (najvažnije!)

3.1 Krovni / županijski savezi

Zajednica sportova PGŽ:    https://sport-pgz.hr/  (scraped, 18 godišnjaka downloaded)
Sportska zajednica Rijeke: http://rss.hr/
Nogometni savez PGŽ:       https://www.nspgz.hr/
Šahovski savez PGŽ:        https://www.sah-pgz.hr/  ← KLUB LIST: /klubovi/
Pikado savez PGŽ:          http://www.pikado-pgz.hr
Parasport Rijeka:          https://www.ssoi-rijeka.hr/
Parasport PGŽ:             https://ssoi-pgz.hr/
Školski sport PGŽ:         http://www.savezssd-pgz.hr/

3.2 Nacionalni savezi

HOO:        https://www.hoo.hr/        ← scraped 478 PDF, 1447 docs in pgz_sport
HNS:        https://hns.family/         ← /hr/sportski-djelatnici/ ima registar
HRS:        https://hrs.hr/             ← api.hrs.hr blokiran Zoraxy gateway
HKS-CBF:    https://hks-cbf.hr/         ← natjecanja.hks-cbf.hr ima statistike
HVS:        https://hvs.hr/             ← API token gore ✓ DONE
HŠS:        https://hrvatski-sahovski-savez.hr/
HSSRM:      https://www.hssrm.hr/       (sport ribolov na moru)

3.3 PGŽ klubovi s URL-om (Damir potvrdio!)

NOGOMET:
  HNK Rijeka:           https://nk-rijeka.hr/         (već u DB)
  HNK Orijent 1919:     https://nk-orijent.hr/        (već u DB ima drugi URL)
  NK Krk 1940:          https://nkkrk.hr/             (već u DB ima drugi)
  NK Opatija:           https://nkopatija.hr/         (DB ima Wiki URL — fix)
  NK Grobničan:         https://nk-grobnican.hr/      (NEW)
  NK Pomorac:           https://nk-pomorac.hr/        (NEW)
  NK Naprijed Hreljin:  https://nk-naprijed.hr/       (NEW)
  NK Turbina Tribalj:   http://www.nk-turbina-tribalj.hr/  (NEW)
  NK Klana:             https://nk-klana.hr/          (updated)
  NK Mune:              https://nk-mune.hr/           (DB ima Wiki — fix)
  NK Crikvenica:        https://nk-crikvenica.hr/     (DB ima semafor — fix)

RUKOMET:
  RK Kozala:    https://rk-kozala.hr/         (NEW)
  RK Zamet:     https://rk-zamet.hr/          (DB ima drugi)
  ŽRK Zamet:    https://zrk-zamet.hr/         (updated)
  RK Viškovo:   https://rk-viskovo.hr/        (updated)
  RK Omišalj:   https://rk-omisalj.hr/        (updated)
  RK Murvica:   https://rk-murvica.hr/        (updated)

KOŠARKA:
  KK Kvarner 2010: https://kkkvarner2010.hr/   (NEW — pazi NIJE AK Kvarner!)
  KK Škrljevo:     https://kk-skrljevo.hr/     (updated)
  KK Ri-Basket:    https://ri-basket.hr/       (NEW)
  KK Kastav:       https://kkkastav.hr/        (updated)
  KK Kraljevica:   https://kk-kraljevica.hr/   (NEW)

VATERPOLO:
  VK Primorje EB:  https://vaterpolo-primorje.hr/  ← SLUŽBENI!
  ŠD Primorje 08:  http://www.primorje08.hr        (updated)

OSTALO:
  PK Primorje:     https://pk-primorje.hr/    (plivanje, NEW)
  AK Kvarner:      https://akkvarner.hr/      (atletika, DB ima krivi Wiki — FIX)
  HAOK Rijeka:     https://haok-rijeka.hr/    (odbojka, updated)

ŠAHOVSKI:
  Lista: https://www.sah-pgz.hr/klubovi/      ← scrape ovo!
  Najveći: ŠK Rijeka, ŠK Kvarner, ŠK Crikvenica, ŠK Lošinj, ŠK Kastav, ŠK Krk, ŠK Viškovo

3.4 Lige PGZ klubova

  • Nogomet: SuperSport HNL, Prva NL, Druga NL, Treća NL Zapad
  • Košarka: Favbet Premijer liga, Prva muška liga
  • Rukomet: Paket24 Premijer, 1. HRL
  • Vaterpolo: Prvenstvo HR
  • Šah: Hrvatska šahovska liga

4. 6-fazni Scraping Pipeline (Damirov plan)

LAYER 1: Seed Sources       ← Damirov seed list gore
LAYER 2: Discovery Engine   ← crawler od seed-a, prati linkove "klub", "članice"
LAYER 3: Entity Extraction  ← LLM (vLLM Qwen 7B), regex + BS4
LAYER 4: Enrichment         ← Google query, FB/IG, WHOIS, MX, schema.org
LAYER 5: Validation         ← HTTP, SSL, MX, SMTP, robots.txt
LAYER 6: Graph DB / Dedup   ← Neo4j ili `civic.entity_connections` + fuzzy + embeddings

Stack koji JA koristim (ne Damirov ideal):

  • Python 3.12 + Playwright sync_api (asyn ne radi - DIS shadow probleme su rješene)
  • httpx (kad sync) ili urllib.request (kad lokalno)
  • BeautifulSoup4 + selectolax za HTML
  • Redis :6379 (postoji) za queue ako treba
  • vLLM Qwen 7B :8001 za LLM extract
  • BGE-M3 :9879 za embeddings

KRITIČNO: shadow dis.py bug — RJEŠENO

Bilo: /tmp/dis.py (stara skripta) shadowala Python dis module u svakoj sesiji koja cd /tmp. Crash svih Playwright skripti. Fixed: mv /tmp/dis.py /tmp/dis.py.shadow_DELETED && rm -rf /tmp/__pycache__. Ako se ponovi — provjeri prvo!


5. ENTITY DEDUP STRATEGIJA (koju Damir zahtijeva!)

Problem: u DB imamo VK Primorje, VK Primorja (typo), Vaterpolski klub Primorje Erste Bank, VK Primorje EB — sve isti klub.

Rješenje (multi-step):

  1. Naive normalizacija: lowercase, ukloni dijakritike, ukloni "klub|sportski|udruga|hnk|nk|vk|kk|rk", razdvoji riječi
  2. Levenshtein/RapidFuzz: sa pragom > 85 = isti klub
  3. BGE-M3 cosine similarity: encode klub naziv + grad + sport, threshold > 0.92
  4. LLM final adjudication: "Jesu li ovi nazivi isti klub? A: X, B: Y" — Qwen 7B yes/no
  5. Cluster i merge: ostavi onaj sa najpopunjenijim poljima (web, OIB, predsjednik), prebaci clanovi.klub_id na master

Ne pokrenu blanket — Damir je već imao 5 backup tabela:

  • klubovi_premerge_20260503 ← prije današnjih merge-ova
  • klubovi_premerge_20260503b ← prije VK merge-ova (ova sesija)
  • klubovi_premerge_20260503c ← prije POŠK fix-a
  • klubovi_dedup_20260502, klubovi_dedup_v2_*, klubovi_dedup_v3_*

6. CURRENT STATE — što JE U DB-u (verified 14:10)

-- pgz_sport.klubovi
525 ukupno, region='PGŽ' aktivni:
  503 BEZ web URL-a
  19  Wikipedia URL (treba prebaci na pravi)
  11  pravi web URL  (HNK Rijeka, NK Orijent, NK Krk, AK Liburnija, RK Zamet,
                       MK Kvarner, AK Liburnija, JK Klub Sušačana...)
  + 9 updated u ovoj sesiji s pravim URL-om iz seed liste

-- pgz_sport.clanovi (1808 ukupno PGZ)
527 vaterpolo igrači (HVS API)
405 boćari (HBS scrape, kategorija fixed)
745 nogomet+ostali (mass enrich, kategorija=NULL ili igrac)
6   uprava+stozer

-- pgz_sport.savezi (15 PGŽ aktivnih)
3 vaterpolski (1 merged )
+ HVS, HBS, HKS, HRS, HOO, HNS, HŠS, HSSRM (insert iz seed)

-- pgz_sport.dokumenti (3379 ukupno)
1447 hoo (Playwright scrape)
288  pgz_sport
260  rss_hr
294  savez_hbs
106  savez_hks
73   pravilnik (stari ZS PGŽ)
+ 18 godišnjaci sport-pgz.hr (NEW)

-- Schema extensions (this session)
clanovi.external_id     TEXT   za HVS: "hvs:igrac:{id}"
clanovi.savez_izvor     TEXT   "HVS","HBS","klub_web","godisnjak"
clanovi.profile_url     TEXT
clanovi.uloga_detalj    TEXT   "trener vrata", "lijevo krilo"
clanovi.metadata        JSONB
+ uloga_katalog tablica sa 49 uloga grupiranih u 8 grupa

Schema problemi za fix u sljedećoj sesiji:

ALTER TABLE pgz_sport.klubovi ADD COLUMN IF NOT EXISTS izvor_unosa TEXT;
ALTER TABLE pgz_sport.natjecanja ADD COLUMN IF NOT EXISTS razina_natjecanja TEXT;
ALTER TABLE pgz_sport.natjecanja ADD COLUMN IF NOT EXISTS web TEXT;

Crime list (krivi URL-ovi u DB):

  • NK Opatija (id=3840) → Wiki URL umjesto nkopatija.hr
  • NK Mune (id=2201) → Wiki URL umjesto nk-mune.hr
  • NK Crikvenica (id=2421) → semafor.hns.family umjesto nk-crikvenica.hr
  • AK Kvarner (id=3746) → KK Kvarner Wikipedia (krivo zbog fuzzy match — ovo POTPUNO drugi klub)

7. ŠTO NE FUNKCIONIRA (proven dead ends)

DuckDuckGo HTML search — vraća 0 rezultata, blokira IP. Ne koristi. Sudreg subjekti pull — 100K subjekata po 'tvrtke' endpointu vraća samo 3 sport (loš pristup) HRS api.hrs.hr — Zoraxy reverse proxy = no public endpoint HKS hks.hr — base URL ne odgovara (treba probat hks-cbf.hr) Većina saveze /klubovi/ /registar/ URL-ovi — 404 WordPress REST /wp-json/wp/v2/{cpt} — postoji samo na HVS, HBS, HRS (Jetpack), HTS, HTriS

Što JE radilo:

  • HVS rezultati API JWT token (mass scrape brutalno brz)
  • HOO Playwright /dokumenti/29|30|/page/X (Damirov scraper, 478 PDF)
  • HBS direktan scrape — svi klubovi + 405 boćara
  • sport-pgz.hr Playwright crawl (18 godišnjak PDF)
  • LLM klub-web enrich preko /api/v2/enrich/klub-web

8. NEXT SESSION TO-DO (priority order)

A) ENTITY DEDUP (Damir hard-blocking issue)

  1. Napraviti pgz_sport.klub_dedup_engine — modul s 4-step:
    • Naive normalize
    • RapidFuzz match (instalirat: pip install rapidfuzz)
    • BGE-M3 cosine sim (preko :9879/api/embeddings)
    • vLLM yes/no adjudication
  2. Run preko 525 PGZ klubova → grupe duplikata
  3. Damir review (ne auto-merge!) → merge approved

B) GODISNJAK LLM EXTRACT (Damir prioritet 1)

Skripta /tmp/godisnjak_llm.py već napisana:

  • vLLM primary, Groq fallback
  • 18 godišnjaka × ~5500 chars chunks ≈ 1500-2000 chunks
  • Parallel=10 prema vLLM
  • ⚠️ GPU je PUN — vLLM već 18.2/20.5GB. Možda treba max_workers=5 umjesto 10.
  • Output: JSON osobe/klubovi/savezi
  • Insert u clanovi s savez_izvor='godisnjak', metadata.year=2007..2022

C) DAMIR SEED LIST FULL CRAWL

  1. Fix schema: ALTER TABLE klubovi ADD COLUMN izvor_unosa TEXT
  2. Fix krive URL-ove (Wiki/Semafor → pravi):
    UPDATE klubovi SET web='https://nkopatija.hr/' WHERE id=3840;
    UPDATE klubovi SET web='https://nk-mune.hr/' WHERE id=2201;
    UPDATE klubovi SET web='https://nk-crikvenica.hr/' WHERE id=2421;
    UPDATE klubovi SET web='https://akkvarner.hr/' WHERE id=3746 AND naziv='AK Kvarner';
    
  3. Insert 27 klubova iz Damirovog seed-a (15 NEW + 12 update web)
  4. Mass klub-web enrich za 30+ klubova s URL-om

D) HVS-PATTERN SAVEZ API DISCOVERY

Sniffinrali smo ali nije temeljito. Pokušati:

  • rezultati.hbs.hr/api/?rubrika=person&id=X (boćanje)
  • rezultati.hks-cbf.hr/api/?... (košarka)
  • rezultati.hns-cff.hr/api/?... (nogomet — ali HNS ima drugačiji stack)
  • Pokušati Damirov https://semafor.hns.family/klubovi/{id} koji već postoji u DB

E) NETWORK XHR sniff za specifične klub stranice

Kad idemo na https://nkkrk.hr/igraci/ koji XHR pokrene? Ako klub ima JS-bound popis igrača, to treba sniff.

F) NSPGZ.hr — službena PGZ nogomet web

Damir potvrdio. Njihov registar = svi PGZ nogometni klubovi + igrači?

G) sah-pgz.hr/klubovi/ direktan scrape

Konkretan URL od Damira = lista šahovskih klubova PGŽ. Brza pobjeda.

H) Šahovski savez HR registar

hrvatski-sahovski-savez.hr ima sigurno registar igrača.


9. CREDENTIALS / Refs

# Bridge
URL=https://api.rinet.one/bridge/exec
KEY=rinet-yS4ZnKlwUqsjk

# DB
HOST=localhost PORT=6432 DB=rinet_v3 USER=rinet PASS=R1net2026!SecureDB#v7

# vLLM (jedini LLM — pazi na GPU!)
VLLM=http://localhost:8001/v1/chat/completions
MODEL="Qwen/Qwen2.5-7B-Instruct-AWQ"
MAX_TOKENS=8192

# Embedder (pazi: input mora biti LIST!)
EMBED=http://localhost:9879/api/embeddings
BODY={"model":"bge-m3","input":["text"]}

# Qdrant
QDRANT=http://10.10.0.2:6333
COLLECTION=pgz_sport_v1

# Cloud LLMs (fallback)
GROQ_API_KEY=SET (in /opt/rinet-gpu/.env.master)
DEEPSEEK_API_KEY=SET

# Sudreg
SUDREG_CLIENT_ID + SUDREG_CLIENT_SECRET (u .env.master)
TOKEN_URL=https://sudreg-data.gov.hr/api/oauth/token

# HVS rezultati JWT (gore)
TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIVlMi...

# Telegram
TG_BOT=8535797835:AAFItT-92jzZ9NWFafLxn0dLa1_n2s-JE5Y
TG_CHAT=7969491558

10. KRITIČNA UPOZORENJA za novu sesiju

  1. 3-strike rule active — ne lažirati "complete" tvrdnje, uvijek find /opt -name X ili API check
  2. GPU 18.2/20.5GB pun — vLLM uzima sve. Ne pokrećeš 2. LLM proces. max_workers=5 za batch.
  3. Embedder input MORA biti LIST (["text"], ne "text")
  4. /tmp/dis.py šta ako se ponovo pojavi → Python module crash, briši odmah
  5. NIKAD srpski/crnogorski u outputu_lang_fix filter aktivan
  6. POŠK je iz Splita — ne PGŽ! (3893 marked SDŽ, 177 igrača re-locira)
  7. Stack file headers — svaki .py file ima # Fajl/Verzija/Datum/Autor/Lokacija/Svrha
  8. Web URL discovery preko Google/DDG = NE RADI, blokira IP. Koristiti Damirov seed list.
  9. Damir hoće FUZZY DEDUP — bez 5x istog kluba u varijantama

11. NESPREMNE SKRIPTE (saved u /tmp/)

/tmp/godisnjak_llm.py        — LLM ekstraktor 18 godišnjaka (NIJE pokrenut, GPU full!)
/tmp/seed_pgz.py             — Damirov seed list insert (delovi failed — schema fix)
/tmp/sudreg_mass.py          — Sudreg PGZ pull (loš approach, ne re-run)
/tmp/hvs_mass.py             — HVS API mass scrape (✓ SUCCESS, 795 osoba)
/tmp/hvs_teams.py            — HVS team probe (✓ 52 PGZ momčadi)
/tmp/sport_pgz_scrape.py     — sport-pgz.hr scrape (✓ 142 PDF found)
/tmp/godisnjak_pull.py       — 18 godišnjak download + DB insert (✓ SUCCESS)
/tmp/quickfix.py             — POŠK + savez merge (✓ done)
/tmp/fix_kat.py              — kategorija fix za boćare (✓ done)

Damirov scraper (referenca):
/opt/rinet-gpu/sport_pipeline/scrapers/hoo_pw_fetch.py  ← sync_playwright pattern
/opt/rinet-gpu/sport_pipeline/scrapers/_common.py       ← upsert_doc() pattern

12. POZICIJA — pre-handoff

Damir je rekao "Napravi jebeno dobre scrapere, bez podataka smo ništa!". HVS je dobar (745 osoba). Ostali sportovi minimalno (1234 mass enrich, kategorija nedodjeljena).

Trenutno 1808 PGZ osoba u DB. Damir hoće mnogo više. Sljedeća sesija mora:

  1. Dovršiti godišnjak LLM ekstrakcija (potencijal: 5K-10K osoba)
  2. Damir seed list klub-web mass scrape (potencijal: 30 klubova × ~30 osoba = 900)
  3. NSPGZ.hr scrape (svi PGZ nogometni klubovi)
  4. sah-pgz.hr/klubovi/ direktan scrape

Svaki novi savez treba Layer 1-6 pipeline kao što je Damir predložio.


END HANDOFF

Pokreni novu sesiju s ovim file-om u Project Knowledge. Damir nastavlja sa njim.