railbot/parser.py

109 lines
3.9 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]:
"""
Повертає список поїздів зі станціями, часами і маршрутом для кожного потяга.
"""
# Завантаження 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')
# Таблиця з часами руху
times_table = soup.select_one('div#tabs-trains1 table.td_center')
if not times_table:
raise RuntimeError('Не знайдено таблицю розкладу')
# Маршрути: кожен <td class="course"> відповідає одному потягу
route_tags = times_table.select('td.course')
routes = [tag.get_text(strip=True) for tag in route_tags]
# Список станцій (35)
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]
# Заголовок з номерами потягів
rows = times_table.find_all('tr')
header_row = next(r for r in rows if r.find('td', class_='on_right_t'))
train_cells = header_row.find_all('td', class_='on_right_t')
entries: List[Dict] = []
for idx, cell in enumerate(train_cells):
text = cell.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)
route = routes[idx] if idx < len(routes) else ''
entries.append({
'train_number': num,
'days': days,
'route': route,
'times': []
})
# Рядки з часами руху
time_rows = [r for r in rows if r.find('td', class_='q0') or r.find('td', class_='q1')]
# Збирання часу для кожного поїзда та станції
for idx, entry in enumerate(entries):
base = idx * 3
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 ''
entry['times'].append({
'station': stations[si],
'arrival': arrival,
'departure': departure
})
return entries