railbot/parser.py

115 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
from bs4 import BeautifulSoup
from typing import List, Dict
BASE_URL = 'https://swrailway.gov.ua/timetable/eltrain/?rid=2'
LOCAL_HTML = 'rozklad.html'
# Індекс днів тижня: понеділок=0, ..., неділя=6
DAY_INDEX = {
'пн': 0, 'вт': 1, 'ср': 2,
'чт': 3, 'пт': 4, 'сб': 5,
'нд': 6
}
def parse_days(text: str) -> str:
"""Перетворює текст днів курсування на бінарний рядок, довжиною 7."""
text = text.lower().strip()
if 'щоденно' in text:
return '1111111'
if text.startswith('крім'):
# виключні дні
days_part = text.split('крім', 1)[1]
days = [d.strip(' .') for d in days_part.split(',')]
mask = [1] * 7
for d in days:
idx = DAY_INDEX.get(d)
if idx is not None:
mask[idx] = 0
return ''.join(str(b) for b in mask)
if text.startswith('по'):
# тільки вказані дні
days_part = text.split('по', 1)[1]
days = [d.strip(' .') for d in days_part.split(',')]
mask = [0] * 7
for d in days:
idx = DAY_INDEX.get(d)
if idx is not None:
mask[idx] = 1
return ''.join(str(b) for b in mask)
# за замовчуванням — щоденно
return '1111111'
def fetch_schedule(use_local: bool = False) -> List[Dict]:
"""Повертає список поїздів зі станціями та часами:
[
{
'train_number': '6902',
'days': '1111111',
'times': [
{'station': 'Київ-Волинський', 'arrival': '', 'departure': '05:10'},
... 35 записів ...
]
},
...
]
"""
# Завантажуємо HTML
if use_local:
with open(LOCAL_HTML, 'r', encoding='utf-8') as f:
html = f.read()
else:
resp = requests.get(BASE_URL, timeout=10)
resp.raise_for_status()
html = resp.text
soup = BeautifulSoup(html, 'html.parser')
# Список станцій
station_tags = soup.select(
'div#tabs-trains1 table.left tr.on a.et, div#tabs-trains1 table.left tr.onx a.et'
)
stations = [a.get_text(strip=True) for a in station_tags]
# Таблиця з часами
times_table = soup.select_one('div#tabs-trains1 table.td_center')
trs = times_table.find_all('tr')
# Рядок з заголовками поїздів
header_row = next(r for r in trs if r.find('td', class_='on_right_t'))
tds = header_row.find_all('td', class_='on_right_t')
entries: List[Dict] = []
for td in tds:
text = td.get_text(separator='|', strip=True)
parts = text.split('|')
num = parts[0].rstrip(',').strip()
days_text = parts[1].strip() if len(parts) > 1 else 'щоденно'
days = parse_days(days_text)
entries.append({
'train_number': num,
'days': days,
'times': []
})
# Рядки з часами руху (35 рядків)
time_rows = [r for r in trs if r.find('td', class_='q0') or r.find('td', class_='q1')]
# Заповнюємо часи для кожного поїзда та станції
for idx, entry in enumerate(entries):
base = idx * 3
times_list = []
for si, row in enumerate(time_rows):
cells = row.find_all('td')
arrival = cells[base + 1].get_text(strip=True) if base + 1 < len(cells) else ''
departure = cells[base + 2].get_text(strip=True) if base + 2 < len(cells) else ''
times_list.append({
'station': stations[si],
'arrival': arrival,
'departure': departure
})
entry['times'] = times_list
return entries