- JavaScript 64.6%
- CSS 23.8%
- HTML 9.1%
- Python 2.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| data | ||
| docs/superpowers | ||
| src/pyicparser | ||
| tests | ||
| Zestawienia pociągów - PKP Intercity_files | ||
| .gitignore | ||
| .python-version | ||
| main.py | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
| Zestawienia pociągów - PKP Intercity.html | ||
pyicparser
Parser plakatów zestawień pociągów PKP Intercity (PDF). Wyciąga dane o odjazdach, składach, numerach wagonów i usługach pokładowych.
Projekt jest eksperymentalny i nie jest oficjalnym produktem PKP Intercity.
Instalacja
Wymagany jest Python 3.14 lub nowszy oraz uv.
uv sync
Aby zainstalować również zależności developerskie:
uv sync --extra dev
Użycie
Parsowanie PDF
uv run pyicparser parse <plik.pdf>
uv run pyicparser parse <plik.pdf> --format json
uv run pyicparser parse <plik.pdf> --format json --output wynik.json
uv run pyicparser parse <plik.pdf> --engine mupdf # silnik PyMuPDF (eksperymentalny)
Pobieranie PDFów z intercity.pl
uv run pyicparser pull all # pobierz wszystkie stacje
uv run pyicparser pull all --station "Iława" # filtruj po stacji
uv run pyicparser pull all --force # re-download nawet jeśli już jest
uv run pyicparser pull status # stan pobranych plików
uv run pyicparser pull status --format json
PDFy trafiają do data/, manifest do data/manifest.json.
Pobieranie wymaga dostępu do internetu. Opcja pull all może pobrać kilka dużych plików PDF, dlatego warto użyć --station, jeśli potrzebny jest tylko jeden rozkład.
Przykład wyniku JSON
{
"station": "Katowice",
"valid_from": "2026-03-08",
"valid_to": "2026-06-13",
"trains": [
{
"departure_time": "08:15",
"category": "IC",
"number": "1234",
"name": "PRZYKŁAD",
"destinations": ["Warszawa Centralna"],
"platform": "3",
"track": "2",
"wagons": []
}
]
}
Nie wszystkie pola są pokazane w przykładzie. Pełny schemat opisano poniżej.
Dane wyjściowe
Pole trains zawiera listę obiektów:
| Pole | Typ | Opis |
|---|---|---|
departure_time |
str | Godzina odjazdu (HH:MM) |
category |
str | Kategoria (EIP, EIC, IC, TLK, EC, EN) |
number |
str | Numer pociągu |
name |
str | Nazwa handlowa |
destinations |
list[str] | Stacje docelowe |
platform |
str | Peron |
track |
str | Tor |
routes |
list[str] | Trasy odcinkowe (A → B) |
wagons |
list[Wagon] | Wagony z numerami i cechami |
wagon_sequence_raw |
str | Surowy ciąg wagonów |
has_couchette |
bool | Kuszetki |
has_wheelchair |
bool | Miejsce dla wózka |
has_bicycle |
bool | Przewóz rowerów |
has_combo_wagon |
bool | Wagon COMBO |
has_power_outlets |
bool | Gniazdka elektryczne |
is_emu / is_dmu |
bool | Zespół elektryczny/spalinowy |
international |
bool | Komunikacja międzynarodowa |
Wagon:
| Pole | Typ | Opis |
|---|---|---|
number |
str | Numer wagonu |
features |
list[str] | Cechy: couchette, seating, wheelchair, bicycle, direction_change, … |
footnotes |
list[int] | Numery przypisów |
Silniki parsowania
plumber (domyślny)
Oparty o pdfplumber. Ekstrakcja znak po znaku, grupowanie w tokeny, detekcja kolumn przez strukturalne linie pionowe. Skuteczny dla obu wariantów PDF.
mupdf (eksperymentalny)
Oparty o PyMuPDF. Ekstrakcja na poziomie spanów tekstowych. Mniej dokładny — może tracić pociągi na stronie 2, zalecany tylko do testów.
Jak działa parser
Domyślny pipeline składa się z kilku etapów:
pdfplumberodczytuje znaki i ich pozycje na stronie.- Moduł layoutu wykrywa kolumny oraz bloki odpowiadające pojedynczym pociągom.
- Tokenizer grupuje znaki w fragmenty tekstu.
- Ekstrakcja interpretuje informacje o pociągu, trasie i składzie.
- Parser składu rozpoznaje numery wagonów, ich cechy i przypisy.
- Wynik jest walidowany przez modele Pydantic i prezentowany jako tabela albo JSON.
Struktura PDFów PKP IC — uwagi implementacyjne
PKP IC używa co najmniej dwóch różnych generatorów PDF:
Wariant A (duże stacje, np. Katowice)
- Ikony w dedykowanej czcionce Intercity24 (glyph font)
- Czyste pozycje znaków, bez nakładek
- Linie strukturalne kolumn mają
top < 0
Wariant B (mniejsze stacje, np. Iława Główna)
- Ikony w zwykłej ArialMT (te same znaki:
=,<,_,`) - Podwójnie renderowane znaki — te same glify 1–5pt obok siebie
- Numery wagonów na granicach segmentów mogą się nakładać (dwa 2-cyfrowe w ~7pt)
- Wiersz cech wagonu (np.
_<}= kuszetka/wózek/rower) może być oddzielnym wierszem nad wierszem numerów
Zastosowane poprawki
- Deduplikacja znaków:
page.dedupe_chars(tolerance=3, extra_attrs=("size",))przed parsowaniem - Detekcja kolumn: filtr
top < 0odrzuca linie schematu składu (zaczynają się odtop=0) - Tokenizer: wykrywanie nowej linii przez y-jump + ujemny x-gap
- Sortowanie znaków: zaokrąglenie
topdo 0.5pt eliminuje błędy kolejności przez float micro-precision - Regex kategorii: dopuszcza prefiks
*(*IC,*EICna kolejnych stronach) - Detekcja ikon: sprawdza zarówno czcionkę Intercity24 jak i znane znaki ikon w ArialMT
- Nakładające się cyfry wagonów: pomiń bold digit jeśli gap < -1pt (duplikat renderowania)
- Ikony przed pierwszym wagonem: wiersz cech w nagłówku diagramu — ikony akumulowane przed pierwszym numerem wagonu są przypisywane do pierwszego wagonu
Znane ograniczenia
- Nakładające się numery wagonów na granicach segmentów (dwa numery w ~7pt) — nierozwiązywalne z samych danych znakowych
- Niektóre PDFy pomijają ikony kuszetki/miejsc dla konkretnych pociągów (defekt w źródle)
- Cyfry przypisów mogą wchodzić w nazwy pociągów (np.
BALTIC EXPRESS5I) - Niniejszy moduł nie jest oficjalnym produktem PKP Intercity
Struktura projektu
src/pyicparser/
cli.py # CLI (Typer): parse, pull all/status
parser.py # główny parser (pdfplumber)
engine_mupdf.py # alternatywny silnik (PyMuPDF)
layout.py # detekcja kolumn i bloków wierszy
extraction.py # ekstrakcja danych pociągu z bloku znaków
composition.py # parsowanie ciągu tokenów wagonu
tokenizer.py # grupowanie znaków w tokeny
constants.py # ICON_MAP, ICON_CHARS
models.py # modele Pydantic: Train, Wagon
pull.py # scraping i pobieranie PDFów z intercity.pl
display.py # Rich output
metadata.py # metadane PDFa
Zależności
pdfplumber— ekstrakcja znaków z PDFpymupdf— alternatywny silnikhttpx— pobieranie PDFów (SSL TLS 1.2+, custom headers)typer+rich— CLI i formatowaniepydantic— modele danych
Development
Uruchomienie testów:
uv sync --extra dev
uv run pytest
Przykładowe sprawdzenie lokalnego PDF-a:
uv run pyicparser parse data/Katowice_20260308-20260613_20260401.pdf --format json
Katalog data/ zawiera przykładowe PDF-y i manifest pobrania. Są to dane wejściowe do testów ręcznych, a nie część oficjalnej bazy rozkładów.