railbot/parser.py

118 lines
4.2 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 = [d.strip(' .') for d in text.split('крім', 1)[1].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 = [d.strip(' .') for d in text.split('по', 1)[1].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(tab: int = 1, use_local: bool = False) -> List[Dict]:
"""
Парсить вкладку розкладу:
tab=1 — Київ→Ніжин,
tab=2 — Ніжин→Київ.
Повертає список поїздів з полями:
'tid', 'train_number', 'days', 'route', 'times'.
"""
# Завантаження 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')
prefix = f'div#tabs-trains{tab}'
# Таблиця розкладу
times_table = soup.select_one(f'{prefix} table.td_center')
if not times_table:
raise RuntimeError(f'Не знайдено таблицю розкладу для tab={tab}')
# Список станцій (35)
station_tags = soup.select(
f'{prefix} table.left tr.on a.et, '
f'{prefix} table.left tr.onx a.et'
)
stations = [a.get_text(strip=True) for a in station_tags]
# Ряди таблиці
trs = times_table.find_all('tr')
# Рядок з номерами потягів та днями курсування
header_row = next(r for r in trs if r.find('td', class_='on_right_t'))
train_cells = header_row.find_all('td', class_='on_right_t')
# Парсимо маршрути (<td class="course">) для кожного потяга
route_tags = times_table.select('td.course')
routes = [tag.get_text(strip=True) for tag in route_tags[:len(train_cells)]]
entries: List[Dict] = []
for idx, cell in enumerate(train_cells):
# Витягнути унікальний tid з href
a_tag = cell.find('a', class_='et')
href = a_tag['href'] # наприклад ".?tid=26397"
tid = href.split('tid=')[-1]
parts = cell.get_text(separator='|', strip=True).split('|')
num = parts[0].rstrip(',').strip()
days = parse_days(parts[1] if len(parts) > 1 else 'щоденно')
route = routes[idx] if idx < len(routes) else ''
entries.append({
'tid': tid,
'train_number': num,
'days': days,
'route': route,
'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
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