notebookvb
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
"""
|
||||||
|
Přihlásí se na VZP Point a stáhne nové zprávy.
|
||||||
|
|
||||||
|
Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění.
|
||||||
|
|
||||||
|
POZNÁMKA: 01_prihlaseni.py čeká na ruční výběr certifikátu a stisk Enter —
|
||||||
|
počkejte na výzvu, než se spustí stahování.
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 04_prihlaseni_a_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def run(script: str) -> None:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, os.path.join(DIR, script)],
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=== Přihlášení ===")
|
||||||
|
run("01_prihlaseni.py")
|
||||||
|
|
||||||
|
print("\n=== Stahování nových zpráv ===")
|
||||||
|
run("03_stahuj_nove.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
IČZ: 9305000 - MUDr. Michaela Buzalková
|
||||||
|
Období: 3/2026
|
||||||
|
|
||||||
|
IČP: 9305001 - Ordinace praktického lékaře pro dospělé
|
||||||
|
|
||||||
|
Věková skupina Počet reg. placených poj. Koeficient Počet jednicových pojištěnců
|
||||||
|
15 - 19 let 7 1,06 7,42
|
||||||
|
20 - 24 let 35 0,90 31,50
|
||||||
|
25 - 29 let 32 0,95 30,40
|
||||||
|
30 - 34 let 37 1,00 37,00
|
||||||
|
35 - 39 let 56 1,05 58,80
|
||||||
|
40 - 44 let 54 1,05 56,70
|
||||||
|
45 - 49 let 103 1,10 113,30
|
||||||
|
50 - 54 let 105 1,43 150,15
|
||||||
|
55 - 59 let 60 1,54 92,40
|
||||||
|
60 - 64 let 58 1,59 92,22
|
||||||
|
65 - 69 let 46 1,80 82,80
|
||||||
|
70 - 74 let 52 2,12 110,24
|
||||||
|
75 - 79 let 118 2,54 299,72
|
||||||
|
80 - 84 let 71 3,07 217,97
|
||||||
|
nad 85 let 65 3,60 234,00
|
||||||
|
|
||||||
|
Celkem registrovných placených pojištěnců: 899
|
||||||
|
Celkem jednicových pojištěnců: 1 614,62
|
||||||
|
Navýšení kapitačního paušálu: 1,00 Kč
|
||||||
|
Navýšení kapitační platby: 1 614,62 Kč
|
||||||
|
Degresní koeficient ve výši 0,984831 nebyl uplatněn.
|
||||||
|
|
||||||
|
Navýšení kapitační platby za období: 3/2026 1 614,62 Kč
|
||||||
|
Navýšení kapitační platby za IČZ: 9305000 1 614,62 Kč
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
IÈZ: 9305000 - MUDr. Michaela Buzalková
|
||||||
|
Období: 3/2026
|
||||||
|
|
||||||
|
IÈP: 9305001 - Ordinace praktického lékaøe pro dospìlé
|
||||||
|
|
||||||
|
Vìková skupina Poèet reg. placených poj. Koeficient Poèet jednicových poji¹tìncù
|
||||||
|
15 - 19 let 7 1,06 7,42
|
||||||
|
20 - 24 let 35 0,90 31,50
|
||||||
|
25 - 29 let 32 0,95 30,40
|
||||||
|
30 - 34 let 37 1,00 37,00
|
||||||
|
35 - 39 let 56 1,05 58,80
|
||||||
|
40 - 44 let 54 1,05 56,70
|
||||||
|
45 - 49 let 103 1,10 113,30
|
||||||
|
50 - 54 let 105 1,43 150,15
|
||||||
|
55 - 59 let 60 1,54 92,40
|
||||||
|
60 - 64 let 58 1,59 92,22
|
||||||
|
65 - 69 let 46 1,80 82,80
|
||||||
|
70 - 74 let 52 2,12 110,24
|
||||||
|
75 - 79 let 118 2,54 299,72
|
||||||
|
80 - 84 let 71 3,07 217,97
|
||||||
|
nad 85 let 65 3,60 234,00
|
||||||
|
|
||||||
|
Celkem registrovných placených poji¹tìncù: 899
|
||||||
|
Celkem jednicových poji¹tìncù: 1 614,62
|
||||||
|
Sazba kapitaèního pau¹álu: 76,00 Kè
|
||||||
|
Celková kapitaèní platba: 122 711,12 Kè
|
||||||
|
Degresní koeficient ve vý¹i 0,984831 nebyl uplatnìn.
|
||||||
|
|
||||||
|
Celková kapitaèní platba za období: 3/2026 122 711,12 Kè
|
||||||
|
Celková kapitaèní platba za IÈZ: 9305000 122 711,12 Kè
|
||||||
@@ -13,6 +13,7 @@ POUŽITÍ:
|
|||||||
python 02_stahuj_vse.py
|
python 02_stahuj_vse.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
@@ -103,11 +104,17 @@ def stahni_zpravu(session: requests.Session, msg: dict) -> bool:
|
|||||||
"""Stáhne protokol/přílohu zprávy. Vrátí True pokud staženo, False pokud přeskočeno."""
|
"""Stáhne protokol/přílohu zprávy. Vrátí True pokud staženo, False pokud přeskočeno."""
|
||||||
nazev_base = safe_name(f"{msg['datum']} {msg['druh']} (Ref. {msg['ref']})")
|
nazev_base = safe_name(f"{msg['datum']} {msg['druh']} (Ref. {msg['ref']})")
|
||||||
|
|
||||||
# Přeskoč pokud existuje v jakékoliv příponě
|
# Přeskoč pokud existuje soubor se stejným Ref. číslem (imunní vůči Unicode/mezera variantám)
|
||||||
for ext in (".html", ".pdf", ".xml", ".zip"):
|
if msg["ref"]:
|
||||||
if os.path.exists(os.path.join(STAZENO_DIR, nazev_base + ext)):
|
existing = glob.glob(os.path.join(STAZENO_DIR, f"*(Ref. {msg['ref']}).*"))
|
||||||
print(f" [přeskočeno] {nazev_base}{ext}")
|
if existing:
|
||||||
|
print(f" [přeskočeno] {os.path.basename(existing[0])}")
|
||||||
return False
|
return False
|
||||||
|
else:
|
||||||
|
for ext in (".html", ".pdf", ".xml", ".zip"):
|
||||||
|
if os.path.exists(os.path.join(STAZENO_DIR, nazev_base + ext)):
|
||||||
|
print(f" [přeskočeno] {nazev_base}{ext}")
|
||||||
|
return False
|
||||||
|
|
||||||
# Detail zprávy → najdi download link
|
# Detail zprávy → najdi download link
|
||||||
r = session.get(f"{BASE_URL}/app/schranka/detail/{msg['id']}/", timeout=15)
|
r = session.get(f"{BASE_URL}/app/schranka/detail/{msg['id']}/", timeout=15)
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
"""
|
||||||
|
Stahování NOVÝCH protokolů ze schránek ČPZP — zastaví se při první již stažené zprávě.
|
||||||
|
|
||||||
|
Použij po 01_prihlaseni.py (ten uloží cpzp_cookies.json).
|
||||||
|
|
||||||
|
Co dělá:
|
||||||
|
- Prochází Schránku klienta a Schránku PZS od nejnovějších zpráv
|
||||||
|
- Jakmile narazí na zprávu, která už je v Staženo/, okamžitě se zastaví
|
||||||
|
- Vhodné pro pravidelné spouštění — stáhne jen to nové
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 03_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
BASE_URL = "https://portal.cpzp.cz"
|
||||||
|
COOKIES_FILE = os.path.join(os.path.dirname(__file__), "cpzp_cookies.json")
|
||||||
|
STAZENO_DIR = os.path.join(os.path.dirname(__file__), "Staženo")
|
||||||
|
|
||||||
|
SCHRANKY = [
|
||||||
|
("/app/schranka/", "Schránka klienta"),
|
||||||
|
("/app/schranka-pzs/", "Schránka PZS"),
|
||||||
|
]
|
||||||
|
PAGE_SIZE = 20
|
||||||
|
|
||||||
|
|
||||||
|
def make_session() -> requests.Session:
|
||||||
|
with open(COOKIES_FILE, encoding="utf-8") as f:
|
||||||
|
cookies = json.load(f)
|
||||||
|
s = requests.Session()
|
||||||
|
s.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||||
|
for c in cookies:
|
||||||
|
s.cookies.set(c["name"], c["value"], domain=c["domain"].lstrip("."))
|
||||||
|
|
||||||
|
r = s.get(f"{BASE_URL}/app/", timeout=15)
|
||||||
|
if "frmPrihlasCert" in r.text:
|
||||||
|
raise SystemExit("Cookies expirovala — nejdřív spusť 01_prihlaseni.py")
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def parse_datum(datum_str: str) -> str:
|
||||||
|
"""'01.04.2026 22:05:42' → '2026-04-01'"""
|
||||||
|
m = re.match(r"(\d{2})\.(\d{2})\.(\d{4})", datum_str.strip())
|
||||||
|
if m:
|
||||||
|
return f"{m.group(3)}-{m.group(2)}-{m.group(1)}"
|
||||||
|
return "0000-00-00"
|
||||||
|
|
||||||
|
|
||||||
|
def safe_name(text: str) -> str:
|
||||||
|
return re.sub(r'[\\/:*?"<>|]', "_", text).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def uz_stazeno(ref: str, nazev_base: str) -> str | None:
|
||||||
|
"""Vrátí název existujícího souboru, nebo None."""
|
||||||
|
if ref:
|
||||||
|
existing = glob.glob(os.path.join(STAZENO_DIR, f"*(Ref. {ref}).*"))
|
||||||
|
if existing:
|
||||||
|
return os.path.basename(existing[0])
|
||||||
|
else:
|
||||||
|
for ext in (".html", ".pdf", ".xml", ".zip"):
|
||||||
|
if os.path.exists(os.path.join(STAZENO_DIR, nazev_base + ext)):
|
||||||
|
return nazev_base + ext
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def stahni_zpravu(session: requests.Session, msg: dict) -> bool:
|
||||||
|
"""Stáhne zprávu. Vrátí True pokud staženo."""
|
||||||
|
nazev_base = safe_name(f"{msg['datum']} {msg['druh']} (Ref. {msg['ref']})")
|
||||||
|
|
||||||
|
r = session.get(f"{BASE_URL}/app/schranka/detail/{msg['id']}/", timeout=15)
|
||||||
|
r.raise_for_status()
|
||||||
|
soup = BeautifulSoup(r.content, "html.parser", from_encoding="utf-8")
|
||||||
|
dl_link = soup.find("a", href=re.compile(r"/app/schranka/protokol/"))
|
||||||
|
if not dl_link:
|
||||||
|
print(f" [bez přílohy] {msg['id']} — {msg['druh']}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
for pokus in range(3):
|
||||||
|
try:
|
||||||
|
r2 = session.get(BASE_URL + dl_link["href"], timeout=60)
|
||||||
|
r2.raise_for_status()
|
||||||
|
break
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
if pokus == 2:
|
||||||
|
print(f" [chyba] timeout po 3 pokusech — {msg['druh']} Ref. {msg['ref']}")
|
||||||
|
return False
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
ct = r2.headers.get("Content-Type", "")
|
||||||
|
link_text = dl_link.get_text(strip=True)
|
||||||
|
if link_text.lower().endswith(".pdf") or "pdf" in ct:
|
||||||
|
ext = ".pdf"
|
||||||
|
elif link_text.lower().endswith(".xml") or "xml" in ct:
|
||||||
|
ext = ".xml"
|
||||||
|
elif link_text.lower().endswith(".zip") or "zip" in ct:
|
||||||
|
ext = ".zip"
|
||||||
|
else:
|
||||||
|
ext = ".html"
|
||||||
|
|
||||||
|
cil = os.path.join(STAZENO_DIR, nazev_base + ext)
|
||||||
|
with open(cil, "wb") as f:
|
||||||
|
f.write(r2.content)
|
||||||
|
print(f" [OK] {os.path.basename(cil)}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def stahuj_schranku(session: requests.Session, mailbox_url: str, seen_ids: set) -> tuple[int, bool]:
|
||||||
|
"""
|
||||||
|
Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou.
|
||||||
|
Vrátí (počet stažených, zda bylo dosaženo konce = nalezena stará zpráva).
|
||||||
|
"""
|
||||||
|
stazeno = 0
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
r = session.get(f"{BASE_URL}{mailbox_url}?offset={offset}", timeout=15)
|
||||||
|
r.raise_for_status()
|
||||||
|
soup = BeautifulSoup(r.content, "html.parser", from_encoding="utf-8")
|
||||||
|
rows = soup.select("tr[id^='message-']")
|
||||||
|
if not rows:
|
||||||
|
print(" Žádné další zprávy.")
|
||||||
|
return stazeno, True
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
msg_id = row["id"].replace("message-", "")
|
||||||
|
if msg_id in seen_ids:
|
||||||
|
continue
|
||||||
|
seen_ids.add(msg_id)
|
||||||
|
|
||||||
|
cells = row.find_all("td")
|
||||||
|
datum_raw = cells[5].get_text(strip=True) if len(cells) > 5 else ""
|
||||||
|
druh_ref = cells[4].get_text(separator=" ", strip=True) if len(cells) > 4 else ""
|
||||||
|
druh_match = re.match(r"^(.+?)\s+Ref\.\s*č\.\s*(\d+)", druh_ref)
|
||||||
|
druh = druh_match.group(1).strip() if druh_match else druh_ref
|
||||||
|
ref_c = druh_match.group(2).strip() if druh_match else ""
|
||||||
|
|
||||||
|
msg = {
|
||||||
|
"id": msg_id,
|
||||||
|
"datum": parse_datum(datum_raw),
|
||||||
|
"druh": druh,
|
||||||
|
"ref": ref_c,
|
||||||
|
}
|
||||||
|
|
||||||
|
nazev_base = safe_name(f"{msg['datum']} {msg['druh']} (Ref. {msg['ref']})")
|
||||||
|
existing = uz_stazeno(ref_c, nazev_base)
|
||||||
|
if existing:
|
||||||
|
print(f" [stop] Nalezena již stažená zpráva: {existing}")
|
||||||
|
return stazeno, True
|
||||||
|
|
||||||
|
if stahni_zpravu(session, msg):
|
||||||
|
stazeno += 1
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
offset += PAGE_SIZE
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
os.makedirs(STAZENO_DIR, exist_ok=True)
|
||||||
|
session = make_session()
|
||||||
|
|
||||||
|
seen_ids: set = set()
|
||||||
|
celkem = 0
|
||||||
|
|
||||||
|
for url, name in SCHRANKY:
|
||||||
|
print(f"\n=== {name} ({url}) ===")
|
||||||
|
stazeno, _ = stahuj_schranku(session, url, seen_ids)
|
||||||
|
celkem += stazeno
|
||||||
|
|
||||||
|
print(f"\nHotovo: {celkem} nových zpráv staženo.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Přihlásí se na portál ČPZP a stáhne nové zprávy.
|
||||||
|
|
||||||
|
Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění.
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 04_prihlaseni_a_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def run(script: str) -> None:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, os.path.join(DIR, script)],
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=== Přihlášení ===")
|
||||||
|
run("01_prihlaseni.py")
|
||||||
|
|
||||||
|
print("\n=== Stahování nových zpráv ===")
|
||||||
|
run("03_stahuj_nove.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "PHPSESSID",
|
"name": "PHPSESSID",
|
||||||
"value": "7ps03755qsp9n4gpms64rqle77",
|
"value": "5mbiobj1htd5joflu2fpm480a3",
|
||||||
"domain": ".portal.cpzp.cz",
|
"domain": ".portal.cpzp.cz",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": -1,
|
"expires": -1,
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
"""
|
||||||
|
Stahování NOVÝCH zpráv ze schránek OZP — zastaví se při první již stažené zprávě.
|
||||||
|
|
||||||
|
Použij po 01_prihlaseni.py (ten uloží ozp_cookies.json).
|
||||||
|
|
||||||
|
Co dělá:
|
||||||
|
- Prochází každou schránku od nejnovějších zpráv
|
||||||
|
- Jakmile narazí na zprávu, která už je v Staženo/, okamžitě zastaví danou schránku
|
||||||
|
- Vhodné pro pravidelné spouštění — stáhne jen to nové
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 03_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
BASE_URL = "https://portal.ozp.cz"
|
||||||
|
INBOX_URL = f"{BASE_URL}/app/prehled-zprav-ve-schrankach"
|
||||||
|
DOWNLOAD_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-prilohu"
|
||||||
|
PROTOKOL_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-protokol"
|
||||||
|
|
||||||
|
COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "ozp_cookies.json"))
|
||||||
|
DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo")
|
||||||
|
|
||||||
|
SCHRANKY = {
|
||||||
|
"7-osobni-schranka-pzs": "Osobní schránka PZS",
|
||||||
|
"9-schranka-klienta": "Schránka klienta",
|
||||||
|
"27-zuctovaci-zpravy": "Zúčtovací zprávy",
|
||||||
|
"167-schranka-poskytovatele-zdravotnich-sluzeb": "Schránka poskytovatele ZS",
|
||||||
|
"179-schranka-klientu-portalu": "Schránka klientů portálu",
|
||||||
|
"201-vypis-pojistencu-v-registraci": "Výpis pojištěnců v registraci",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date(date_str: str) -> str:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(date_str.strip()[:19], "%d.%m.%Y %H:%M:%S").strftime("%Y-%m-%d")
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(date_str.strip()[:10], "%d.%m.%Y").strftime("%Y-%m-%d")
|
||||||
|
except Exception:
|
||||||
|
return "0000-00-00"
|
||||||
|
|
||||||
|
|
||||||
|
def safe_filename(name: str) -> str:
|
||||||
|
return re.sub(r'[\\/:*?"<>|]', "_", name).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_row(cells: list) -> dict:
|
||||||
|
date_raw = cells[1].strip() if len(cells) > 1 else ""
|
||||||
|
desc_raw = cells[2].strip() if len(cells) > 2 else ""
|
||||||
|
fname_raw = cells[3].strip() if len(cells) > 3 else ""
|
||||||
|
|
||||||
|
desc_lines = [l.strip() for l in desc_raw.split("\n") if l.strip()]
|
||||||
|
if len(desc_lines) >= 3:
|
||||||
|
description = desc_lines[2]
|
||||||
|
elif len(desc_lines) >= 2:
|
||||||
|
description = desc_lines[1]
|
||||||
|
else:
|
||||||
|
description = desc_lines[0] if desc_lines else ""
|
||||||
|
description = description[:80]
|
||||||
|
|
||||||
|
fname_match = re.match(r'^(.+?)\s*\(\d{2}\.\d{2}\.\d{4}\)\s*$', fname_raw)
|
||||||
|
original = fname_match.group(1).strip() if fname_match else fname_raw.split("(")[0].strip()
|
||||||
|
orig_path = Path(original)
|
||||||
|
stem = orig_path.stem or "zprava"
|
||||||
|
ext = orig_path.suffix or ""
|
||||||
|
|
||||||
|
date_iso = parse_date(date_raw)
|
||||||
|
name = f"{date_iso} {safe_filename(description)} ({safe_filename(stem)}){ext}"
|
||||||
|
if len(name) > 240:
|
||||||
|
name = f"{date_iso} ({safe_filename(stem)}){ext}"
|
||||||
|
|
||||||
|
return {"date": date_iso, "desc": description, "original": original, "filename": name}
|
||||||
|
|
||||||
|
|
||||||
|
def process_schránka(page, context, segment: str, name: str, already: set) -> int:
|
||||||
|
"""
|
||||||
|
Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou.
|
||||||
|
Vrátí počet stažených souborů.
|
||||||
|
"""
|
||||||
|
downloaded = 0
|
||||||
|
page_num = 1
|
||||||
|
seen_ids: set = set()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
url = f"{INBOX_URL}/{segment}/stranka-{page_num}"
|
||||||
|
print(f" Stranka {page_num}: {url}")
|
||||||
|
try:
|
||||||
|
page.goto(url, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Navigace selhala: {e}")
|
||||||
|
break
|
||||||
|
|
||||||
|
page.wait_for_load_state("networkidle", timeout=15_000)
|
||||||
|
|
||||||
|
data = page.evaluate("""() => {
|
||||||
|
const rows = [];
|
||||||
|
for (const tr of document.querySelectorAll('table tr')) {
|
||||||
|
const cells = Array.from(tr.querySelectorAll('td')).map(td => td.innerText.trim());
|
||||||
|
if (cells.length < 4) continue;
|
||||||
|
const dlLink = tr.querySelector('a[onclick*="SchrPolOpenFile"]');
|
||||||
|
if (!dlLink) continue;
|
||||||
|
const mFile = dlLink.getAttribute('onclick').match(/\\d+/);
|
||||||
|
const protLink = tr.querySelector('a[onclick*="SchrPolDBProtokol"]');
|
||||||
|
const mProt = protLink ? protLink.getAttribute('onclick').match(/\\d+/) : null;
|
||||||
|
rows.push({
|
||||||
|
cells,
|
||||||
|
fileId: mFile ? mFile[0] : null,
|
||||||
|
protokolId: mProt ? mProt[0] : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}""")
|
||||||
|
rows = [r for r in data if r["fileId"]]
|
||||||
|
|
||||||
|
if not rows:
|
||||||
|
print(f" Stranka {page_num} - zadne radky, koncim schranku.")
|
||||||
|
break
|
||||||
|
|
||||||
|
current_ids = {r["fileId"] for r in rows}
|
||||||
|
if current_ids & seen_ids:
|
||||||
|
print(f" Stranka {page_num} - opakujici se obsah, koncim schranku.")
|
||||||
|
break
|
||||||
|
seen_ids.update(current_ids)
|
||||||
|
print(f" Nalezeno {len(rows)} zprav.")
|
||||||
|
|
||||||
|
stop = False
|
||||||
|
for row in rows:
|
||||||
|
info = parse_row(row["cells"])
|
||||||
|
target = os.path.join(DOWNLOAD_DIR, info["filename"])
|
||||||
|
|
||||||
|
if info["filename"] in already or os.path.exists(target):
|
||||||
|
print(f" [stop] Nalezena již stažená zpráva: {info['filename']}")
|
||||||
|
stop = True
|
||||||
|
break
|
||||||
|
|
||||||
|
dl_url = f"{DOWNLOAD_URL}?zprava_id={row['fileId']}"
|
||||||
|
try:
|
||||||
|
r = context.request.get(dl_url, headers={"Referer": INBOX_URL}, timeout=30_000)
|
||||||
|
if not r.ok:
|
||||||
|
print(f" HTTP {r.status} priloha (id={row['fileId']})")
|
||||||
|
else:
|
||||||
|
with open(target, "wb") as f:
|
||||||
|
f.write(r.body())
|
||||||
|
print(f" OK: {info['filename']}")
|
||||||
|
already.add(info["filename"])
|
||||||
|
downloaded += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Chyba priloha (id={row['fileId']}): {e}")
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
if row.get("protokolId"):
|
||||||
|
prot_name = safe_filename(f"{info['date']} {info['desc']} (protokol-{row['protokolId']}).html")
|
||||||
|
prot_target = os.path.join(DOWNLOAD_DIR, prot_name)
|
||||||
|
if prot_name not in already and not os.path.exists(prot_target):
|
||||||
|
prot_url = f"{PROTOKOL_URL}?id={row['protokolId']}"
|
||||||
|
try:
|
||||||
|
r2 = context.request.get(prot_url, headers={"Referer": INBOX_URL}, timeout=30_000)
|
||||||
|
if r2.ok:
|
||||||
|
with open(prot_target, "wb") as f:
|
||||||
|
f.write(r2.body())
|
||||||
|
print(f" OK: {prot_name}")
|
||||||
|
already.add(prot_name)
|
||||||
|
downloaded += 1
|
||||||
|
else:
|
||||||
|
print(f" HTTP {r2.status} protokol (id={row['protokolId']})")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Chyba protokol (id={row['protokolId']}): {e}")
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
if stop:
|
||||||
|
break
|
||||||
|
|
||||||
|
page_num += 1
|
||||||
|
|
||||||
|
return downloaded
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
try:
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
except ImportError:
|
||||||
|
print("Chybi playwright: pip install playwright && playwright install chrome")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
if not os.path.exists(COOKIES_FILE):
|
||||||
|
print(f"Soubor {COOKIES_FILE} nenalezen - spust 01_prihlaseni.py")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(COOKIES_FILE, encoding="utf-8") as f:
|
||||||
|
cookies = json.load(f)
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
context = p.chromium.launch_persistent_context(
|
||||||
|
user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"),
|
||||||
|
channel="chrome",
|
||||||
|
headless=False,
|
||||||
|
slow_mo=100,
|
||||||
|
ignore_https_errors=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
context.add_cookies(cookies)
|
||||||
|
page = context.new_page()
|
||||||
|
page.goto(INBOX_URL, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
|
||||||
|
if "prihlaseni" in page.url or "login" in page.url.lower():
|
||||||
|
print("Cookies expirovala - spust 01_prihlaseni.py")
|
||||||
|
return
|
||||||
|
print("Prihlaseni OK\n")
|
||||||
|
|
||||||
|
already = set(os.listdir(DOWNLOAD_DIR))
|
||||||
|
print(f"V archivu: {len(already)} souboru.\n")
|
||||||
|
|
||||||
|
celkem = 0
|
||||||
|
for segment, name in SCHRANKY.items():
|
||||||
|
print(f"\n=== {name} ===")
|
||||||
|
dl = process_schránka(page, context, segment, name, already)
|
||||||
|
print(f" {name}: stazeno {dl}")
|
||||||
|
celkem += dl
|
||||||
|
|
||||||
|
print(f"\nHotovo. Celkem stazeno: {celkem}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
context.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Přihlásí se na portál OZP a stáhne nové zprávy.
|
||||||
|
|
||||||
|
Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění.
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 04_prihlaseni_a_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def run(script: str) -> None:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, os.path.join(DIR, script)],
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=== Přihlášení ===")
|
||||||
|
run("01_prihlaseni.py")
|
||||||
|
|
||||||
|
print("\n=== Stahování nových zpráv ===")
|
||||||
|
run("03_stahuj_nove.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "SID",
|
"name": "SID",
|
||||||
"value": "acdfc00c69bd5a7002809cca6d82e354",
|
"value": "8c2c0ab2e344d74177d4e7866797dd28",
|
||||||
"domain": ".portal.ozp.cz",
|
"domain": ".portal.ozp.cz",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": -1,
|
"expires": -1,
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"value": "CERT",
|
"value": "CERT",
|
||||||
"domain": ".portal.ozp.cz",
|
"domain": ".portal.ozp.cz",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": 1808228214,
|
"expires": 1808281759,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": false,
|
"httpOnly": false,
|
||||||
"sameSite": "Lax"
|
"sameSite": "Lax"
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
"""
|
||||||
|
Stahování NOVÝCH zpráv ze schránek ZPŠ — zastaví se při první již stažené zprávě.
|
||||||
|
|
||||||
|
Použij po 01_prihlaseni.py (ten uloží zps_cookies.json).
|
||||||
|
|
||||||
|
Co dělá:
|
||||||
|
- Prochází každou schránku od nejnovějších zpráv
|
||||||
|
- Jakmile narazí na zprávu, která už je v Staženo/, okamžitě zastaví danou schránku
|
||||||
|
- Vhodné pro pravidelné spouštění — stáhne jen to nové
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 03_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
BASE_URL = "https://portal.zpskoda.cz"
|
||||||
|
INBOX_URL = f"{BASE_URL}/app/prehled-zprav-ve-schrankach"
|
||||||
|
DOWNLOAD_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-prilohu"
|
||||||
|
PROTOKOL_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-protokol"
|
||||||
|
|
||||||
|
COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "zps_cookies.json"))
|
||||||
|
DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo")
|
||||||
|
|
||||||
|
SCHRANKY = {
|
||||||
|
"17-schranka-poskytovatele-zdravotnich-sluzeb": "Schránka poskytovatele ZS",
|
||||||
|
"19-schranka-klienta": "Schránka klienta",
|
||||||
|
"145-vypis-registrovanych-pojistencu": "Výpis registrovaných pojištěnců",
|
||||||
|
"169-zpravy-od-klientu-pzp-": "Zprávy od klientů PZP",
|
||||||
|
"181-schranka-klientu-portalu": "Schránka klientů portálu",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date(date_str: str) -> str:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(date_str.strip()[:19], "%d.%m.%Y %H:%M:%S").strftime("%Y-%m-%d")
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(date_str.strip()[:10], "%d.%m.%Y").strftime("%Y-%m-%d")
|
||||||
|
except Exception:
|
||||||
|
return "0000-00-00"
|
||||||
|
|
||||||
|
|
||||||
|
def safe_filename(name: str) -> str:
|
||||||
|
return re.sub(r'[\\/:*?"<>|]', "_", name).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_row(cells: list) -> dict:
|
||||||
|
date_raw = cells[1].strip() if len(cells) > 1 else ""
|
||||||
|
desc_raw = cells[2].strip() if len(cells) > 2 else ""
|
||||||
|
fname_raw = cells[3].strip() if len(cells) > 3 else ""
|
||||||
|
|
||||||
|
desc_lines = [l.strip() for l in desc_raw.split("\n") if l.strip()]
|
||||||
|
if len(desc_lines) >= 3:
|
||||||
|
description = desc_lines[2]
|
||||||
|
elif len(desc_lines) >= 2:
|
||||||
|
description = desc_lines[1]
|
||||||
|
else:
|
||||||
|
description = desc_lines[0] if desc_lines else ""
|
||||||
|
description = description[:80]
|
||||||
|
|
||||||
|
fname_match = re.match(r'^(.+?)\s*\(\d{2}\.\d{2}\.\d{4}\)\s*$', fname_raw)
|
||||||
|
original = fname_match.group(1).strip() if fname_match else fname_raw.split("(")[0].strip()
|
||||||
|
orig_path = Path(original)
|
||||||
|
stem = orig_path.stem or "zprava"
|
||||||
|
ext = orig_path.suffix or ""
|
||||||
|
|
||||||
|
date_iso = parse_date(date_raw)
|
||||||
|
name = f"{date_iso} {safe_filename(description)} ({safe_filename(stem)}){ext}"
|
||||||
|
if len(name) > 240:
|
||||||
|
name = f"{date_iso} ({safe_filename(stem)}){ext}"
|
||||||
|
|
||||||
|
return {"date": date_iso, "desc": description, "original": original, "filename": name}
|
||||||
|
|
||||||
|
|
||||||
|
def process_schránka(page, context, segment: str, name: str, already: set) -> int:
|
||||||
|
"""
|
||||||
|
Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou.
|
||||||
|
Vrátí počet stažených souborů.
|
||||||
|
"""
|
||||||
|
downloaded = 0
|
||||||
|
page_num = 1
|
||||||
|
seen_ids: set = set()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
url = f"{INBOX_URL}/{segment}/stranka-{page_num}"
|
||||||
|
print(f" Stranka {page_num}: {url}")
|
||||||
|
try:
|
||||||
|
page.goto(url, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Navigace selhala: {e}")
|
||||||
|
break
|
||||||
|
|
||||||
|
page.wait_for_load_state("networkidle", timeout=15_000)
|
||||||
|
|
||||||
|
data = page.evaluate("""() => {
|
||||||
|
const rows = [];
|
||||||
|
for (const tr of document.querySelectorAll('table tr')) {
|
||||||
|
const cells = Array.from(tr.querySelectorAll('td')).map(td => td.innerText.trim());
|
||||||
|
if (cells.length < 4) continue;
|
||||||
|
const dlLink = tr.querySelector('a[onclick*="SchrPolOpenFile"]');
|
||||||
|
if (!dlLink) continue;
|
||||||
|
const mFile = dlLink.getAttribute('onclick').match(/\\d+/);
|
||||||
|
const protLink = tr.querySelector('a[onclick*="SchrPolDBProtokol"]');
|
||||||
|
const mProt = protLink ? protLink.getAttribute('onclick').match(/\\d+/) : null;
|
||||||
|
rows.push({
|
||||||
|
cells,
|
||||||
|
fileId: mFile ? mFile[0] : null,
|
||||||
|
protokolId: mProt ? mProt[0] : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}""")
|
||||||
|
rows = [r for r in data if r["fileId"]]
|
||||||
|
|
||||||
|
if not rows:
|
||||||
|
print(f" Stranka {page_num} - zadne radky, koncim schranku.")
|
||||||
|
break
|
||||||
|
|
||||||
|
current_ids = {r["fileId"] for r in rows}
|
||||||
|
if current_ids & seen_ids:
|
||||||
|
print(f" Stranka {page_num} - opakujici se obsah, koncim schranku.")
|
||||||
|
break
|
||||||
|
seen_ids.update(current_ids)
|
||||||
|
print(f" Nalezeno {len(rows)} zprav.")
|
||||||
|
|
||||||
|
stop = False
|
||||||
|
for row in rows:
|
||||||
|
info = parse_row(row["cells"])
|
||||||
|
target = os.path.join(DOWNLOAD_DIR, info["filename"])
|
||||||
|
|
||||||
|
if info["filename"] in already or os.path.exists(target):
|
||||||
|
print(f" [stop] Nalezena již stažená zpráva: {info['filename']}")
|
||||||
|
stop = True
|
||||||
|
break
|
||||||
|
|
||||||
|
dl_url = f"{DOWNLOAD_URL}?zprava_id={row['fileId']}"
|
||||||
|
try:
|
||||||
|
r = context.request.get(dl_url, headers={"Referer": INBOX_URL}, timeout=30_000)
|
||||||
|
if not r.ok:
|
||||||
|
print(f" HTTP {r.status} priloha (id={row['fileId']})")
|
||||||
|
else:
|
||||||
|
with open(target, "wb") as f:
|
||||||
|
f.write(r.body())
|
||||||
|
print(f" OK: {info['filename']}")
|
||||||
|
already.add(info["filename"])
|
||||||
|
downloaded += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Chyba priloha (id={row['fileId']}): {e}")
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
if row.get("protokolId"):
|
||||||
|
prot_name = safe_filename(f"{info['date']} {info['desc']} (protokol-{row['protokolId']}).html")
|
||||||
|
prot_target = os.path.join(DOWNLOAD_DIR, prot_name)
|
||||||
|
if prot_name not in already and not os.path.exists(prot_target):
|
||||||
|
prot_url = f"{PROTOKOL_URL}?id={row['protokolId']}"
|
||||||
|
try:
|
||||||
|
r2 = context.request.get(prot_url, headers={"Referer": INBOX_URL}, timeout=30_000)
|
||||||
|
if r2.ok:
|
||||||
|
with open(prot_target, "wb") as f:
|
||||||
|
f.write(r2.body())
|
||||||
|
print(f" OK: {prot_name}")
|
||||||
|
already.add(prot_name)
|
||||||
|
downloaded += 1
|
||||||
|
else:
|
||||||
|
print(f" HTTP {r2.status} protokol (id={row['protokolId']})")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Chyba protokol (id={row['protokolId']}): {e}")
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
if stop:
|
||||||
|
break
|
||||||
|
|
||||||
|
page_num += 1
|
||||||
|
|
||||||
|
return downloaded
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
try:
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
except ImportError:
|
||||||
|
print("Chybi playwright: pip install playwright && playwright install chrome")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
if not os.path.exists(COOKIES_FILE):
|
||||||
|
print(f"Soubor {COOKIES_FILE} nenalezen - spust 01_prihlaseni.py")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(COOKIES_FILE, encoding="utf-8") as f:
|
||||||
|
cookies = json.load(f)
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
context = p.chromium.launch_persistent_context(
|
||||||
|
user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"),
|
||||||
|
channel="chrome",
|
||||||
|
headless=False,
|
||||||
|
slow_mo=100,
|
||||||
|
ignore_https_errors=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
context.add_cookies(cookies)
|
||||||
|
page = context.new_page()
|
||||||
|
page.goto(INBOX_URL, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
|
||||||
|
if "prihlaseni" in page.url or "login" in page.url.lower():
|
||||||
|
print("Cookies expirovala - spust 01_prihlaseni.py")
|
||||||
|
return
|
||||||
|
print("Prihlaseni OK\n")
|
||||||
|
|
||||||
|
already = set(os.listdir(DOWNLOAD_DIR))
|
||||||
|
print(f"V archivu: {len(already)} souboru.\n")
|
||||||
|
|
||||||
|
celkem = 0
|
||||||
|
for segment, name in SCHRANKY.items():
|
||||||
|
print(f"\n=== {name} ===")
|
||||||
|
dl = process_schránka(page, context, segment, name, already)
|
||||||
|
print(f" {name}: stazeno {dl}")
|
||||||
|
celkem += dl
|
||||||
|
|
||||||
|
print(f"\nHotovo. Celkem stazeno: {celkem}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
context.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Přihlásí se na portál ZPŠ a stáhne nové zprávy.
|
||||||
|
|
||||||
|
Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění.
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 04_prihlaseni_a_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def run(script: str) -> None:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, os.path.join(DIR, script)],
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=== Přihlášení ===")
|
||||||
|
run("01_prihlaseni.py")
|
||||||
|
|
||||||
|
print("\n=== Stahování nových zpráv ===")
|
||||||
|
run("03_stahuj_nove.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "SID",
|
"name": "SID",
|
||||||
"value": "9ed44d48a017f8915cdfb566d2e2e952",
|
"value": "e85dcec9acf6345f151cd5996be23576",
|
||||||
"domain": ".portal.zpskoda.cz",
|
"domain": ".portal.zpskoda.cz",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": -1,
|
"expires": -1,
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"value": "CERT",
|
"value": "CERT",
|
||||||
"domain": ".portal.zpskoda.cz",
|
"domain": ".portal.zpskoda.cz",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": 1808246438,
|
"expires": 1808281979,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": false,
|
"httpOnly": false,
|
||||||
"sameSite": "Lax"
|
"sameSite": "Lax"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ POUŽITÍ:
|
|||||||
python 02_stahuj_vse.py
|
python 02_stahuj_vse.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -149,7 +150,11 @@ def process_sekce(page, context, cfg: dict, already: set) -> tuple:
|
|||||||
filename = f"{period} {label} {prefix}.{ext}"
|
filename = f"{period} {label} {prefix}.{ext}"
|
||||||
target = os.path.join(DOWNLOAD_DIR, filename)
|
target = os.path.join(DOWNLOAD_DIR, filename)
|
||||||
|
|
||||||
if filename in already or os.path.exists(target):
|
# Prefix je unikátní — soubor může mít různý suffix z Content-Disposition
|
||||||
|
prefix_check = f"{period} {label} {prefix}"
|
||||||
|
if any(f.startswith(prefix_check) for f in already) or glob.glob(
|
||||||
|
os.path.join(DOWNLOAD_DIR, glob.escape(prefix_check) + "*")
|
||||||
|
):
|
||||||
skipped += 1
|
skipped += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,234 @@
|
|||||||
|
"""
|
||||||
|
Stahování NOVÝCH dokumentů ze ZPMVČR — zastaví se při první již stažené zprávě v každé sekci.
|
||||||
|
|
||||||
|
Použij po 01_prihlaseni.py (ten uloží zpmvcr_cookies.json).
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 03_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
BASE_URL = "https://eforms.zpmvcr.cz"
|
||||||
|
ZPRAVY_URL = f"{BASE_URL}/eforms/smluvni_zdravotnicke_zarizeni/dokumenty_ke_stazeni/zuctovaci_zprava"
|
||||||
|
AVIZA_URL = f"{BASE_URL}/eforms/smluvni_zdravotnicke_zarizeni/dokumenty_ke_stazeni/aviza"
|
||||||
|
|
||||||
|
COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "zpmvcr_cookies.json"))
|
||||||
|
DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo")
|
||||||
|
|
||||||
|
SEKCE = [
|
||||||
|
{
|
||||||
|
"name": "Zúčtovací zprávy",
|
||||||
|
"url": ZPRAVY_URL,
|
||||||
|
"need_history": True,
|
||||||
|
"dl_historie": "1",
|
||||||
|
"ident_col": 1,
|
||||||
|
"typ_col": 3,
|
||||||
|
"obdobi_col": 5,
|
||||||
|
"ident_prefix": "VS-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Avíza",
|
||||||
|
"url": AVIZA_URL,
|
||||||
|
"need_history": False,
|
||||||
|
"dl_historie": "0",
|
||||||
|
"ident_col": 1,
|
||||||
|
"typ_col": 2,
|
||||||
|
"obdobi_col": 4,
|
||||||
|
"ident_prefix": "",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_period(raw: str) -> str:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(raw.strip()[:10], "%d.%m.%Y").strftime("%Y-%m")
|
||||||
|
except Exception:
|
||||||
|
cleaned = raw.strip()[:7].replace(".", "-")
|
||||||
|
return cleaned if cleaned else "0000"
|
||||||
|
|
||||||
|
|
||||||
|
def safe_filename(name: str) -> str:
|
||||||
|
return re.sub(r'[\\/:*?"<>|]', "_", name).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def make_js_extract(ident_col: int, typ_col: int, obdobi_col: int) -> str:
|
||||||
|
return f"""() => {{
|
||||||
|
const KNOWN_EXTS = new Set(['pdf', 'xml', 'txt', 'zip', 'doc', 'xls', 'csv']);
|
||||||
|
const docs = [];
|
||||||
|
for (const inp of document.querySelectorAll('input[name="doc_id"]')) {{
|
||||||
|
const docId = inp.value;
|
||||||
|
if (!docId) continue;
|
||||||
|
const td = inp.closest('td');
|
||||||
|
const tr = td ? td.closest('tr') : null;
|
||||||
|
let ident = '', typ = 'pdf', obdobi = '';
|
||||||
|
if (tr) {{
|
||||||
|
const tds = Array.from(tr.querySelectorAll('td')).map(t => t.innerText.trim());
|
||||||
|
ident = tds[{ident_col}] || '';
|
||||||
|
const t = (tds[{typ_col}] || '').toLowerCase().trim();
|
||||||
|
typ = KNOWN_EXTS.has(t) ? t : 'pdf';
|
||||||
|
obdobi = tds[{obdobi_col}] || '';
|
||||||
|
}}
|
||||||
|
docs.push({{ docId, ident, typ, obdobi }});
|
||||||
|
}}
|
||||||
|
return docs;
|
||||||
|
}}"""
|
||||||
|
|
||||||
|
|
||||||
|
def click_next_page(page, page_num: int) -> bool:
|
||||||
|
next_val = str(page_num + 1)
|
||||||
|
btn = page.locator(f"input[name='page'][value='{next_val}']")
|
||||||
|
if btn.count() == 0:
|
||||||
|
return False
|
||||||
|
btn.first.click()
|
||||||
|
page.wait_for_load_state("networkidle", timeout=20_000)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def uz_stazeno(prefix_check: str, already: set) -> bool:
|
||||||
|
return any(f.startswith(prefix_check) for f in already) or bool(
|
||||||
|
glob.glob(os.path.join(DOWNLOAD_DIR, glob.escape(prefix_check) + "*"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def process_sekce(page, context, cfg: dict, already: set) -> int:
|
||||||
|
name = cfg["name"]
|
||||||
|
url = cfg["url"]
|
||||||
|
ident_pfx = cfg["ident_prefix"]
|
||||||
|
js_extract = make_js_extract(cfg["ident_col"], cfg["typ_col"], cfg["obdobi_col"])
|
||||||
|
downloaded = 0
|
||||||
|
|
||||||
|
print(f"\n=== {name} ===")
|
||||||
|
page.goto(url, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
page.wait_for_load_state("networkidle", timeout=15_000)
|
||||||
|
|
||||||
|
if cfg["need_history"]:
|
||||||
|
hist = page.get_by_text("Zobrazit historii dokumentů za poslední 3 roky")
|
||||||
|
if hist.count() > 0:
|
||||||
|
hist.first.click()
|
||||||
|
page.wait_for_load_state("networkidle", timeout=20_000)
|
||||||
|
|
||||||
|
page_num = 1
|
||||||
|
seen_ids: set = set()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print(f" Strana {page_num}...")
|
||||||
|
docs = page.evaluate(js_extract)
|
||||||
|
docs = [d for d in docs if d["docId"] not in seen_ids]
|
||||||
|
|
||||||
|
if not docs:
|
||||||
|
print(f" Strana {page_num} — žádné nové dokumenty, konec sekce.")
|
||||||
|
break
|
||||||
|
|
||||||
|
seen_ids.update(d["docId"] for d in docs)
|
||||||
|
print(f" Nalezeno {len(docs)} dokumentů")
|
||||||
|
|
||||||
|
stop = False
|
||||||
|
for doc in docs:
|
||||||
|
period = parse_period(doc["obdobi"])
|
||||||
|
ext = doc["typ"] or "pdf"
|
||||||
|
ident = safe_filename(doc["ident"])
|
||||||
|
label = safe_filename(name)
|
||||||
|
prefix = ident_pfx + ident if ident else f"id-{doc['docId']}"
|
||||||
|
prefix_check = f"{period} {label} {prefix}"
|
||||||
|
|
||||||
|
if uz_stazeno(prefix_check, already):
|
||||||
|
print(f" [stop] Nalezen již stažený dokument: {prefix_check}*")
|
||||||
|
stop = True
|
||||||
|
break
|
||||||
|
|
||||||
|
r = context.request.post(url, form={
|
||||||
|
"bin": "1", "dl": "1", "historie": cfg["dl_historie"],
|
||||||
|
"doc_id": doc["docId"], "save": "Stáhnout",
|
||||||
|
}, timeout=60_000)
|
||||||
|
|
||||||
|
if not r.ok:
|
||||||
|
print(f" HTTP {r.status} doc_id={doc['docId']}")
|
||||||
|
time.sleep(1.0)
|
||||||
|
continue
|
||||||
|
|
||||||
|
filename = f"{prefix_check}.{ext}"
|
||||||
|
cd = r.headers.get("content-disposition", "")
|
||||||
|
m = re.search(r'filename=["\']?([^"\';\r\n]+)', cd)
|
||||||
|
if m:
|
||||||
|
orig = m.group(1).strip()
|
||||||
|
stem, suf = os.path.splitext(orig)
|
||||||
|
ext2 = suf.lstrip(".").lower() or ext
|
||||||
|
filename = f"{prefix_check} ({safe_filename(stem)}).{ext2}"
|
||||||
|
|
||||||
|
target = os.path.join(DOWNLOAD_DIR, filename)
|
||||||
|
with open(target, "wb") as f:
|
||||||
|
f.write(r.body())
|
||||||
|
print(f" OK: {filename}")
|
||||||
|
already.add(filename)
|
||||||
|
downloaded += 1
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
if stop:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not click_next_page(page, page_num):
|
||||||
|
break
|
||||||
|
page_num += 1
|
||||||
|
|
||||||
|
return downloaded
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
try:
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
except ImportError:
|
||||||
|
print("Chybi playwright: pip install playwright && playwright install chrome")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
if not os.path.exists(COOKIES_FILE):
|
||||||
|
print(f"Soubor {COOKIES_FILE} nenalezen — spust 01_prihlaseni.py")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(COOKIES_FILE, encoding="utf-8") as f:
|
||||||
|
cookies = json.load(f)
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
context = p.chromium.launch_persistent_context(
|
||||||
|
user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"),
|
||||||
|
channel="chrome",
|
||||||
|
headless=False,
|
||||||
|
slow_mo=100,
|
||||||
|
ignore_https_errors=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
context.add_cookies(cookies)
|
||||||
|
page = context.new_page()
|
||||||
|
|
||||||
|
page.goto(ZPRAVY_URL, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
page.wait_for_load_state("networkidle", timeout=15_000)
|
||||||
|
if page.locator("input[name='pin']").count() > 0:
|
||||||
|
print("Cookies expirovala — spust 01_prihlaseni.py")
|
||||||
|
return
|
||||||
|
print("Prihlaseni OK\n")
|
||||||
|
|
||||||
|
already = set(os.listdir(DOWNLOAD_DIR))
|
||||||
|
print(f"V archivu: {len(already)} souboru.")
|
||||||
|
|
||||||
|
celkem = 0
|
||||||
|
for cfg in SEKCE:
|
||||||
|
dl = process_sekce(page, context, cfg, already)
|
||||||
|
print(f" {cfg['name']}: stazeno {dl}")
|
||||||
|
celkem += dl
|
||||||
|
|
||||||
|
print(f"\nHotovo. Celkem stazeno: {celkem}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
context.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Přihlásí se na portál ZPMVČR a stáhne nové zprávy.
|
||||||
|
|
||||||
|
Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění.
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 04_prihlaseni_a_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def run(script: str) -> None:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, os.path.join(DIR, script)],
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=== Přihlášení ===")
|
||||||
|
run("01_prihlaseni.py")
|
||||||
|
|
||||||
|
print("\n=== Stahování nových zpráv ===")
|
||||||
|
run("03_stahuj_nove.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "JSESSIONID",
|
"name": "JSESSIONID",
|
||||||
"value": "36137BBDC47B5AA6EBACEA28257BB821",
|
"value": "C487947972DEE36DF5C80FA2F0A328CD",
|
||||||
"domain": ".eforms.zpmvcr.cz",
|
"domain": ".eforms.zpmvcr.cz",
|
||||||
"path": "/eforms",
|
"path": "/eforms",
|
||||||
"expires": -1,
|
"expires": -1,
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
"""
|
||||||
|
Stahování NOVÝCH zpráv ze schránek RBP — zastaví se při první již stažené zprávě.
|
||||||
|
|
||||||
|
Použij po 01_prihlaseni.py (ten uloží rbp_cookies.json).
|
||||||
|
|
||||||
|
Co dělá:
|
||||||
|
- Prochází každou schránku od nejnovějších zpráv
|
||||||
|
- Jakmile narazí na zprávu, která už je v Staženo/, okamžitě zastaví danou schránku
|
||||||
|
- Vhodné pro pravidelné spouštění — stáhne jen to nové
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 03_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
BASE_URL = "https://portal.rbp-zp.cz"
|
||||||
|
INBOX_URL = f"{BASE_URL}/app/prehled-zprav-ve-schrankach"
|
||||||
|
DOWNLOAD_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-prilohu"
|
||||||
|
PROTOKOL_URL = f"{BASE_URL}/html/prehled-zprav-ve-schrankach/zobrazit-protokol"
|
||||||
|
|
||||||
|
COOKIES_FILE = os.path.abspath(os.path.join(os.path.dirname(__file__), "rbp_cookies.json"))
|
||||||
|
DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), "Staženo")
|
||||||
|
|
||||||
|
SCHRANKY = {
|
||||||
|
"31-schranka-vyuctovani": "Schránka vyúčtování",
|
||||||
|
"22-schranka-pzs": "Schránka PZS",
|
||||||
|
"24-schranka-klienta": "Schránka klienta",
|
||||||
|
"135-prohlizeni-klientely-odbornosti-001": "Prohlížení klientely",
|
||||||
|
"136-prohlizeni-plateb": "Prohlížení plateb",
|
||||||
|
"138-zadanka-o-schvaleni": "Žádanka o schválení",
|
||||||
|
"170-schranka-poskytovatele-zdravotnich-sluzeb": "Schránka poskytovatele ZS",
|
||||||
|
"182-schranka-klientu-portalu": "Schránka klientů portálu",
|
||||||
|
"203-vypis-pojistencu-registrovanych-u-pzs": "Výpis pojištěnců reg. u PZS",
|
||||||
|
"241-smluvni-dokumenty-pro-pzs": "Smluvní dokumenty pro PZS",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date(date_str: str) -> str:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(date_str.strip()[:19], "%d.%m.%Y %H:%M:%S").strftime("%Y-%m-%d")
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(date_str.strip()[:10], "%d.%m.%Y").strftime("%Y-%m-%d")
|
||||||
|
except Exception:
|
||||||
|
return "0000-00-00"
|
||||||
|
|
||||||
|
|
||||||
|
def safe_filename(name: str) -> str:
|
||||||
|
return re.sub(r'[\\/:*?"<>|]', "_", name).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_row(cells: list) -> dict:
|
||||||
|
date_raw = cells[1].strip() if len(cells) > 1 else ""
|
||||||
|
desc_raw = cells[2].strip() if len(cells) > 2 else ""
|
||||||
|
fname_raw = cells[3].strip() if len(cells) > 3 else ""
|
||||||
|
|
||||||
|
desc_lines = [l.strip() for l in desc_raw.split("\n") if l.strip()]
|
||||||
|
if len(desc_lines) >= 3:
|
||||||
|
description = desc_lines[2]
|
||||||
|
elif len(desc_lines) >= 2:
|
||||||
|
description = desc_lines[1]
|
||||||
|
else:
|
||||||
|
description = desc_lines[0] if desc_lines else ""
|
||||||
|
description = description[:80]
|
||||||
|
|
||||||
|
fname_match = re.match(r'^(.+?)\s*\(\d{2}\.\d{2}\.\d{4}\)\s*$', fname_raw)
|
||||||
|
original = fname_match.group(1).strip() if fname_match else fname_raw.split("(")[0].strip()
|
||||||
|
orig_path = Path(original)
|
||||||
|
stem = orig_path.stem or "zprava"
|
||||||
|
ext = orig_path.suffix or ""
|
||||||
|
|
||||||
|
date_iso = parse_date(date_raw)
|
||||||
|
name = f"{date_iso} {safe_filename(description)} ({safe_filename(stem)}){ext}"
|
||||||
|
if len(name) > 240:
|
||||||
|
name = f"{date_iso} ({safe_filename(stem)}){ext}"
|
||||||
|
|
||||||
|
return {"date": date_iso, "desc": description, "original": original, "filename": name}
|
||||||
|
|
||||||
|
|
||||||
|
def process_schránka(page, context, segment: str, name: str, already: set) -> int:
|
||||||
|
"""
|
||||||
|
Prochází schránku od nejnovějších zpráv a stahuje, dokud nenarazí na již staženou.
|
||||||
|
Vrátí počet stažených souborů.
|
||||||
|
"""
|
||||||
|
downloaded = 0
|
||||||
|
page_num = 1
|
||||||
|
seen_ids: set = set()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
url = f"{INBOX_URL}/{segment}/stranka-{page_num}"
|
||||||
|
print(f" Stranka {page_num}: {url}")
|
||||||
|
try:
|
||||||
|
page.goto(url, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Navigace selhala: {e}")
|
||||||
|
break
|
||||||
|
|
||||||
|
page.wait_for_load_state("networkidle", timeout=15_000)
|
||||||
|
|
||||||
|
data = page.evaluate("""() => {
|
||||||
|
const rows = [];
|
||||||
|
for (const tr of document.querySelectorAll('table tr')) {
|
||||||
|
const cells = Array.from(tr.querySelectorAll('td')).map(td => td.innerText.trim());
|
||||||
|
if (cells.length < 4) continue;
|
||||||
|
const dlLink = tr.querySelector('a[onclick*="SchrPolOpenFile"]');
|
||||||
|
if (!dlLink) continue;
|
||||||
|
const mFile = dlLink.getAttribute('onclick').match(/\\d+/);
|
||||||
|
const protLink = tr.querySelector('a[onclick*="SchrPolDBProtokol"]');
|
||||||
|
const mProt = protLink ? protLink.getAttribute('onclick').match(/\\d+/) : null;
|
||||||
|
rows.push({
|
||||||
|
cells,
|
||||||
|
fileId: mFile ? mFile[0] : null,
|
||||||
|
protokolId: mProt ? mProt[0] : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}""")
|
||||||
|
rows = [r for r in data if r["fileId"]]
|
||||||
|
|
||||||
|
if not rows:
|
||||||
|
print(f" Stranka {page_num} - zadne radky, koncim schranku.")
|
||||||
|
break
|
||||||
|
|
||||||
|
current_ids = {r["fileId"] for r in rows}
|
||||||
|
if current_ids & seen_ids:
|
||||||
|
print(f" Stranka {page_num} - opakujici se obsah, koncim schranku.")
|
||||||
|
break
|
||||||
|
seen_ids.update(current_ids)
|
||||||
|
print(f" Nalezeno {len(rows)} zprav.")
|
||||||
|
|
||||||
|
stop = False
|
||||||
|
for row in rows:
|
||||||
|
info = parse_row(row["cells"])
|
||||||
|
target = os.path.join(DOWNLOAD_DIR, info["filename"])
|
||||||
|
|
||||||
|
if info["filename"] in already or os.path.exists(target):
|
||||||
|
print(f" [stop] Nalezena již stažená zpráva: {info['filename']}")
|
||||||
|
stop = True
|
||||||
|
break
|
||||||
|
|
||||||
|
dl_url = f"{DOWNLOAD_URL}?zprava_id={row['fileId']}"
|
||||||
|
try:
|
||||||
|
r = context.request.get(dl_url, headers={"Referer": INBOX_URL}, timeout=30_000)
|
||||||
|
if not r.ok:
|
||||||
|
print(f" HTTP {r.status} priloha (id={row['fileId']})")
|
||||||
|
else:
|
||||||
|
with open(target, "wb") as f:
|
||||||
|
f.write(r.body())
|
||||||
|
print(f" OK: {info['filename']}")
|
||||||
|
already.add(info["filename"])
|
||||||
|
downloaded += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Chyba priloha (id={row['fileId']}): {e}")
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
if row.get("protokolId"):
|
||||||
|
prot_name = safe_filename(f"{info['date']} {info['desc']} (protokol-{row['protokolId']}).html")
|
||||||
|
prot_target = os.path.join(DOWNLOAD_DIR, prot_name)
|
||||||
|
if prot_name not in already and not os.path.exists(prot_target):
|
||||||
|
prot_url = f"{PROTOKOL_URL}?id={row['protokolId']}"
|
||||||
|
try:
|
||||||
|
r2 = context.request.get(prot_url, headers={"Referer": INBOX_URL}, timeout=30_000)
|
||||||
|
if r2.ok:
|
||||||
|
with open(prot_target, "wb") as f:
|
||||||
|
f.write(r2.body())
|
||||||
|
print(f" OK: {prot_name}")
|
||||||
|
already.add(prot_name)
|
||||||
|
downloaded += 1
|
||||||
|
else:
|
||||||
|
print(f" HTTP {r2.status} protokol (id={row['protokolId']})")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Chyba protokol (id={row['protokolId']}): {e}")
|
||||||
|
time.sleep(1.0)
|
||||||
|
|
||||||
|
if stop:
|
||||||
|
break
|
||||||
|
|
||||||
|
page_num += 1
|
||||||
|
|
||||||
|
return downloaded
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
try:
|
||||||
|
from playwright.sync_api import sync_playwright
|
||||||
|
except ImportError:
|
||||||
|
print("Chybi playwright: pip install playwright && playwright install chrome")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
if not os.path.exists(COOKIES_FILE):
|
||||||
|
print(f"Soubor {COOKIES_FILE} nenalezen - spust 01_prihlaseni.py")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(COOKIES_FILE, encoding="utf-8") as f:
|
||||||
|
cookies = json.load(f)
|
||||||
|
|
||||||
|
with sync_playwright() as p:
|
||||||
|
context = p.chromium.launch_persistent_context(
|
||||||
|
user_data_dir=os.path.join(os.path.dirname(__file__), "chrome_profile"),
|
||||||
|
channel="chrome",
|
||||||
|
headless=False,
|
||||||
|
slow_mo=100,
|
||||||
|
ignore_https_errors=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
context.add_cookies(cookies)
|
||||||
|
page = context.new_page()
|
||||||
|
page.goto(INBOX_URL, wait_until="domcontentloaded", timeout=30_000)
|
||||||
|
|
||||||
|
if "prihlaseni" in page.url or "login" in page.url.lower():
|
||||||
|
print("Cookies expirovala - spust 01_prihlaseni.py")
|
||||||
|
return
|
||||||
|
print("Prihlaseni OK\n")
|
||||||
|
|
||||||
|
already = set(os.listdir(DOWNLOAD_DIR))
|
||||||
|
print(f"V archivu: {len(already)} souboru.\n")
|
||||||
|
|
||||||
|
celkem = 0
|
||||||
|
for segment, name in SCHRANKY.items():
|
||||||
|
print(f"\n=== {name} ===")
|
||||||
|
dl = process_schránka(page, context, segment, name, already)
|
||||||
|
print(f" {name}: stazeno {dl}")
|
||||||
|
celkem += dl
|
||||||
|
|
||||||
|
print(f"\nHotovo. Celkem stazeno: {celkem}")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
context.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Přihlásí se na portál RBP a stáhne nové zprávy.
|
||||||
|
|
||||||
|
Kombinuje 01_prihlaseni.py + 03_stahuj_nove.py do jednoho spuštění.
|
||||||
|
|
||||||
|
POUŽITÍ:
|
||||||
|
python 04_prihlaseni_a_stahuj_nove.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def run(script: str) -> None:
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, os.path.join(DIR, script)],
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise SystemExit(f"Skript {script} skončil s chybou (kód {result.returncode})")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=== Přihlášení ===")
|
||||||
|
run("01_prihlaseni.py")
|
||||||
|
|
||||||
|
print("\n=== Stahování nových zpráv ===")
|
||||||
|
run("03_stahuj_nove.py")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "SID",
|
"name": "SID",
|
||||||
"value": "361dbfba7820026abe74bee7d934616e",
|
"value": "58777dbd1ed8edded39575edb310c5c8",
|
||||||
"domain": ".portal.rbp-zp.cz",
|
"domain": ".portal.rbp-zp.cz",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": -1,
|
"expires": -1,
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"value": "CERT",
|
"value": "CERT",
|
||||||
"domain": ".portal.rbp-zp.cz",
|
"domain": ".portal.rbp-zp.cz",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": 1808227293,
|
"expires": 1808282104,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"httpOnly": false,
|
"httpOnly": false,
|
||||||
"sameSite": "Lax"
|
"sameSite": "Lax"
|
||||||
|
|||||||
Reference in New Issue
Block a user