""" Stahování seznamu registrovaných pojištěnců ZPMVČR (211) — čistý Python (requests + bs4). ZPMVČR běží na ODLIŠNÉ platformě (eforms.zpmvcr.cz) — ne portalzp.cz: - login: PIN + heslo (POST formulář), bez certifikátu a bez NMSigneru - seznam: NENÍ datový soubor jako u ostatních pojišťoven (EP2 sekce je prázdná). Jediný zdroj je HTML "Přehled registrací" na stránce registrovani_pojistenci, který se naparsuje a uloží jako CSV. Co skript dělá: 1. Přihlásí se (PIN + heslo) 2. Projde stránkovaný přehled VŠECH registrací (platné i neplatné) pro IČP 09305001 3. Uloží výsledek jako CSV do složky SeznamyPojištěnců (sloupce níže) CSV sloupce: Číslo pojištěnce; Titul; Příjmení; Jméno; Registrace od; Registrace do """ import csv import os import sys from datetime import date import requests from bs4 import BeautifulSoup try: sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") except Exception: pass sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))) from Knihovny.najdi_dropbox import get_dropbox_root # ── Přihlašovací údaje ──────────────────────────────────────────────────────── PIN = "9023895287" PIN2 = "" HESLO = "Ax162q8+" # ───────────────────────────────────────────────────────────────────────────── BASE_URL = "https://eforms.zpmvcr.cz" LOGIN_URL = f"{BASE_URL}/eforms/ekomunikace" SEZNAM_URL = f"{BASE_URL}/eforms/smluvni_zdravotnicke_zarizeni/registrovani_pojistenci" ICP = "09305001" # IČP MUDr. Michaela Buzalková REGISTRACE = "3" # 1=platné, 2=neplatné, 3=všechny TRIDIT = "1" # 1=příjmení, 2=číslo pojištěnce CSV_HLAVICKA = ["Číslo pojištěnce", "Titul", "Příjmení", "Jméno", "Registrace od", "Registrace do"] DEST_DIR = os.path.join( get_dropbox_root(), "Ordinace", "Dokumentace_ke_zpracování", "Zúčtovací zprávy", "SeznamyPojištěnců", ) def prihlaseni() -> requests.Session: """Přihlásí se PIN + heslem, vrátí session.""" session = requests.Session() session.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" session.get(LOGIN_URL, timeout=15).raise_for_status() r = session.post(LOGIN_URL, data={"pin": PIN, "pin2": PIN2, "pwd": HESLO}, timeout=15) r.raise_for_status() if 'name="pin"' in r.text and "Přihlásit" in r.text: raise RuntimeError("Přihlášení selhalo — zkontroluj PIN a heslo") print("Přihlášení úspěšné!") return session def parse_rows(html: str) -> list[list[str]]: """Naparsuje řádky přehledu. Vrátí seznam [číslo, titul, příjmení, jméno, reg_od, reg_do].""" soup = BeautifulSoup(html, "html.parser") rows = [] for tr in soup.select("tr.c1, tr.c2"): vals = [] for td in tr.find_all("td"): for sp in td.select("span.responsiveColumn"): sp.extract() vals.append(td.get_text(strip=True)) # platný datový řádek má vyplněné číslo pojištěnce v prvním sloupci if len(vals) >= 6 and vals[0]: rows.append(vals[:6]) return rows def precti_celkem(html: str) -> int | None: """Z hlášky 'Přehled ... (celkem N)' získá očekávaný počet.""" import re m = re.search(r"celkem\s+(\d+)", html) return int(m.group(1)) if m else None def stahni_seznam(session: requests.Session) -> list[list[str]]: """Projde stránkovaný přehled a vrátí všechny řádky.""" base_data = { "icp": ICP, "arztart": "", "mesic": str(date.today().month), "rok": str(date.today().year), "registrace": REGISTRACE, "tridit": TRIDIT, "vyhledat": "Vyhledat", } vsechny: list[list[str]] = [] videno: set = set() celkem_ocekavano = None page = 1 while page <= 200: data = dict(base_data) if page > 1: data["page"] = str(page) r = session.post(SEZNAM_URL, data=data, timeout=30) r.raise_for_status() if celkem_ocekavano is None: celkem_ocekavano = precti_celkem(r.text) if celkem_ocekavano is not None: print(f"Přehled hlásí celkem {celkem_ocekavano} registrací.") rows = parse_rows(r.text) nove = [row for row in rows if tuple(row) not in videno] if not nove: break for row in nove: videno.add(tuple(row)) vsechny.extend(nove) print(f" Strana {page}: +{len(nove)} (celkem {len(vsechny)})") # poslední strana — méně řádků než plná stránka if len(rows) < 20: break page += 1 if celkem_ocekavano is not None and len(vsechny) != celkem_ocekavano: print(f" POZOR: staženo {len(vsechny)}, ale přehled hlásil {celkem_ocekavano}.") return vsechny def uloz_csv(rows: list[list[str]]) -> str: """Uloží řádky jako CSV (Excel-friendly: utf-8-sig, oddělovač ;). Vrátí cestu.""" os.makedirs(DEST_DIR, exist_ok=True) dnes = date.today().strftime("%Y-%m-%d") filename = f"{dnes} 211 ZPMVČR vsechny registrace.csv" path = os.path.join(DEST_DIR, filename) with open(path, "w", encoding="utf-8-sig", newline="") as f: w = csv.writer(f, delimiter=";") w.writerow(CSV_HLAVICKA) w.writerows(rows) return path def hlavni() -> None: session = prihlaseni() print("\n=== Stahování přehledu registrací ===") rows = stahni_seznam(session) print(f"Staženo: {len(rows)} registrací.") if not rows: print("Žádné registrace — CSV se neuloží.") return path = uloz_csv(rows) print(f"\nHotovo — uloženo: {path}") if __name__ == "__main__": hlavni()